From f461f03b0857ac2a7fd5e3e92abb67fd5a3916b7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Nov 2017 14:51:31 +0900 Subject: [PATCH 001/537] Make osu! compile with framework changes --- osu-framework | 2 +- osu.Desktop/OsuGameDesktop.cs | 4 ++-- osu.Desktop/osu.Desktop.csproj | 3 +-- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 3 +-- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 3 +-- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 3 +-- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 3 +-- osu.Game.Tests/osu.Game.Tests.csproj | 3 +-- osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarUserArea.cs | 1 + osu.Game/osu.Game.csproj | 3 +-- 11 files changed, 12 insertions(+), 18 deletions(-) diff --git a/osu-framework b/osu-framework index c95b9350ed..aa7fb2f83c 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit c95b9350edb6305cfefdf08f902f6f73d336736b +Subproject commit aa7fb2f83c5883a23eca0f5799905669fa6cead3 diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 3393bbf7fb..60d70e1ece 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -100,7 +100,7 @@ namespace osu.Desktop { desktopWindow.CursorState |= CursorState.Hidden; - desktopWindow.Icon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); + // desktopWindow.Icon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); desktopWindow.Title = Name; desktopWindow.FileDrop += fileDrop; @@ -109,7 +109,7 @@ namespace osu.Desktop private void fileDrop(object sender, FileDropEventArgs e) { - var filePaths = new [] { e.FileName }; + var filePaths = new[] { e.FileName }; if (filePaths.All(f => Path.GetExtension(f) == @".osz")) Task.Factory.StartNew(() => BeatmapManager.Import(filePaths), TaskCreationOptions.LongRunning); diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 91c0da6f65..dd328858e0 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -136,8 +136,7 @@ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + True diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index a666984b95..0c61a5b312 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -37,8 +37,7 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + True diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 6f45a64d92..edc77f5c7b 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -37,8 +37,7 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + True diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 3c90749777..3939b3f108 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -38,8 +38,7 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + True diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index bf627d205a..868460e3d6 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -37,8 +37,7 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + True diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 974bde9319..3d134f79bf 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -34,8 +34,7 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + True diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 56b9a55398..4e7712936a 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -18,7 +18,7 @@ using System.ComponentModel; using osu.Game.Graphics; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; - +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Overlays.Settings.Sections.General diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs index 95a25fcb86..7683406b49 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using OpenTK; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; namespace osu.Game.Overlays.Toolbar { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ac1498c9d9..6ef8f6570d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -143,8 +143,7 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + True From d2ab0621f32b2f1b2a9b09e894316d8c762afee8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 19 Nov 2017 13:46:51 +0900 Subject: [PATCH 002/537] Make osu.Game + rulesets compile with netstandard --- LangVer.props | 6 + OpenTK.props | 9 + osu-framework | 2 +- osu-resources | 2 +- osu.Desktop/OsuGameDesktop.cs | 2 - osu.Game.Rulesets.Catch/OpenTK.dll.config | 25 - .../Properties/AssemblyInfo.cs | 28 - osu.Game.Rulesets.Catch/app.config | 19 - .../osu.Game.Rulesets.Catch.csproj | 115 +-- osu.Game.Rulesets.Mania/OpenTK.dll.config | 25 - .../Properties/AssemblyInfo.cs | 28 - osu.Game.Rulesets.Mania/app.config | 19 - .../osu.Game.Rulesets.Mania.csproj | 138 +-- osu.Game.Rulesets.Osu/OpenTK.dll.config | 25 - .../Properties/AssemblyInfo.cs | 28 - osu.Game.Rulesets.Osu/app.config | 19 - .../osu.Game.Rulesets.Osu.csproj | 146 +-- osu.Game.Rulesets.Taiko/OpenTK.dll.config | 25 - .../Properties/AssemblyInfo.cs | 28 - osu.Game.Rulesets.Taiko/app.config | 19 - .../osu.Game.Rulesets.Taiko.csproj | 137 +-- osu.Game.props | 8 + osu.Game/Beatmaps/BeatmapManager.cs | 4 +- osu.Game/Beatmaps/IO/OszArchiveReader.cs | 14 +- osu.Game/IO/Legacy/SerializationReader.cs | 3 +- osu.Game/IO/Legacy/SerializationWriter.cs | 4 +- .../20171019041408_InitialCreate.cs | 2 - .../20171025071459_AddMissingIndexRules.cs | 2 - .../Migrations/OsuDbContextModelSnapshot.cs | 4 - osu.Game/OpenTK.dll.config | 25 - .../Sections/General/LoginSettings.cs | 1 - osu.Game/Overlays/Toolbar/ToolbarUserArea.cs | 1 - osu.Game/Properties/AssemblyInfo.cs | 28 - osu.Game/Utils/ZipUtils.cs | 33 + osu.Game/app.config | 19 - osu.Game/osu.Game.csproj | 836 +----------------- osu.sln | 14 +- 37 files changed, 169 insertions(+), 1674 deletions(-) create mode 100644 LangVer.props create mode 100644 OpenTK.props delete mode 100644 osu.Game.Rulesets.Catch/OpenTK.dll.config delete mode 100644 osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Rulesets.Catch/app.config delete mode 100644 osu.Game.Rulesets.Mania/OpenTK.dll.config delete mode 100644 osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Rulesets.Mania/app.config delete mode 100644 osu.Game.Rulesets.Osu/OpenTK.dll.config delete mode 100644 osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Rulesets.Osu/app.config delete mode 100644 osu.Game.Rulesets.Taiko/OpenTK.dll.config delete mode 100644 osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs delete mode 100644 osu.Game.Rulesets.Taiko/app.config create mode 100644 osu.Game.props delete mode 100644 osu.Game/OpenTK.dll.config delete mode 100644 osu.Game/Properties/AssemblyInfo.cs create mode 100644 osu.Game/Utils/ZipUtils.cs delete mode 100644 osu.Game/app.config diff --git a/LangVer.props b/LangVer.props new file mode 100644 index 0000000000..6b1d1dae73 --- /dev/null +++ b/LangVer.props @@ -0,0 +1,6 @@ + + + + 6 + + \ No newline at end of file diff --git a/OpenTK.props b/OpenTK.props new file mode 100644 index 0000000000..0876c52b66 --- /dev/null +++ b/OpenTK.props @@ -0,0 +1,9 @@ + + + + + + PreserveNewest + + + \ No newline at end of file diff --git a/osu-framework b/osu-framework index aa7fb2f83c..7d5df64338 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit aa7fb2f83c5883a23eca0f5799905669fa6cead3 +Subproject commit 7d5df64338e3b3be7bdf3509e9c47a3e9b930430 diff --git a/osu-resources b/osu-resources index 1750ab8f67..c3def1cf00 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 1750ab8f6761ab35592fd46da71fbe0c141bfd93 +Subproject commit c3def1cf0038c5a8bcbe35a56914ffeebb25b10b diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 60d70e1ece..3eaa23f4c7 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -2,10 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Drawing; using System.IO; using System.Linq; -using System.Reflection; using System.Threading.Tasks; using Microsoft.Win32; using osu.Desktop.Overlays; diff --git a/osu.Game.Rulesets.Catch/OpenTK.dll.config b/osu.Game.Rulesets.Catch/OpenTK.dll.config deleted file mode 100644 index 5620e3d9e2..0000000000 --- a/osu.Game.Rulesets.Catch/OpenTK.dll.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs deleted file mode 100644 index dd2006c60c..0000000000 --- a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Rulesets.Catch")] -[assembly: AssemblyDescription("catch the fruit. to the beat.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("ppy Pty Ltd")] -[assembly: AssemblyProduct("osu.Game.Rulesets.Catch")] -[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("58f6c80c-1253-4a0e-a465-b8c85ebeadf3")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Catch/app.config b/osu.Game.Rulesets.Catch/app.config deleted file mode 100644 index c9d4e44b1a..0000000000 --- a/osu.Game.Rulesets.Catch/app.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 0c61a5b312..76396f1815 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -1,102 +1,23 @@ - - - - - Debug - AnyCPU - {58F6C80C-1253-4A0E-A465-B8C85EBEADF3} + + + + + netstandard2.0;net461 Library - Properties - osu.Game.Rulesets.Catch - osu.Game.Rulesets.Catch - v4.6.1 - 512 + AnyCPU + true + ppy Pty Ltd + 1.0.0.0 + ppy Pty Ltd 2007-2017 + osu.Game.Rulesets.Catch + catch the fruit. to the beat. + osu.Game.Rulesets.Catch - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - 6 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - True - - - True - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - osu.licenseheader - - - - - - - - {C76BF5B3-985E-4D39-95FE-97C9C879B83A} - osu.Framework - True - - - {2a66dd92-adb1-4994-89e2-c94e04acda0d} - osu.Game - True - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/OpenTK.dll.config b/osu.Game.Rulesets.Mania/OpenTK.dll.config deleted file mode 100644 index 5620e3d9e2..0000000000 --- a/osu.Game.Rulesets.Mania/OpenTK.dll.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs deleted file mode 100644 index 85a8f95b14..0000000000 --- a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Rulesets.Mania")] -[assembly: AssemblyDescription("smash the keys. to the beat.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("ppy Pty Ltd")] -[assembly: AssemblyProduct("osu.Game.Rulesets.Mania")] -[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("48f4582b-7687-4621-9cbe-5c24197cb536")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Mania/app.config b/osu.Game.Rulesets.Mania/app.config deleted file mode 100644 index c9d4e44b1a..0000000000 --- a/osu.Game.Rulesets.Mania/app.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index edc77f5c7b..ba9176c853 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -1,125 +1,23 @@ - - - - - Debug - AnyCPU - {48F4582B-7687-4621-9CBE-5C24197CB536} + + + + + netstandard2.0;net461 Library - Properties - osu.Game.Rulesets.Mania - osu.Game.Rulesets.Mania - v4.6.1 - 512 + AnyCPU + true + ppy Pty Ltd + 1.0.0.0 + ppy Pty Ltd 2007-2017 + osu.Game.Rulests.Mania + smash the keys. to the beat. + osu.Game.Rulests.Mania - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - 6 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - True - - - True - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - osu.licenseheader - - - - - - - - {C76BF5B3-985E-4D39-95FE-97C9C879B83A} - osu.Framework - True - - - {2a66dd92-adb1-4994-89e2-c94e04acda0d} - osu.Game - True - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/OpenTK.dll.config b/osu.Game.Rulesets.Osu/OpenTK.dll.config deleted file mode 100644 index 5620e3d9e2..0000000000 --- a/osu.Game.Rulesets.Osu/OpenTK.dll.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs deleted file mode 100644 index b6cf47071a..0000000000 --- a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Mode.Osu")] -[assembly: AssemblyDescription("click the circles. to the beat.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("ppy Pty Ltd")] -[assembly: AssemblyProduct("osu.Game.Mode.Osu")] -[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c92a607b-1fdd-4954-9f92-03ff547d9080")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Osu/app.config b/osu.Game.Rulesets.Osu/app.config deleted file mode 100644 index c9d4e44b1a..0000000000 --- a/osu.Game.Rulesets.Osu/app.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 3939b3f108..a28ee218b5 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -1,133 +1,23 @@ - - - - - Debug - AnyCPU - {C92A607B-1FDD-4954-9F92-03FF547D9080} + + + + + netstandard2.0;net461 Library - Properties - osu.Game.Rulesets.Osu - osu.Game.Rulesets.Osu - v4.6.1 - 512 - + AnyCPU + true + ppy Pty Ltd + 1.0.0.0 + ppy Pty Ltd 2007-2017 + osu.Game.Rulesets.Osu + click the circles. to the beat. + osu.Game.Rulesets.Osu - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - 6 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - True - - - True - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - osu.licenseheader - - - - - - - - {C76BF5B3-985E-4D39-95FE-97C9C879B83A} - osu.Framework - True - - - {2a66dd92-adb1-4994-89e2-c94e04acda0d} - osu.Game - True - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/OpenTK.dll.config b/osu.Game.Rulesets.Taiko/OpenTK.dll.config deleted file mode 100644 index 5620e3d9e2..0000000000 --- a/osu.Game.Rulesets.Taiko/OpenTK.dll.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs deleted file mode 100644 index f6a9c8f101..0000000000 --- a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game.Rulesets.Taiko")] -[assembly: AssemblyDescription("bash the drum. to the beat.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("ppy Pty Ltd")] -[assembly: AssemblyProduct("osu.Game.Rulesets.Taiko")] -[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f167e17a-7de6-4af5-b920-a5112296c695")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game.Rulesets.Taiko/app.config b/osu.Game.Rulesets.Taiko/app.config deleted file mode 100644 index c9d4e44b1a..0000000000 --- a/osu.Game.Rulesets.Taiko/app.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 868460e3d6..c4899d3c5c 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -1,124 +1,23 @@ - - - - - Debug - AnyCPU - {F167E17A-7DE6-4AF5-B920-A5112296C695} + + + + + netstandard2.0;net461 Library - Properties - osu.Game.Rulesets.Taiko - osu.Game.Rulesets.Taiko - v4.6.1 - 512 + AnyCPU + true + ppy Pty Ltd + 1.0.0.0 + ppy Pty Ltd 2007-2017 + osu.Game.Rulesets.Taiko + bash the drum. to the beat. + osu.Game.Rulesets.Taiko - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - 6 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - True - - - True - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - osu.licenseheader - - - - - - - - {C76BF5B3-985E-4D39-95FE-97C9C879B83A} - osu.Framework - True - - - {2a66dd92-adb1-4994-89e2-c94e04acda0d} - osu.Game - True - - - - \ No newline at end of file diff --git a/osu.Game.props b/osu.Game.props new file mode 100644 index 0000000000..c352fa2b06 --- /dev/null +++ b/osu.Game.props @@ -0,0 +1,8 @@ + + + + + osu.licenseheader + + + \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index ff0abd3d78..4bce2a96da 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; -using Ionic.Zip; using Microsoft.EntityFrameworkCore; using osu.Framework.Audio.Track; using osu.Framework.Extensions; @@ -24,6 +23,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; +using osu.Game.Utils; namespace osu.Game.Beatmaps { @@ -418,7 +418,7 @@ namespace osu.Game.Beatmaps /// A reader giving access to the beatmap's content. private ArchiveReader getReaderFrom(string path) { - if (ZipFile.IsZipFile(path)) + if (ZipUtils.IsZipArchive(path)) // ReSharper disable once InconsistentlySynchronizedField return new OszArchiveReader(storage.GetStream(path)); return new LegacyFilesystemReader(path); diff --git a/osu.Game/Beatmaps/IO/OszArchiveReader.cs b/osu.Game/Beatmaps/IO/OszArchiveReader.cs index 4e0c40d28e..491bd06487 100644 --- a/osu.Game/Beatmaps/IO/OszArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/OszArchiveReader.cs @@ -4,31 +4,31 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Ionic.Zip; +using SharpCompress.Archives.Zip; namespace osu.Game.Beatmaps.IO { public sealed class OszArchiveReader : ArchiveReader { private readonly Stream archiveStream; - private readonly ZipFile archive; + private readonly ZipArchive archive; public OszArchiveReader(Stream archiveStream) { this.archiveStream = archiveStream; - archive = ZipFile.Read(archiveStream); + archive = ZipArchive.Open(archiveStream); } public override Stream GetStream(string name) { - ZipEntry entry = archive.Entries.SingleOrDefault(e => e.FileName == name); + ZipArchiveEntry entry = archive.Entries.SingleOrDefault(e => e.Key == name); if (entry == null) throw new FileNotFoundException(); // allow seeking MemoryStream copy = new MemoryStream(); - using (Stream s = entry.OpenReader()) + using (Stream s = entry.OpenEntryStream()) s.CopyTo(copy); copy.Position = 0; @@ -42,8 +42,8 @@ namespace osu.Game.Beatmaps.IO archiveStream.Dispose(); } - public override IEnumerable Filenames => archive.Entries.Select(e => e.FileName).ToArray(); + public override IEnumerable Filenames => archive.Entries.Select(e => e.Key).ToArray(); public override Stream GetUnderlyingStream() => archiveStream; } -} \ No newline at end of file +} diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index c7ea884821..e79eef4e2b 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters.Binary; using System.Text; @@ -175,7 +174,7 @@ namespace osu.Game.IO.Legacy versionBinder = new VersionConfigToNamespaceAssemblyObjectBinder(); formatter = new BinaryFormatter { - AssemblyFormat = FormatterAssemblyStyle.Simple, +// AssemblyFormat = FormatterAssemblyStyle.Simple, Binder = versionBinder }; } diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs index 7325129128..c8af66fd8a 100644 --- a/osu.Game/IO/Legacy/SerializationWriter.cs +++ b/osu.Game/IO/Legacy/SerializationWriter.cs @@ -219,7 +219,7 @@ namespace osu.Game.IO.Legacy Write((byte)ObjType.otherType); BinaryFormatter b = new BinaryFormatter { - AssemblyFormat = FormatterAssemblyStyle.Simple, +// AssemblyFormat = FormatterAssemblyStyle.Simple, TypeFormat = FormatterTypeStyle.TypesWhenNeeded }; b.Serialize(BaseStream, obj); @@ -259,4 +259,4 @@ namespace osu.Game.IO.Legacy WriteRawBytes(Encoding.UTF8.GetBytes(str)); } } -} \ No newline at end of file +} diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.cs b/osu.Game/Migrations/20171019041408_InitialCreate.cs index 23e5b6f8bb..922aa85f18 100644 --- a/osu.Game/Migrations/20171019041408_InitialCreate.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.cs @@ -1,6 +1,4 @@ using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs index a20652eedc..ad8884a4bf 100644 --- a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs +++ b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs @@ -1,6 +1,4 @@ using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 7029dcdcd5..025607bbd9 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -1,11 +1,7 @@ // using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; using osu.Game.Database; -using System; namespace osu.Game.Migrations { diff --git a/osu.Game/OpenTK.dll.config b/osu.Game/OpenTK.dll.config deleted file mode 100644 index 5620e3d9e2..0000000000 --- a/osu.Game/OpenTK.dll.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 4e7712936a..9e962a64a6 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; using osu.Game.Graphics.Sprites; diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs index 7683406b49..3a0abf0232 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using OpenTK; using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; diff --git a/osu.Game/Properties/AssemblyInfo.cs b/osu.Game/Properties/AssemblyInfo.cs deleted file mode 100644 index e28f8a3873..0000000000 --- a/osu.Game/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Game")] -[assembly: AssemblyDescription("click the circles. to the beat.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("ppy Pty Ltd")] -[assembly: AssemblyProduct("osu.Game")] -[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("55e28cb2-7b6c-4595-8dcc-9871d8aad7e9")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Game/Utils/ZipUtils.cs b/osu.Game/Utils/ZipUtils.cs new file mode 100644 index 0000000000..c1e0d75a7b --- /dev/null +++ b/osu.Game/Utils/ZipUtils.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using System; +using SharpCompress.Archives.Zip; + +namespace osu.Game.Utils +{ + public static class ZipUtils + { + public static bool IsZipArchive(string path) + { + try + { + using (var arc = ZipArchive.Open(path)) + { + foreach (var entry in arc.Entries) + { + using (entry.OpenEntryStream()) + { + } + } + } + + return true; + } + catch (Exception) + { + return false; + } + } + } +} diff --git a/osu.Game/app.config b/osu.Game/app.config deleted file mode 100644 index 7f2ad68041..0000000000 --- a/osu.Game/app.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6ef8f6570d..f995fc76e1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,823 +1,27 @@ - - - - {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D} - Debug - AnyCPU + + + + + netstandard2.0;net461 Library - Properties - osu.Game - osu.Game - 3CF060CD28877D0E3112948951A64B2A7CEEC909 - codesigning.pfx - false - false - false - - - 3.5 - - - OnOutputUpdated - false - LocalIntranet - v4.6.1 - true - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 2 - 1.0.0.%2a - false - true - 12.0.0 - 2.0 - - - - - - - true - full - false - bin\Debug\ - TRACE;DEBUG - prompt - 0 - true - false AnyCPU true - false - false - false - - - 6 + ppy Pty Ltd + 1.0.0.0 + ppy Pty Ltd 2007-2017 + osu.Game + click the circles. to the beat. + osu.Game - - none - true - bin\Release\ - - - prompt - 4 - true - false - AnyCPU - true - false - false - - - - - - - - - $(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll - True - - - $(SolutionDir)\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll - - - $(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll - - - $(SolutionDir)\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll - - - $(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Design.2.0.0\lib\net461\Microsoft.EntityFrameworkCore.Design.dll - - - $(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Relational.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll - - - $(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Sqlite.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.Configuration.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll - - - $(SolutionDir)\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll - - - - - $(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - True - - - $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - True - - - True - - - $(SolutionDir)\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll - - - $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll - True - - - $(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll - True - - - $(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll - True - - - $(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll - True - - - $(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll - True - - - - $(SolutionDir)\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll - - - $(SolutionDir)\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll - - - - - - $(SolutionDir)\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll - - - $(SolutionDir)\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll - - - $(SolutionDir)\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - - - $(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll - True - + + + - - - osu.licenseheader - - - - - + + + + + + - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - - - - - {c76bf5b3-985e-4d39-95fe-97c9c879b83a} - osu.Framework - - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} - osu.Game.Resources - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 20171019041408_InitialCreate.cs - - - - 20171025071459_AddMissingIndexRules.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.sln b/osu.sln index 356ec4cc7b..86a0e4818b 100644 --- a/osu.sln +++ b/osu.sln @@ -3,19 +3,19 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27004.2006 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "osu-framework\osu.Framework\osu.Framework.csproj", "{C76BF5B3-985E-4D39-95FE-97C9C879B83A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework", "osu-framework\osu.Framework\osu.Framework.csproj", "{C76BF5B3-985E-4D39-95FE-97C9C879B83A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Resources", "osu-resources\osu.Game.Resources\osu.Game.Resources.csproj", "{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Resources", "osu-resources\osu.Game.Resources\osu.Game.Resources.csproj", "{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj", "{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj", "{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko", "osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj", "{F167E17A-7DE6-4AF5-B920-A5112296C695}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko", "osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj", "{F167E17A-7DE6-4AF5-B920-A5112296C695}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Mania", "osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj", "{48F4582B-7687-4621-9CBE-5C24197CB536}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Mania", "osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj", "{48F4582B-7687-4621-9CBE-5C24197CB536}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Desktop.Deploy", "osu.Desktop.Deploy\osu.Desktop.Deploy.csproj", "{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}" EndProject From 440c1a9f83fca54efc8db58cc7bec66e964f5aba Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 19 Nov 2017 14:30:56 +0900 Subject: [PATCH 003/537] Make osu.Desktop and osu.Game.Tests compile under net461+netstandard --- osu.Desktop/OpenTK.dll.config | 25 -- osu.Desktop/OsuGameDesktop.cs | 2 + osu.Desktop/Overlays/VersionManager.cs | 13 ++ osu.Desktop/Properties/AssemblyInfo.cs | 28 --- osu.Desktop/Properties/app.manifest | 57 ----- osu.Desktop/app.config | 44 ---- osu.Desktop/osu.Desktop.csproj | 303 ++----------------------- osu.Game.Tests/osu.Game.Tests.csproj | 166 ++------------ osu.sln | 4 +- 9 files changed, 64 insertions(+), 578 deletions(-) delete mode 100644 osu.Desktop/OpenTK.dll.config delete mode 100644 osu.Desktop/Properties/AssemblyInfo.cs delete mode 100644 osu.Desktop/Properties/app.manifest delete mode 100644 osu.Desktop/app.config diff --git a/osu.Desktop/OpenTK.dll.config b/osu.Desktop/OpenTK.dll.config deleted file mode 100644 index 5620e3d9e2..0000000000 --- a/osu.Desktop/OpenTK.dll.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 3eaa23f4c7..b8eeeb1a5b 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -47,6 +47,7 @@ namespace osu.Desktop string stableInstallPath; +#if NET461 try { using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) @@ -58,6 +59,7 @@ namespace osu.Desktop catch { } +#endif stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); if (checkExists(stableInstallPath)) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 8c17e18ed8..8bb9d596d1 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -21,13 +21,19 @@ using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using OpenTK; using OpenTK.Graphics; + +#if NET461 using Squirrel; +#endif namespace osu.Desktop.Overlays { public class VersionManager : OverlayContainer { +#if NET461 private UpdateManager updateManager; +#endif + private NotificationOverlay notificationOverlay; private OsuConfigManager config; private OsuGameBase game; @@ -138,11 +144,15 @@ namespace osu.Desktop.Overlays protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + +#if NET461 updateManager?.Dispose(); +#endif } private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) { +#if NET461 //should we schedule a retry on completion of this check? bool scheduleRetry = true; @@ -213,6 +223,7 @@ namespace osu.Desktop.Overlays notification.State = ProgressNotificationState.Cancelled; } } +#endif } protected override void PopIn() @@ -234,7 +245,9 @@ namespace osu.Desktop.Overlays Activated = () => { // Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here +#if NET461 UpdateManager.RestartAppWhenExited().Wait(); +#endif game.GracefullyExit(); return true; } diff --git a/osu.Desktop/Properties/AssemblyInfo.cs b/osu.Desktop/Properties/AssemblyInfo.cs deleted file mode 100644 index 2ed304ebd7..0000000000 --- a/osu.Desktop/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu!lazer")] -[assembly: AssemblyDescription("click the circles. to the beat.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("ppy Pty Ltd")] -[assembly: AssemblyProduct("osu!lazer")] -[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("b0cb1d48-e4c2-4612-a347-beea7b1a71e7")] - -[assembly: AssemblyVersion("0.0.0")] -[assembly: AssemblyFileVersion("0.0.0")] diff --git a/osu.Desktop/Properties/app.manifest b/osu.Desktop/Properties/app.manifest deleted file mode 100644 index 555db8513d..0000000000 --- a/osu.Desktop/Properties/app.manifest +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - - - - - diff --git a/osu.Desktop/app.config b/osu.Desktop/app.config deleted file mode 100644 index ea1576b3d8..0000000000 --- a/osu.Desktop/app.config +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index dd328858e0..fd0644bc5d 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -1,287 +1,32 @@ - - - - {419659FD-72EA-4678-9EB8-B22A746CED70} - Debug - AnyCPU - WinExe - Properties - osu.Desktop - osu! - 3CF060CD28877D0E3112948951A64B2A7CEEC909 - codesigning.pfx - false - false - false - - - 3.5 - - - osu.Desktop.Program - OnOutputUpdated - false - LocalIntranet - v4.6.1 - true - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 2 - 1.0.0.%2a - false - true - 12.0.0 - 2.0 - - - - - - - true - full - false - bin\Debug\ - DEBUG - prompt - 0 - true - false + + + + + netcoreapp2.0;net461 + Exe AnyCPU true - false - false - false - - - 6 + ppy Pty Ltd + 0.0.0.0 + click the circles. to the beat. + ppy Pty Ltd 2007-2017 + osu!lazer + osu!lazer - - none - true - bin\Release\ - CuttingEdge NoUpdate - prompt - 4 - true - false - AnyCPU - true - false - false - - - - - - - lazer.ico - - - Properties\app.manifest - - - true - bin\Debug\ - DEBUG - true - 0 - true - full - AnyCPU - false - 6 - prompt - --tests - - - - $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll - True - - - $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.MsDelta.dll - True - - - $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll - True - - - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll - True - - - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll - True - - - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll - True - - - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll - True - - - - $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll - True - - - True - - - $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll - True - - - $(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll - True - - - $(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll - - - $(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll - - - $(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll - - - $(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll - - - $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll - True - - - - - - $(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll - True - - + + + + + + + + - - - osu.licenseheader - - - - - - + + + - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - + - - - {c76bf5b3-985e-4d39-95fe-97c9c879b83a} - osu.Framework - - - {d9a367c9-4c1a-489f-9b05-a0cea2b53b58} - osu.Game.Resources - - - {58f6c80c-1253-4a0e-a465-b8c85ebeadf3} - osu.Game.Rulesets.Catch - - - {48f4582b-7687-4621-9cbe-5c24197cb536} - osu.Game.Rulesets.Mania - - - {c92a607b-1fdd-4954-9f92-03ff547d9080} - osu.Game.Rulesets.Osu - - - {f167e17a-7de6-4af5-b920-a5112296c695} - osu.Game.Rulesets.Taiko - - - {54377672-20b1-40af-8087-5cf73bf3953a} - osu.Game.Tests - - - {2a66dd92-adb1-4994-89e2-c94e04acda0d} - osu.Game - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - \ No newline at end of file diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 3d134f79bf..e34fb4638f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,150 +1,30 @@ - - - - Debug - AnyCPU - {54377672-20B1-40AF-8087-5CF73BF3953A} + + + + + netstandard2.0;net461 Library - osu.Game.Tests - osu.Game.Tests - v4.6.1 + AnyCPU + true + ppy Pty Ltd + 1.0.0.0 + ppy Pty Ltd 2007-2017 + osu.Game.Tests + osu.Game.Tests - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - false - 6 - - - true - bin\Release - prompt - 4 - false - false - - - - $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - True - - - True - - - - $(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll - True - + + + + + + + + - - - osu.licenseheader - - - - + + - - - {c76bf5b3-985e-4d39-95fe-97c9c879b83a} - osu.Framework - - - {c92a607b-1fdd-4954-9f92-03ff547d9080} - osu.Game.Rulesets.Osu - - - {58f6c80c-1253-4a0e-a465-b8c85ebeadf3} - osu.Game.Rulesets.Catch - - - {48f4582b-7687-4621-9cbe-5c24197cb536} - osu.Game.Rulesets.Mania - - - {f167e17a-7de6-4af5-b920-a5112296c695} - osu.Game.Rulesets.Taiko - - - {2a66dd92-adb1-4994-89e2-c94e04acda0d} - osu.Game - - - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} - osu.Game.Resources - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - \ No newline at end of file diff --git a/osu.sln b/osu.sln index 86a0e4818b..c75ff78f2d 100644 --- a/osu.sln +++ b/osu.sln @@ -19,9 +19,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Mania", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Desktop.Deploy", "osu.Desktop.Deploy\osu.Desktop.Deploy.csproj", "{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tests", "osu.Game.Tests\osu.Game.Tests.csproj", "{54377672-20B1-40AF-8087-5CF73BF3953A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Tests", "osu.Game.Tests\osu.Game.Tests.csproj", "{54377672-20B1-40AF-8087-5CF73BF3953A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Desktop", "osu.Desktop\osu.Desktop.csproj", "{419659FD-72EA-4678-9EB8-B22A746CED70}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Desktop", "osu.Desktop\osu.Desktop.csproj", "{419659FD-72EA-4678-9EB8-B22A746CED70}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From db4c912e1e8fd93443790a57a905ef1647b461af Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 19 Nov 2017 14:46:51 +0900 Subject: [PATCH 004/537] Fix up assembly name --- osu.Desktop/osu.Desktop.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index fd0644bc5d..e1ab0e1476 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -10,7 +10,8 @@ 0.0.0.0 click the circles. to the beat. ppy Pty Ltd 2007-2017 - osu!lazer + osu! + osu!lazer osu!lazer From 5fd16c62682f2b6c8f84b752f52758315339a1d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 19 Nov 2017 16:30:41 +0900 Subject: [PATCH 005/537] Make osu.Desktop.Deploy work --- osu.Desktop.Deploy/App.config | 19 --- osu.Desktop.Deploy/Program.cs | 23 ++- osu.Desktop.Deploy/Properties/AssemblyInfo.cs | 38 ----- osu.Desktop.Deploy/osu.Desktop.Deploy.csproj | 146 +++--------------- osu.Desktop/osu.Desktop.csproj | 3 +- osu.sln | 5 +- 6 files changed, 41 insertions(+), 193 deletions(-) delete mode 100644 osu.Desktop.Deploy/Properties/AssemblyInfo.cs diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config index 2fae7a5e1c..42b2d34097 100644 --- a/osu.Desktop.Deploy/App.config +++ b/osu.Desktop.Deploy/App.config @@ -18,23 +18,4 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 54fb50d0f8..e13b63f602 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -299,18 +299,27 @@ namespace osu.Desktop.Deploy private static void updateAssemblyInfo(string version) { - string file = Path.Combine(ProjectName, "Properties", "AssemblyInfo.cs"); + var toUpdate = new[] { "", "" }; + string file = Path.Combine(ProjectName, $"{ProjectName}.csproj"); var l1 = File.ReadAllLines(file); List l2 = new List(); foreach (var l in l1) { - if (l.StartsWith("[assembly: AssemblyVersion(")) - l2.Add($"[assembly: AssemblyVersion(\"{version}\")]"); - else if (l.StartsWith("[assembly: AssemblyFileVersion(")) - l2.Add($"[assembly: AssemblyFileVersion(\"{version}\")]"); - else - l2.Add(l); + string line = l; + + foreach (var tag in toUpdate) + { + int startIndex = l.IndexOf(tag); + if (startIndex == -1) + continue; + startIndex += tag.Length; + + int endIndex = l.IndexOf("<", startIndex); + line = $"{l.Substring(0, startIndex)}{version}{l.Substring(endIndex)}"; + } + + l2.Add(line); } File.WriteAllLines(file, l2); diff --git a/osu.Desktop.Deploy/Properties/AssemblyInfo.cs b/osu.Desktop.Deploy/Properties/AssemblyInfo.cs deleted file mode 100644 index 5841c1b082..0000000000 --- a/osu.Desktop.Deploy/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("osu.Desktop.Deploy")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("osu.Desktop.Deploy")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("baea2f74-0315-4667-84e0-acac0b4bf785")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index 6727a86a91..ccbf351bda 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -1,127 +1,21 @@ - - - - - Debug - AnyCPU - {BAEA2F74-0315-4667-84E0-ACAC0B4BF785} - Exe - Properties - osu.Desktop.Deploy - osu.Desktop.Deploy - v4.6.1 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - 6 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - osu.Desktop.Deploy.Program - - - - $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll - True - - - $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.MsDelta.dll - True - - - $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll - True - - - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll - True - - - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll - True - - - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll - True - - - $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll - True - - - $(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - True - - - $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll - True - - - $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll - True - - - $(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll - True - - - $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll - True - - - - - - - - - - - - - - - - - - - - osu.licenseheader - - - PreserveNewest - - - - - - {C76BF5B3-985E-4D39-95FE-97C9C879B83A} - osu.Framework - - - - + + + + net461 + Exe + AnyCPU + true + ppy Pty Ltd + ppy Pty Ltd 2007-2017 + osu.Desktop.Deploy + osu.Desktop.Deploy + + + + + + + + + \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index e1ab0e1476..34da4fe5a9 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -7,12 +7,13 @@ AnyCPU true ppy Pty Ltd - 0.0.0.0 click the circles. to the beat. ppy Pty Ltd 2007-2017 osu! osu!lazer osu!lazer + 0.0.0.0 + 0.0.0.0 diff --git a/osu.sln b/osu.sln index c75ff78f2d..934ea874d1 100644 --- a/osu.sln +++ b/osu.sln @@ -17,7 +17,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Mania", "osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj", "{48F4582B-7687-4621-9CBE-5C24197CB536}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Desktop.Deploy", "osu.Desktop.Deploy\osu.Desktop.Deploy.csproj", "{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Desktop.Deploy", "osu.Desktop.Deploy\osu.Desktop.Deploy.csproj", "{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Tests", "osu.Game.Tests\osu.Game.Tests.csproj", "{54377672-20B1-40AF-8087-5CF73BF3953A}" EndProject @@ -71,8 +71,9 @@ Global {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.Build.0 = Debug|Any CPU + {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.Build.0 = Debug|Any CPUı {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.Build.0 = Debug|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Release|Any CPU.ActiveCfg = Release|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU From 7f757123e51324c77f703b4f6ff3acc402fcfd27 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 19 Nov 2017 17:24:32 +0900 Subject: [PATCH 006/537] Update vscode launch configurations --- .vscode/launch.json | 86 ++++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 506915f462..6c0a8929f7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,13 +1,10 @@ { "version": "0.2.0", "configurations": [{ - "name": "osu! VisualTests (Debug)", - "windows": { - "type": "clr" - }, - "type": "mono", + "name": "osu! VisualTests (Debug, .NETFramework)", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe", + "type": "clr", + "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net461/osu!.exe", "args": [ "--tests" ], @@ -18,13 +15,24 @@ "console": "internalConsole" }, { - "name": "osu! VisualTests (Release)", - "windows": { - "type": "clr" - }, - "type": "mono", + "name": "osu! VisualTests (Debug, .NETCore)", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Release/osu!.exe", + "type": "coreclr", + "program": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll", + "args": [ + "--tests" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "osu! VisualTests (Release, .NETFramework)", + "request": "launch", + "type": "clr", + "program": "${workspaceRoot}/osu.Desktop/bin/Release/net461/osu!.exe", "args": [ "--tests" ], @@ -35,13 +43,24 @@ "console": "internalConsole" }, { - "name": "osu! (Debug)", - "windows": { - "type": "clr" - }, - "type": "mono", + "name": "osu! VisualTests (Release, .NETCore)", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe", + "type": "coreclr", + "program": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll", + "args": [ + "--tests" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "osu! (Debug, .NETFramework)", + "request": "launch", + "type": "clr", + "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net461/osu!.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug)", "runtimeExecutable": null, @@ -49,13 +68,32 @@ "console": "internalConsole" }, { - "name": "osu! (Release)", - "windows": { - "type": "clr" - }, - "type": "mono", + "name": "osu! (Debug, .NETCore)", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Release/osu!.exe", + "type": "coreclr", + "program": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "osu! (Release, .NETFramework)", + "request": "launch", + "type": "clr", + "program": "${workspaceRoot}/osu.Desktop/bin/Release/net461/osu!.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "osu! (Release, .NETCore)", + "request": "launch", + "type": "coreclr", + "program": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release)", "runtimeExecutable": null, From 067c018422abc5f4e7ad1a72e8b2c730c28a430d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 19 Nov 2017 17:31:35 +0900 Subject: [PATCH 007/537] Cleanups --- osu-framework | 2 +- osu.Desktop.Deploy/packages.config | 14 ---- osu.Desktop/packages.config | 20 ------ osu.Game.Rulesets.Catch/packages.config | 5 -- osu.Game.Rulesets.Mania/packages.config | 5 -- osu.Game.Rulesets.Osu/packages.config | 5 -- osu.Game.Rulesets.Taiko/packages.config | 5 -- osu.Game.Tests/OpenTK.dll.config | 25 ------- osu.Game.Tests/app.config | 15 ---- osu.Game.Tests/packages.config | 10 --- osu.Game/packages.config | 95 ------------------------- 11 files changed, 1 insertion(+), 200 deletions(-) delete mode 100644 osu.Desktop.Deploy/packages.config delete mode 100644 osu.Desktop/packages.config delete mode 100644 osu.Game.Rulesets.Catch/packages.config delete mode 100644 osu.Game.Rulesets.Mania/packages.config delete mode 100644 osu.Game.Rulesets.Osu/packages.config delete mode 100644 osu.Game.Rulesets.Taiko/packages.config delete mode 100644 osu.Game.Tests/OpenTK.dll.config delete mode 100644 osu.Game.Tests/app.config delete mode 100644 osu.Game.Tests/packages.config delete mode 100644 osu.Game/packages.config diff --git a/osu-framework b/osu-framework index 7d5df64338..90dbacf0c4 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 7d5df64338e3b3be7bdf3509e9c47a3e9b930430 +Subproject commit 90dbacf0c4f54a2076f1a0be98f8606b627cf947 diff --git a/osu.Desktop.Deploy/packages.config b/osu.Desktop.Deploy/packages.config deleted file mode 100644 index 7725be5f5e..0000000000 --- a/osu.Desktop.Deploy/packages.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config deleted file mode 100644 index 6b6361b578..0000000000 --- a/osu.Desktop/packages.config +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config deleted file mode 100644 index cde428acea..0000000000 --- a/osu.Game.Rulesets.Catch/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config deleted file mode 100644 index cde428acea..0000000000 --- a/osu.Game.Rulesets.Mania/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config deleted file mode 100644 index cde428acea..0000000000 --- a/osu.Game.Rulesets.Osu/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config deleted file mode 100644 index cde428acea..0000000000 --- a/osu.Game.Rulesets.Taiko/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/osu.Game.Tests/OpenTK.dll.config b/osu.Game.Tests/OpenTK.dll.config deleted file mode 100644 index 5620e3d9e2..0000000000 --- a/osu.Game.Tests/OpenTK.dll.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osu.Game.Tests/app.config b/osu.Game.Tests/app.config deleted file mode 100644 index 2f5b13a89d..0000000000 --- a/osu.Game.Tests/app.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config deleted file mode 100644 index ecc44f0c70..0000000000 --- a/osu.Game.Tests/packages.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/osu.Game/packages.config b/osu.Game/packages.config deleted file mode 100644 index 02ace918de..0000000000 --- a/osu.Game/packages.config +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 756f6f2d163b5b0f54ead7af7d1eda50c816ff2e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Nov 2017 10:10:28 +0900 Subject: [PATCH 008/537] Remove net461 targets --- osu-framework | 2 +- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 2 +- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 2 +- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 2 +- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu-framework b/osu-framework index 90dbacf0c4..56ca2152f3 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 90dbacf0c4f54a2076f1a0be98f8606b627cf947 +Subproject commit 56ca2152f3096217cd327721519cd1eb7e57fbee diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 76396f1815..ce07b8c7ff 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -2,7 +2,7 @@ - netstandard2.0;net461 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index ba9176c853..90d2ad05d4 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -2,7 +2,7 @@ - netstandard2.0;net461 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index a28ee218b5..8cce2de1b4 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -2,7 +2,7 @@ - netstandard2.0;net461 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index c4899d3c5c..f4672b913e 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -2,7 +2,7 @@ - netstandard2.0;net461 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index e34fb4638f..adc96ae3cd 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -2,7 +2,7 @@ - netstandard2.0;net461 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f995fc76e1..dd591bcba8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -2,7 +2,7 @@ - netstandard2.0;net461 + netstandard2.0 Library AnyCPU true From e608d2841bc4808ad932769ab978b357fedd0c93 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Nov 2017 16:10:34 +0900 Subject: [PATCH 009/537] Prefer net461 --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 34da4fe5a9..3b08715d2d 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -2,7 +2,7 @@ - netcoreapp2.0;net461 + net461;netcoreapp2.0 Exe AnyCPU true From f0966a2e1264712f5d4c983342685b09db838b21 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Nov 2017 18:40:00 +0900 Subject: [PATCH 010/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 56ca2152f3..b77ceb37e6 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 56ca2152f3096217cd327721519cd1eb7e57fbee +Subproject commit b77ceb37e6186cbf6f5b8a72959d753fbb56f9a5 From aac41d2de63f154aa98dfa16f24cb3bcb6fa5466 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Nov 2017 18:55:48 +0900 Subject: [PATCH 011/537] Disable resharper inspections on case-by-case basis --- osu.Desktop.Deploy/Program.cs | 4 ++-- osu.Desktop/OsuGameDesktop.cs | 11 +++++++++-- osu.Desktop/Overlays/VersionManager.cs | 12 +++++++----- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 1 + osu.Game/Beatmaps/BeatmapManager.cs | 1 + osu.Game/Graphics/Cursor/MenuCursor.cs | 1 + osu.Game/IO/Legacy/SerializationReader.cs | 1 + osu.Game/Online/API/OAuth.cs | 1 + .../Online/API/Requests/GetUserBeatmapsRequest.cs | 1 + .../Online/API/Requests/GetUserScoresRequest.cs | 3 ++- .../API/Requests/SearchBeatmapSetsRequest.cs | 1 + osu.Game/Online/Chat/Message.cs | 1 + osu.Game/Overlays/ChatOverlay.cs | 2 ++ osu.Game/Overlays/MusicController.cs | 1 + .../Rulesets/Timing/MultiplierControlPoint.cs | 1 + osu.Game/Rulesets/UI/RulesetContainer.cs | 1 + osu.Game/Rulesets/UI/ScrollingPlayfield.cs | 1 + .../Select/Leaderboards/LeaderboardScore.cs | 1 + osu.Game/Screens/Tournament/Drawings.cs | 1 + .../Screens/Tournament/ScrollingTeamContainer.cs | 15 +++++++++------ .../Tournament/Teams/StorageBackedTeamList.cs | 1 + 21 files changed, 46 insertions(+), 16 deletions(-) diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index e13b63f602..3c1d532c0f 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -310,12 +310,12 @@ namespace osu.Desktop.Deploy foreach (var tag in toUpdate) { - int startIndex = l.IndexOf(tag); + int startIndex = l.IndexOf(tag, StringComparison.InvariantCulture); if (startIndex == -1) continue; startIndex += tag.Length; - int endIndex = l.IndexOf("<", startIndex); + int endIndex = l.IndexOf("<", startIndex, StringComparison.InvariantCulture); line = $"{l.Substring(0, startIndex)}{version}{l.Substring(endIndex)}"; } diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b8eeeb1a5b..ff4b37a8fb 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -5,13 +5,16 @@ using System; using System.IO; using System.Linq; using System.Threading.Tasks; -using Microsoft.Win32; using osu.Desktop.Overlays; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osu.Game; using OpenTK.Input; +#if NET461 +using Microsoft.Win32; +#endif + namespace osu.Desktop { internal class OsuGameDesktop : OsuGame @@ -45,9 +48,9 @@ namespace osu.Desktop { Func checkExists = p => Directory.Exists(Path.Combine(p, "Songs")); +#if NET461 string stableInstallPath; -#if NET461 try { using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) @@ -61,7 +64,11 @@ namespace osu.Desktop } #endif +#if NET461 stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); +#else + var stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); +#endif if (checkExists(stableInstallPath)) return stableInstallPath; diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 8bb9d596d1..fef7e6d52a 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -1,9 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Diagnostics; -using System.Net.Http; using osu.Framework.Allocation; using osu.Framework.Development; using osu.Framework.Graphics; @@ -12,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Logging; using osu.Game; using osu.Game.Configuration; using osu.Game.Graphics; @@ -23,6 +20,9 @@ using OpenTK; using OpenTK.Graphics; #if NET461 +using System; +using System.Net.Http; +using osu.Framework.Logging; using Squirrel; #endif @@ -101,8 +101,10 @@ namespace osu.Desktop.Overlays } }; +#if NET461 if (game.IsDeployedBuild) checkForUpdateAsync(); +#endif } protected override void LoadComplete() @@ -150,9 +152,9 @@ namespace osu.Desktop.Overlays #endif } +#if NET461 private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) { -#if NET461 //should we schedule a retry on completion of this check? bool scheduleRetry = true; @@ -223,8 +225,8 @@ namespace osu.Desktop.Overlays notification.State = ProgressNotificationState.Cancelled; } } -#endif } +#endif protected override void PopIn() { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index a5be6a7952..793a9b73ad 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -128,6 +128,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { Trace.Assert(lastPosition.HasValue); + // ReSharper disable once PossibleInvalidOperationException Vector2 pos1 = lastPosition.Value; Vector2 diff = pos2 - pos1; float distance = diff.Length; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 4bce2a96da..13cefc6a73 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -307,6 +307,7 @@ namespace osu.Game.Beatmaps context.ChangeTracker.AutoDetectChangesEnabled = false; // re-fetch the beatmap set on the import context. + // ReSharper disable once AccessToModifiedClosure beatmapSet = context.BeatmapSetInfo.Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == beatmapSet.ID); // create local stores so we can isolate and thread safely, and share a context/transaction. diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index da117a94c1..451422038f 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -33,6 +33,7 @@ namespace osu.Game.Graphics.Cursor // don't start rotating until we're moved a minimum distance away from the mouse down location, // else it can have an annoying effect. + // ReSharper disable once PossibleInvalidOperationException startRotation |= Vector2Extensions.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30; if (startRotation) diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index e79eef4e2b..393a13448e 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -186,6 +186,7 @@ namespace osu.Game.IO.Legacy Debug.Assert(formatter != null, "formatter != null"); + // ReSharper disable once PossibleNullReferenceException return formatter.Deserialize(stream); } diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs index ca38f72904..198c71f0c1 100644 --- a/osu.Game/Online/API/OAuth.cs +++ b/osu.Game/Online/API/OAuth.cs @@ -96,6 +96,7 @@ namespace osu.Game.Online.API // if not, let's try using our refresh token to request a new access token. if (!string.IsNullOrEmpty(Token?.RefreshToken)) + // ReSharper disable once PossibleNullReferenceException AuthenticateWithRefresh(Token.RefreshToken); return accessTokenValid; diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index a66799f404..923ae0e269 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -19,6 +19,7 @@ namespace osu.Game.Online.API.Requests this.type = type; } + // ReSharper disable once ImpureMethodCallOnReadonlyValueField protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; } diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 98db234196..e30a449978 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -18,6 +18,7 @@ namespace osu.Game.Online.API.Requests this.offset = offset; } + // ReSharper disable once ImpureMethodCallOnReadonlyValueField protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLower()}?offset={offset}"; } @@ -27,4 +28,4 @@ namespace osu.Game.Online.API.Requests Firsts, Recent } -} \ No newline at end of file +} diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 56858b3d56..b949bf35f0 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -27,6 +27,7 @@ namespace osu.Game.Online.API.Requests this.direction = direction; } + // ReSharper disable once ImpureMethodCallOnReadonlyValueField protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; } } diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs index 79b5c4fc1a..972e4755c9 100644 --- a/osu.Game/Online/Chat/Message.cs +++ b/osu.Game/Online/Chat/Message.cs @@ -57,6 +57,7 @@ namespace osu.Game.Online.Chat public virtual bool Equals(Message other) => Id == other?.Id; + // ReSharper disable once ImpureMethodCallOnReadonlyValueField public override int GetHashCode() => Id.GetHashCode(); } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 24fc322199..ef514c95fd 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -207,6 +207,7 @@ namespace osu.Game.Overlays { Trace.Assert(state.Mouse.PositionMouseDown != null); + // ReSharper disable once PossibleInvalidOperationException double targetChatHeight = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y; // If the channel selection screen is shown, mind its minimum height @@ -380,6 +381,7 @@ namespace osu.Game.Overlays { if (channel == null) return; + // ReSharper disable once AccessToModifiedClosure var existing = careChannels.Find(c => c.Id == channel.Id); if (existing != null) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index a99ce89a36..cf8946e399 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -71,6 +71,7 @@ namespace osu.Game.Overlays { Trace.Assert(state.Mouse.PositionMouseDown != null, "state.Mouse.PositionMouseDown != null"); + // ReSharper disable once PossibleInvalidOperationException Vector2 change = state.Mouse.Position - state.Mouse.PositionMouseDown.Value; // Diminish the drag distance as we go further to simulate "rubber band" feeling. diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs index 4f1a85cf2d..ba0e66e04c 100644 --- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs +++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs @@ -60,6 +60,7 @@ namespace osu.Game.Rulesets.Timing DifficultyPoint = other.DifficultyPoint; } + // ReSharper disable once ImpureMethodCallOnReadonlyValueField public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime); } } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 278814ea7e..b627db8aff 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -157,6 +157,7 @@ namespace osu.Game.Rulesets.UI WorkingBeatmap = workingBeatmap; IsForCurrentRuleset = isForCurrentRuleset; + // ReSharper disable once PossibleNullReferenceException Mods = workingBeatmap.Mods.Value; RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs index 395248b2fd..cff3a029e6 100644 --- a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs @@ -255,6 +255,7 @@ namespace osu.Game.Rulesets.UI var sX = (SpeedAdjustmentContainer)x; var sY = (SpeedAdjustmentContainer)y; + // ReSharper disable once ImpureMethodCallOnReadonlyValueField int result = sY.ControlPoint.StartTime.CompareTo(sX.ControlPoint.StartTime); if (result != 0) return result; diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index 9044938a75..6ddafe085e 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -123,6 +123,7 @@ namespace osu.Game.Screens.Select.Leaderboards Origin = Anchor.CentreLeft, Font = @"Exo2.0-MediumItalic", TextSize = 22, + // ReSharper disable once ImpureMethodCallOnReadonlyValueField Text = RankPosition.ToString(), }, }, diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index e540782fc1..269b206a78 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -326,6 +326,7 @@ namespace osu.Game.Screens.Tournament if (line.ToUpper().StartsWith("GROUP")) continue; + // ReSharper disable once AccessToModifiedClosure DrawingsTeam teamToAdd = allTeams.FirstOrDefault(t => t.FullName == line); if (teamToAdd == null) diff --git a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs index 2eb0dacec3..5bae30458e 100644 --- a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs +++ b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs @@ -13,9 +13,9 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Threading; +using osu.Game.Screens.Tournament.Teams; using OpenTK; using OpenTK.Graphics; -using osu.Game.Screens.Tournament.Teams; namespace osu.Game.Screens.Tournament { @@ -118,16 +118,18 @@ namespace osu.Game.Screens.Tournament if (!Children.Any()) break; - Drawable closest = null; + ScrollingTeam closest = null; foreach (var c in Children) { - if (!(c is ScrollingTeam)) + var stc = c as ScrollingTeam; + + if (stc == null) continue; if (closest == null) { - closest = c; + closest = stc; continue; } @@ -135,14 +137,15 @@ namespace osu.Game.Screens.Tournament float lastOffset = Math.Abs(closest.Position.X + closest.DrawWidth / 2f - DrawWidth / 2f); if (o < lastOffset) - closest = c; + closest = stc; } Trace.Assert(closest != null, "closest != null"); + // ReSharper disable once PossibleNullReferenceException offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f); - ScrollingTeam st = closest as ScrollingTeam; + ScrollingTeam st = closest; availableTeams.RemoveAll(at => at == st.Team); diff --git a/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs b/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs index 1b2d84a666..ce3b5ed941 100644 --- a/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs +++ b/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs @@ -38,6 +38,7 @@ namespace osu.Game.Screens.Tournament.Teams if (string.IsNullOrEmpty(line)) continue; + // ReSharper disable once PossibleNullReferenceException string[] split = line.Split(':'); if (split.Length < 2) From 7cfca866e20793efa0aae16cafefba4b8154b8d3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Nov 2017 19:43:46 +0900 Subject: [PATCH 012/537] Don't compile osu.Desktop.Deploy now --- osu.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.sln b/osu.sln index 934ea874d1..3093c19868 100644 --- a/osu.sln +++ b/osu.sln @@ -73,7 +73,6 @@ Global {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.Build.0 = Debug|Any CPUı {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.Build.0 = Debug|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Release|Any CPU.ActiveCfg = Release|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU From e98d9e8a8cd90b0b2e7c62917e516876453a8fac Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 20 Nov 2017 19:44:06 +0900 Subject: [PATCH 013/537] Explicitly define constant --- osu-framework | 2 +- osu.Desktop/OsuGameDesktop.cs | 6 +++--- osu.Desktop/Overlays/VersionManager.cs | 12 ++++++------ osu.Desktop/osu.Desktop.csproj | 3 +++ 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/osu-framework b/osu-framework index b77ceb37e6..6b305b3332 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b77ceb37e6186cbf6f5b8a72959d753fbb56f9a5 +Subproject commit 6b305b3332cdc424ef7fc3c9499c21e174f9eec5 diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index ff4b37a8fb..d356fcf53d 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -11,7 +11,7 @@ using osu.Framework.Platform; using osu.Game; using OpenTK.Input; -#if NET461 +#if NET_FRAMEWORK using Microsoft.Win32; #endif @@ -48,7 +48,7 @@ namespace osu.Desktop { Func checkExists = p => Directory.Exists(Path.Combine(p, "Songs")); -#if NET461 +#if NET_FRAMEWORK string stableInstallPath; try @@ -64,7 +64,7 @@ namespace osu.Desktop } #endif -#if NET461 +#if NET_FRAMEWORK stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); #else var stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index fef7e6d52a..3db7297385 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -19,7 +19,7 @@ using osu.Game.Overlays.Notifications; using OpenTK; using OpenTK.Graphics; -#if NET461 +#if NET_FRAMEWORK using System; using System.Net.Http; using osu.Framework.Logging; @@ -30,7 +30,7 @@ namespace osu.Desktop.Overlays { public class VersionManager : OverlayContainer { -#if NET461 +#if NET_FRAMEWORK private UpdateManager updateManager; #endif @@ -101,7 +101,7 @@ namespace osu.Desktop.Overlays } }; -#if NET461 +#if NET_FRAMEWORK if (game.IsDeployedBuild) checkForUpdateAsync(); #endif @@ -147,12 +147,12 @@ namespace osu.Desktop.Overlays { base.Dispose(isDisposing); -#if NET461 +#if NET_FRAMEWORK updateManager?.Dispose(); #endif } -#if NET461 +#if NET_FRAMEWORK private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) { //should we schedule a retry on completion of this check? @@ -247,7 +247,7 @@ namespace osu.Desktop.Overlays Activated = () => { // Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here -#if NET461 +#if NET_FRAMEWORK UpdateManager.RestartAppWhenExited().Wait(); #endif game.GracefullyExit(); diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 3b08715d2d..9fed566703 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -15,6 +15,9 @@ 0.0.0.0 0.0.0.0 + + $(DefineConstants);NET_FRAMEWORK + From 538acadd230154db7390dbb01d6454fc7ff77b21 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Nov 2017 15:29:15 +0900 Subject: [PATCH 014/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 6b305b3332..0c40800ad4 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 6b305b3332cdc424ef7fc3c9499c21e174f9eec5 +Subproject commit 0c40800ad40e2119ae9e68a815d677814490d55c From a66edea6dc281f6a24d21fa8943e13ea785d43ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Nov 2017 15:33:05 +0900 Subject: [PATCH 015/537] Move sqlite batteries to osu.Desktop Fixes e_sqlite.dll not being copied to output on Windows --- osu.Desktop/Program.cs | 3 +++ osu.Desktop/osu.Desktop.csproj | 1 + osu.Game/Database/OsuDbContext.cs | 6 ------ osu.Game/osu.Game.csproj | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 720b38144c..b7ecefe1ce 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -15,6 +15,9 @@ namespace osu.Desktop [STAThread] public static int Main(string[] args) { + // required to initialise native SQLite libraries on some platforms. + SQLitePCL.Batteries_V2.Init(); + // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 9fed566703..56a455e6b7 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -28,6 +28,7 @@ + diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 928c355696..4208bad2d3 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -29,12 +29,6 @@ namespace osu.Game.Database private static readonly Lazy logger = new Lazy(() => new OsuDbLoggerFactory()); - static OsuDbContext() - { - // required to initialise native SQLite libraries on some platforms. - SQLitePCL.Batteries_V2.Init(); - } - /// /// Create a new in-memory OsuDbContext instance. /// diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index dd591bcba8..b8c1e0551c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,7 +19,7 @@ - + From 465531116520191f45505190597f096fe7e46d02 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Nov 2017 16:38:23 +0900 Subject: [PATCH 016/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 0c40800ad4..ec7d5532b1 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 0c40800ad40e2119ae9e68a815d677814490d55c +Subproject commit ec7d5532b16acf25d2e23a03324b6d99c77cbe01 From 44168b1654757b47aa80f997413ee1a5fcf2764b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Nov 2017 16:42:54 +0900 Subject: [PATCH 017/537] Fix incorrect license --- osu.Game/Utils/ZipUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/ZipUtils.cs b/osu.Game/Utils/ZipUtils.cs index c1e0d75a7b..c9a16eb84b 100644 --- a/osu.Game/Utils/ZipUtils.cs +++ b/osu.Game/Utils/ZipUtils.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; using SharpCompress.Archives.Zip; From 05aa6faf5eb3a340b7d71b7acea3db98938d0a4b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Nov 2017 19:46:52 +0900 Subject: [PATCH 018/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index ec7d5532b1..f6926e465d 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit ec7d5532b16acf25d2e23a03324b6d99c77cbe01 +Subproject commit f6926e465dfecc99f7b11013c7e2428b736bfea2 From d364603e4550faa77b295a0142d765b080d1f51f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Nov 2017 20:02:31 +0900 Subject: [PATCH 019/537] Make registry checking work again --- osu-framework | 2 +- osu.Desktop/OsuGameDesktop.cs | 9 --------- osu.Desktop/osu.Desktop.csproj | 1 + 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/osu-framework b/osu-framework index f6926e465d..a4cbd332c4 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit f6926e465dfecc99f7b11013c7e2428b736bfea2 +Subproject commit a4cbd332c4fcdd682252f5a89d8c01052b405e86 diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index d356fcf53d..f2906f043c 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -10,10 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osu.Game; using OpenTK.Input; - -#if NET_FRAMEWORK using Microsoft.Win32; -#endif namespace osu.Desktop { @@ -48,7 +45,6 @@ namespace osu.Desktop { Func checkExists = p => Directory.Exists(Path.Combine(p, "Songs")); -#if NET_FRAMEWORK string stableInstallPath; try @@ -62,13 +58,8 @@ namespace osu.Desktop catch { } -#endif -#if NET_FRAMEWORK stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); -#else - var stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); -#endif if (checkExists(stableInstallPath)) return stableInstallPath; diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 56a455e6b7..a8f1a86a7a 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -26,6 +26,7 @@ + From 1456f98aff503040eaa563e4c1f282c11f065495 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Nov 2017 21:04:14 +0900 Subject: [PATCH 020/537] Ensure that osu.Desktop.Deploy works Closes https://github.com/ppy/osu/issues/1545. --- osu.Desktop.Deploy/.vscode/launch.json | 29 ++++++++++++ osu.Desktop.Deploy/.vscode/tasks.json | 64 ++++++++++++++++++++++++++ osu.Desktop.Deploy/Program.cs | 10 ++-- 3 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 osu.Desktop.Deploy/.vscode/launch.json create mode 100644 osu.Desktop.Deploy/.vscode/tasks.json diff --git a/osu.Desktop.Deploy/.vscode/launch.json b/osu.Desktop.Deploy/.vscode/launch.json new file mode 100644 index 0000000000..82cd6b4c13 --- /dev/null +++ b/osu.Desktop.Deploy/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [{ + "name": "Deploy (Debug)", + "request": "launch", + "type": "mono", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Desktop.Deploy.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "Deploy (Release)", + "request": "launch", + "type": "clr", + "program": "${workspaceRoot}/bin/Release/net461/osu.Desktop.Deploy.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + } + ] +} \ No newline at end of file diff --git a/osu.Desktop.Deploy/.vscode/tasks.json b/osu.Desktop.Deploy/.vscode/tasks.json new file mode 100644 index 0000000000..35bf9e7a0e --- /dev/null +++ b/osu.Desktop.Deploy/.vscode/tasks.json @@ -0,0 +1,64 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "command": "msbuild", + "type": "shell", + "suppressTaskName": true, + "args": [ + "/property:GenerateFullPaths=true", + "/property:DebugType=portable", + "/verbosity:minimal", + "/m" //parallel compiling support. + ], + "tasks": [{ + "taskName": "Build (Debug)", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$msCompile" + ] + }, + { + "taskName": "Build (Release)", + "group": "build", + "args": [ + "/property:Configuration=Release" + ], + "problemMatcher": [ + "$msCompile" + ] + }, + { + "taskName": "Clean (Debug)", + "args": [ + "/target:Clean" + ], + "problemMatcher": [ + "$msCompile" + ] + }, + { + "taskName": "Clean (Release)", + "args": [ + "/target:Clean", + "/property:Configuration=Release" + ], + "problemMatcher": [ + "$msCompile" + ] + }, + { + "taskName": "Clean All", + "dependsOn": [ + "Clean (Debug)", + "Clean (Release)" + ], + "problemMatcher": [ + "$msCompile" + ] + } + ] +} \ No newline at end of file diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 3c1d532c0f..1be4bdeb0c 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -96,7 +96,7 @@ namespace osu.Desktop.Deploy runCommand(nuget_path, "restore " + solutionPath); write("Updating AssemblyInfo..."); - updateAssemblyInfo(version); + updateCsprojVersion(version); write("Running build process..."); foreach (string targetName in TargetNames.Split(',')) @@ -123,7 +123,7 @@ namespace osu.Desktop.Deploy uploadBuild(version); //reset assemblyinfo. - updateAssemblyInfo("0.0.0"); + updateCsprojVersion("0.0.0"); write("Done!", ConsoleColor.White); Console.ReadLine(); @@ -297,7 +297,7 @@ namespace osu.Desktop.Deploy Directory.CreateDirectory(directory); } - private static void updateAssemblyInfo(string version) + private static void updateCsprojVersion(string version) { var toUpdate = new[] { "", "" }; string file = Path.Combine(ProjectName, $"{ProjectName}.csproj"); @@ -336,8 +336,8 @@ namespace osu.Desktop.Deploy path = Environment.CurrentDirectory; while (!File.Exists(Path.Combine(path, $"{SolutionName}.sln"))) - path = path.Remove(path.LastIndexOf('\\')); - path += "\\"; + path = path.Remove(path.LastIndexOf(Path.DirectorySeparatorChar)); + path += Path.DirectorySeparatorChar; Environment.CurrentDirectory = path; } From 6003282e663c31b51173e1f5a024502073a76bb6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Nov 2017 22:07:55 +0900 Subject: [PATCH 021/537] Give osu!lazer an application icon again Fixes https://github.com/ppy/osu/issues/1548. --- osu.Desktop/osu.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index a8f1a86a7a..ca2b1c2bbe 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -12,6 +12,7 @@ osu! osu!lazer osu!lazer + lazer.ico 0.0.0.0 0.0.0.0 From 6e50fa29147f0fd75c4d0460ab0b7431541c8caa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 23:47:42 +0900 Subject: [PATCH 022/537] Update submodules --- osu-framework | 2 +- osu-resources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index a4cbd332c4..d830e87aaf 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit a4cbd332c4fcdd682252f5a89d8c01052b405e86 +Subproject commit d830e87aafd666aa0a42bd42351b17b05157dcaf diff --git a/osu-resources b/osu-resources index c3def1cf00..c79d917605 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit c3def1cf0038c5a8bcbe35a56914ffeebb25b10b +Subproject commit c79d917605fa792a5f046fd70d7aa75e58dc9fbc From ec04871abd756dc6fe39d23c500014f943c7e72e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 00:02:10 +0900 Subject: [PATCH 023/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index d830e87aaf..92fd14146f 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d830e87aafd666aa0a42bd42351b17b05157dcaf +Subproject commit 92fd14146f41c63df29032684d66be40a34fad6b From 49f0a7ff36af98b768fe866f0d5ad598c363b0b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 00:02:22 +0900 Subject: [PATCH 024/537] Add LangVer to osu.Game.props --- osu.Game.props | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.props b/osu.Game.props index c352fa2b06..61d5bea511 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -1,5 +1,6 @@ + osu.licenseheader From 0ec10ba32d9ecfc9fec949a38aa6a514bd817df5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 00:11:00 +0900 Subject: [PATCH 025/537] CI fixes --- osu.Desktop/Overlays/VersionManager.cs | 1 - osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs | 4 ++-- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 2 +- osu.Game/Overlays/Settings/SidebarButton.cs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 9f2f79f1df..0780ed9cce 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -21,7 +21,6 @@ using OpenTK.Graphics; #if NET_FRAMEWORK using System; -using System.Net.Http; using osu.Framework.Logging; using Squirrel; #endif diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index 95b691e07f..53989c8775 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -126,7 +126,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var beatmap = decoder.Decode(new StreamReader(stream)); var curveData = beatmap.HitObjects[0] as IHasCurve; - var positionData = beatmap.HitObjects[0] as IHasPosition; + var positionData = (IHasPosition)beatmap.HitObjects[0]; Assert.IsNotNull(positionData); Assert.IsNotNull(curveData); @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(956, beatmap.HitObjects[0].StartTime); Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); - positionData = beatmap.HitObjects[1] as IHasPosition; + positionData = (IHasPosition)beatmap.HitObjects[1]; Assert.IsNotNull(positionData); Assert.AreEqual(new Vector2(304, 56), positionData.Position); diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 8a835634b8..b59df1214a 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { private readonly OsuSpriteText valueText; - public int Count + public new int Count { set { valueText.Text = value.ToString(); } } diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index 4b8366f0fc..bc5357f05a 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Settings private readonly SpriteText headerText; private readonly Box selectionIndicator; private readonly Container text; - public Action Action; + public new Action Action; private SettingsSection section; public SettingsSection Section From 599a86a69a2d06460755ad45ae6356ebb0e22d23 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Dec 2017 18:50:59 +0900 Subject: [PATCH 026/537] Remove LangVer.props --- LangVer.props | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 LangVer.props diff --git a/LangVer.props b/LangVer.props deleted file mode 100644 index 6b1d1dae73..0000000000 --- a/LangVer.props +++ /dev/null @@ -1,6 +0,0 @@ - - - - 6 - - \ No newline at end of file From 741153e1a283cdca1ded7a2d9ce1ec8510ca4de3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Dec 2017 18:51:20 +0900 Subject: [PATCH 027/537] Remove unnecessary properties --- osu.Game.props | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.props b/osu.Game.props index 60a5e97944..07abeb5539 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -1,7 +1,5 @@ - - - + 7 From 1b83f2e3d452e6790e6599c4ef503d44b245258c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Dec 2017 18:53:36 +0900 Subject: [PATCH 028/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 92fd14146f..19e8161046 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 92fd14146f41c63df29032684d66be40a34fad6b +Subproject commit 19e81610469455c4531e0a58574d58bca9ae4cc7 From ce8c5deab94cb539007dd118c1484cc246bb1807 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Dec 2017 20:48:59 +0900 Subject: [PATCH 029/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 19e8161046..3f3cb3b662 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 19e81610469455c4531e0a58574d58bca9ae4cc7 +Subproject commit 3f3cb3b66215a3b00802b5abf9b0a43e4a0f932e From 6dfc596319eaf5c118911dce9a348c02f56d46be Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Dec 2017 20:59:26 +0900 Subject: [PATCH 030/537] Post-merge fixes --- osu.Game.Tests/osu.Game.Tests.csproj | 1 + osu.Game/Rulesets/Edit/Layers/Selection/Handle.cs | 2 +- .../Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 4507d3d7f3..1c9b0503c0 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -23,6 +23,7 @@ + diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/Handle.cs b/osu.Game/Rulesets/Edit/Layers/Selection/Handle.cs index 2982a68b3b..b1ed5bc8b5 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/Handle.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/Handle.cs @@ -5,12 +5,12 @@ using System; 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; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; namespace osu.Game.Rulesets.Edit.Layers.Selection { diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs index 6f73d6b916..8f380b77c3 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs @@ -6,13 +6,13 @@ using System.Collections.Generic; 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.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; using OpenTK.Graphics; using osu.Framework.Configuration; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; namespace osu.Game.Rulesets.Edit.Layers.Selection { From bec1c80c7aaaedabdd43616b184acf07b9796994 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Dec 2017 21:06:41 +0900 Subject: [PATCH 031/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 3f3cb3b662..4b819607f3 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 3f3cb3b66215a3b00802b5abf9b0a43e4a0f932e +Subproject commit 4b819607f36f6a87472c7d654e02f2c8e83fac70 From 36e9232472ba3627875a3a32b3a99a524bcae388 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Jan 2018 00:42:48 +0900 Subject: [PATCH 032/537] Merge master into netstandard --- COMPILING.md | 36 ++++ NuGet.config | 6 - README.md | 7 +- osu.Desktop/Overlays/VersionManager.cs | 2 +- .../Drawable/DrawableCatchHitObject.cs | 1 + .../Scoring/CatchScoreProcessor.cs | 1 - .../Judgements/HitWindows.cs | 2 +- .../Judgements/HoldNoteTailJudgement.cs | 4 +- .../Judgements/HoldNoteTickJudgement.cs | 4 +- .../Judgements/ManiaJudgement.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaMod.cs | 2 + .../Objects/Drawables/DrawableHoldNote.cs | 1 + .../Objects/Drawables/DrawableHoldNoteTick.cs | 3 +- .../Objects/Drawables/DrawableNote.cs | 1 + .../Scoring/ManiaScoreProcessor.cs | 1 - .../Tests/TestCaseManiaPlayfield.cs | 2 +- .../Judgements/OsuJudgement.cs | 4 +- osu.Game.Rulesets.Osu/Mods/OsuMod.cs | 88 ++++++++- .../Objects/Drawables/DrawableHitCircle.cs | 22 ++- .../Objects/Drawables/DrawableOsuHitObject.cs | 13 +- .../Objects/Drawables/DrawableOsuJudgement.cs | 4 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 11 +- .../Objects/Drawables/DrawableSlider.cs | 81 ++++---- .../Objects/Drawables/DrawableSliderTick.cs | 1 + .../Objects/Drawables/DrawableSpinner.cs | 65 +++---- .../Objects/Drawables/Pieces/SliderBall.cs | 27 +-- .../Objects/Drawables/Pieces/SpinnerDisc.cs | 2 + .../Objects/ISliderProgress.cs | 10 + osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 30 ++- .../Replays/OsuAutoGenerator.cs | 2 +- .../Scoring/OsuPerformanceCalculator.cs | 8 +- .../Scoring/OsuScoreProcessor.cs | 12 +- .../Tests/TestCaseHitCircle.cs | 117 ++++++++++++ .../Tests/TestCaseHitCircleHidden.cs | 22 +++ .../Tests/TestCaseHitObjects.cs | 129 ------------- osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs | 152 +++++++++++++++ .../Tests/TestCaseSliderHidden.cs | 22 +++ .../Tests/TestCaseSpinner.cs | 88 +++++++++ .../Tests/TestCaseSpinnerHidden.cs | 22 +++ .../UI/OsuRulesetContainer.cs | 9 +- .../Audio/DrumSampleMapping.cs | 13 +- .../Judgements/TaikoDrumRollTickJudgement.cs | 4 +- .../Judgements/TaikoJudgement.cs | 2 +- .../Objects/Drawables/DrawableDrumRoll.cs | 1 + .../Objects/Drawables/DrawableDrumRollTick.cs | 1 + .../Objects/Drawables/DrawableHit.cs | 1 + .../Objects/Drawables/DrawableHitStrong.cs | 2 +- .../Objects/Drawables/DrawableSwell.cs | 1 + .../Drawables/DrawableTaikoHitObject.cs | 2 + osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 11 +- .../Replays/TaikoAutoGenerator.cs | 2 +- .../Scoring/TaikoScoreProcessor.cs | 1 - .../Tests/TestCaseInputDrum.cs | 44 +++++ .../Tests/TestCaseTaikoPlayfield.cs | 1 + .../UI/DrawableTaikoJudgement.cs | 3 +- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 4 +- .../Formats/LegacyStoryboardDecoderTest.cs | 4 +- .../Visual/TestCaseBeatmapScoresContainer.cs | 18 +- .../Visual/TestCaseGameplayMenuOverlay.cs | 8 +- osu.Game.Tests/Visual/TestCaseMods.cs | 30 ++- .../Visual/TestCaseNotificationOverlay.cs | 49 ++++- .../Visual/TestCasePlaySongSelect.cs | 13 +- osu.Game.Tests/Visual/TestCaseResults.cs | 19 +- osu.Game/Audio/SampleInfo.cs | 16 +- osu.Game/Beatmaps/BeatmapManager.cs | 37 ++-- .../ControlPoints/SampleControlPoint.cs | 2 +- .../Formats/LegacyStoryboardDecoder.cs | 2 +- osu.Game/Graphics/Backgrounds/Background.cs | 5 +- .../Containers/OsuFocusedOverlayContainer.cs | 44 +++++ .../Graphics/Textures/LargeTextureStore.cs | 18 ++ .../Online/API/Requests/GetScoresRequest.cs | 14 +- osu.Game/OsuGame.cs | 63 ++++--- osu.Game/OsuGameBase.cs | 4 + .../BeatmapSet/Scores/DrawableScore.cs | 5 +- .../BeatmapSet/Scores/DrawableTopScore.cs | 4 +- osu.Game/Overlays/ChatOverlay.cs | 2 - osu.Game/Overlays/Mods/ModButton.cs | 175 ++++++++++-------- osu.Game/Overlays/Mods/ModSection.cs | 25 ++- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 15 +- osu.Game/Overlays/MusicController.cs | 6 +- osu.Game/Overlays/NotificationOverlay.cs | 51 ++++- .../Notifications/NotificationSection.cs | 14 +- .../Notifications/ProgressNotification.cs | 2 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 4 +- osu.Game/Overlays/Profile/SupporterIcon.cs | 7 +- .../Settings/Sections/AudioSection.cs | 4 +- osu.Game/Overlays/SettingsOverlay.cs | 2 - osu.Game/Overlays/Toolbar/Toolbar.cs | 8 +- osu.Game/Overlays/UserProfileOverlay.cs | 10 - .../Rulesets/Judgements/DrawableJudgement.cs | 2 +- osu.Game/Rulesets/Judgements/Judgement.cs | 1 + .../Rulesets/Mods/IApplicableFailOverride.cs | 16 ++ osu.Game/Rulesets/Mods/IApplicableMod.cs | 13 ++ osu.Game/Rulesets/Mods/IApplicableToClock.cs | 4 +- .../Rulesets/Mods/IApplicableToDifficulty.cs | 4 +- .../Mods/IApplicableToDrawableHitObject.cs | 20 ++ .../Rulesets/Mods/IApplicableToHitObject.cs | 2 +- .../Mods/IApplicableToRulesetContainer.cs | 2 +- .../Mods/IApplicableToScoreProcessor.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 10 +- osu.Game/Rulesets/Mods/ModAutoplay.cs | 3 +- osu.Game/Rulesets/Mods/ModNoFail.cs | 6 +- .../Objects/Drawables/DrawableHitObject.cs | 15 +- osu.Game/Rulesets/RulesetStore.cs | 7 + .../Drawables => Scoring}/HitResult.cs | 2 +- osu.Game/Rulesets/Scoring/Score.cs | 2 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 1 - osu.Game/Rulesets/UI/ModIcon.cs | 2 +- osu.Game/Rulesets/UI/RulesetContainer.cs | 3 + .../Backgrounds/BackgroundScreenDefault.cs | 13 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Loader.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 14 +- osu.Game/Screens/Menu/Disclaimer.cs | 2 +- osu.Game/Screens/Menu/Intro.cs | 2 +- osu.Game/Screens/Menu/MainMenu.cs | 2 +- .../Screens/Multiplayer/ParticipantInfo.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 14 +- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 8 + .../Screens/Play/HUD/StandardHealthDisplay.cs | 2 +- osu.Game/Screens/Play/Player.cs | 13 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- osu.Game/Screens/Ranking/ResultsPageScore.cs | 17 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 31 +++- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 23 ++- .../Carousel/DrawableCarouselBeatmap.cs | 2 +- osu.Game/Screens/Select/EditSongSelect.cs | 6 +- .../Screens/Select/ImportFromStablePopup.cs | 2 +- .../Select/Leaderboards/Leaderboard.cs | 18 +- .../Select/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/Screens/Select/MatchSongSelect.cs | 6 +- .../Select/Options/BeatmapOptionsOverlay.cs | 2 + osu.Game/Screens/Select/PlaySongSelect.cs | 19 +- osu.Game/Screens/Select/SongSelect.cs | 27 ++- osu.Game/Screens/Tournament/Drawings.cs | 2 +- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 2 +- .../Tests/Visual/TestCasePerformancePoints.cs | 8 +- osu.Game/Tests/Visual/TestCasePlayer.cs | 17 +- osu.Game/Users/Country.cs | 43 +++-- osu.Game/Users/UserPanel.cs | 2 +- 141 files changed, 1555 insertions(+), 660 deletions(-) create mode 100644 COMPILING.md delete mode 100644 NuGet.config create mode 100644 osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs delete mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs create mode 100644 osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs create mode 100644 osu.Game/Graphics/Textures/LargeTextureStore.cs create mode 100644 osu.Game/Rulesets/Mods/IApplicableFailOverride.cs create mode 100644 osu.Game/Rulesets/Mods/IApplicableMod.cs create mode 100644 osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs rename osu.Game/Rulesets/{Objects/Drawables => Scoring}/HitResult.cs (91%) diff --git a/COMPILING.md b/COMPILING.md new file mode 100644 index 0000000000..bfcbf6bc2c --- /dev/null +++ b/COMPILING.md @@ -0,0 +1,36 @@ +# Linux +### 1. Requirements: +Mono >= 5.4.0 (>= 5.8.0 recommended) +Please check [here](http://www.mono-project.com/download/) for stable or [here](http://www.mono-project.com/download/alpha/) for an alpha release. +NuGet >= 4.4.0 +msbuild +git + +### 2. Cloning project +Clone the entire repository with submodules using +``` +git clone https://github.com/ppy/osu --recursive +``` +Then restore NuGet packages from the repository +``` +nuget restore +``` +### 3. Compiling +Simply run `msbuild` where `osu.sln` is located, this will create all binaries in `osu/osu.Desktop/bin/Debug`. +### 4. Optimizing +If you want additional performance you can change build type to Release with +``` +msbuild -p:Configuration=Release +``` +Additionally, mono provides an AOT utility which attempts to precompile binaries. You can utilize that by running +``` +mono --aot ./osu\!.exe +``` +### 5. Troubleshooting +You may run into trouble with NuGet versioning, as the one in packaging system is almost always out of date. Simply run +``` +nuget +sudo nuget update -self +``` +**Warning** NuGet creates few config files when it's run for the first time. +Do not run NuGet as root on the first run or you might run into very peculiar issues. diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index 95f993e510..0000000000 --- a/NuGet.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 856536d22d..47df86f57e 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,11 @@ This is still heavily under development and is not intended for end-user use. Th # Requirements -- A desktop platform which can compile .NET 4.5 (tested on macOS, linux and windows). We recommend using [Visual Studio Code](https://code.visualstudio.com/) (all platforms) or [Visual Studio Community Edition](https://www.visualstudio.com/) (windows only), both of which are free. -- Make sure you initialise and keep submodules up-to-date. +- A desktop platform that can compile .NET 4.6.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here. + +# Getting Started +- Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`) +- Build in your IDE of choice (recommended IDEs automatically restore nuget packages; if you are using an alternative make sure to `nuget restore`) # Contributing diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 0780ed9cce..4bf0b76814 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -118,7 +118,7 @@ namespace osu.Desktop.Overlays // only show a notification if we've previously saved a version to the config file (ie. not the first run). if (!string.IsNullOrEmpty(lastVersion)) - Scheduler.AddDelayed(() => notificationOverlay.Post(new UpdateCompleteNotification(version)), 5000); + notificationOverlay.Post(new UpdateCompleteNotification(version)); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index b90a06b94e..a617b65676 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Objects.Drawable { diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 3826fd1129..3b9eacde9c 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -5,7 +5,6 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs index d7bfa9caa1..ded1bc17af 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Judgements { diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs index a8d1b079eb..e369df6ae1 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Judgements { @@ -24,4 +24,4 @@ namespace osu.Game.Rulesets.Mania.Judgements } } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs index d326c6fc0a..4787a4977b 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Judgements { @@ -11,4 +11,4 @@ namespace osu.Game.Rulesets.Mania.Judgements protected override int NumericResultFor(HitResult result) => 20; } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs index 1f3b352da4..4762a98c40 100644 --- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Judgements { diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs index dfc9993bde..61e11f7610 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs @@ -105,6 +105,8 @@ namespace osu.Game.Rulesets.Mania.Mods public abstract class ManiaKeyMod : Mod { + // TODO: implement using the IApplicable interface. Haven't done so yet because KeyCount isn't even hooked up at the moment. + public override string ShortenedName => Name; public abstract int KeyCount { get; } public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 7b207ca229..41d817a746 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mania.Judgements; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects.Drawables { diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index 557fbf6ea8..8ed5d2b924 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects.Drawables { @@ -113,4 +114,4 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables UpdateJudgement(true); } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 537246509b..aabfcafa85 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -8,6 +8,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects.Drawables { diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 012137f555..140bab2225 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -6,7 +6,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs index 1932038411..b5890b289f 100644 --- a/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Timing; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Timing; using osu.Game.Tests.Visual; diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs index 28b6a04376..cd9c3888df 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs @@ -4,7 +4,7 @@ using OpenTK; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Judgements { @@ -34,4 +34,4 @@ namespace osu.Game.Rulesets.Osu.Judgements public ComboResult Combo; } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs index 71349285b3..7b1f80f439 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs @@ -11,8 +11,11 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; using OpenTK; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Osu.Mods { @@ -23,13 +26,86 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModEasy : ModEasy { - } - public class OsuModHidden : ModHidden + public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects { public override string Description => @"Play with no approach circles and fading notes for a slight score advantage."; public override double ScoreMultiplier => 1.06; + + private const double fade_in_duration_multiplier = 0.4; + private const double fade_out_duration_multiplier = 0.3; + + private float preEmpt => DrawableOsuHitObject.TIME_PREEMPT; + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var d in drawables.OfType()) + { + d.ApplyCustomUpdateState += ApplyHiddenState; + d.FadeInDuration = preEmpt * fade_in_duration_multiplier; + } + } + + protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableOsuHitObject d)) + return; + + var fadeOutStartTime = d.HitObject.StartTime - preEmpt + d.FadeInDuration; + var fadeOutDuration = preEmpt * fade_out_duration_multiplier; + + // new duration from completed fade in to end (before fading out) + var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime; + + switch (drawable) + { + case DrawableHitCircle circle: + // we don't want to see the approach circle + circle.ApproachCircle.Hide(); + + // fade out immediately after fade in. + using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true)) + circle.FadeOut(fadeOutDuration); + break; + case DrawableSlider slider: + using (slider.BeginAbsoluteSequence(fadeOutStartTime, true)) + { + slider.Body.FadeOut(longFadeDuration, Easing.Out); + + // delay a bit less to let the sliderball fade out peacefully instead of having a hard cut + using (slider.BeginDelayedSequence(longFadeDuration - fadeOutDuration, true)) + slider.Ball.FadeOut(fadeOutDuration); + } + + break; + case DrawableSpinner spinner: + // hide elements we don't care about. + spinner.Disc.Hide(); + spinner.Ticks.Hide(); + spinner.Background.Hide(); + + using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true)) + { + spinner.FadeOut(fadeOutDuration); + + // speed up the end sequence accordingly + switch (state) + { + case ArmedState.Hit: + spinner.ScaleTo(spinner.Scale * 1.2f, fadeOutDuration * 2, Easing.Out); + break; + case ArmedState.Miss: + spinner.ScaleTo(spinner.Scale * 0.8f, fadeOutDuration * 2, Easing.In); + break; + } + + spinner.Expire(); + } + + break; + } + } } public class OsuModHardRock : ModHardRock, IApplicableToHitObject @@ -51,11 +127,6 @@ namespace osu.Game.Rulesets.Osu.Mods slider.ControlPoints = newControlPoints; slider.Curve?.Calculate(); // Recalculate the slider curve } - - public void ApplyToHitObjects(RulesetContainer rulesetContainer) - { - - } } public class OsuModSuddenDeath : ModSuddenDeath @@ -96,7 +167,6 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModPerfect : ModPerfect { - } public class OsuModSpunOut : Mod diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index a973d2b580..72ca9b37a8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -6,8 +6,8 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -21,12 +21,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly NumberPiece number; private readonly GlowPiece glow; - public DrawableHitCircle(OsuHitObject h) : base(h) + public DrawableHitCircle(HitCircle h) : base(h) { Origin = Anchor.Centre; Position = HitObject.StackedPosition; - Scale = new Vector2(HitObject.Scale); + Scale = new Vector2(h.Scale); Children = new Drawable[] { @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, number = new NumberPiece { - Text = h is Spinner ? "S" : (HitObject.ComboIndex + 1).ToString(), + Text = (HitObject.ComboIndex + 1).ToString(), }, ring = new RingPiece(), flash = new FlashPiece(), @@ -88,25 +88,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdatePreemptState(); - ApproachCircle.FadeIn(Math.Min(TIME_FADEIN * 2, TIME_PREEMPT)); + ApproachCircle.FadeIn(Math.Min(FadeInDuration * 2, TIME_PREEMPT)); ApproachCircle.ScaleTo(1.1f, TIME_PREEMPT); } protected override void UpdateCurrentState(ArmedState state) { - double duration = ((HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime) - HitObject.StartTime; - - glow.Delay(duration).FadeOut(400); + glow.FadeOut(400); switch (state) { case ArmedState.Idle: - this.Delay(duration + TIME_PREEMPT).FadeOut(TIME_FADEOUT); + this.Delay(TIME_PREEMPT).FadeOut(500); + Expire(true); + + // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. + LifetimeEnd = HitObject.StartTime + HitObject.HitWindowFor(HitResult.Miss); break; case ArmedState.Miss: ApproachCircle.FadeOut(50); - this.FadeOut(TIME_FADEOUT / 5); + this.FadeOut(100); Expire(); break; case ArmedState.Hit: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 3e0c23a1ab..f5f0300ae1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -12,7 +12,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public const float TIME_PREEMPT = 600; public const float TIME_FADEIN = 400; - public const float TIME_FADEOUT = 500; + + /// + /// The number of milliseconds used to fade in. + /// + public virtual double FadeInDuration { get; set; } = TIME_FADEIN; + + public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - TIME_PREEMPT; protected DrawableOsuHitObject(OsuHitObject hitObject) : base(hitObject) @@ -37,10 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - protected virtual void UpdatePreemptState() - { - this.FadeIn(TIME_FADEIN); - } + protected virtual void UpdatePreemptState() => this.FadeIn(FadeInDuration); protected virtual void UpdateCurrentState(ArmedState state) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 7755a54e88..f16a41519e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -2,10 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; using OpenTK; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -24,4 +24,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.LoadComplete(); } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index a9b63ea642..477ced01c6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables; using OpenTK; using osu.Game.Graphics; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -24,19 +25,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.repeatPoint = repeatPoint; this.drawableSlider = drawableSlider; - AutoSizeAxes = Axes.Both; + Size = new Vector2(32 * repeatPoint.Scale); + Blending = BlendingMode.Additive; Origin = Anchor.Centre; - Scale = new Vector2(0.5f); Children = new Drawable[] { new SpriteIcon { - Icon = FontAwesome.fa_eercast, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(32), + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_eercast } }; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index befe84e3e9..f32a027f2e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -10,6 +10,7 @@ using System.Linq; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -17,23 +18,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { private readonly Slider slider; - private readonly DrawableHitCircle initialCircle; + public readonly DrawableHitCircle InitialCircle; private readonly List components = new List(); private readonly Container ticks; private readonly Container repeatPoints; - private readonly SliderBody body; - private readonly SliderBall ball; + public readonly SliderBody Body; + public readonly SliderBall Ball; - public DrawableSlider(Slider s) : base(s) + public DrawableSlider(Slider s) + : base(s) { slider = s; Children = new Drawable[] { - body = new SliderBody(s) + Body = new SliderBody(s) { AccentColour = AccentColour, Position = s.StackedPosition, @@ -41,16 +43,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, ticks = new Container(), repeatPoints = new Container(), - ball = new SliderBall(s) + Ball = new SliderBall(s) { Scale = new Vector2(s.Scale), AccentColour = AccentColour, AlwaysPresent = true, Alpha = 0 }, - initialCircle = new DrawableHitCircle(new HitCircle + InitialCircle = new DrawableHitCircle(new HitCircle { - //todo: avoid creating this temporary HitCircle. StartTime = s.StartTime, Position = s.StackedPosition, ComboIndex = s.ComboIndex, @@ -61,16 +62,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }) }; - components.Add(body); - components.Add(ball); + components.Add(Body); + components.Add(Ball); - AddNested(initialCircle); + AddNested(InitialCircle); var repeatDuration = s.Curve.Distance / s.Velocity; foreach (var tick in s.NestedHitObjects.OfType()) { var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration; - var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? TIME_FADEIN : TIME_FADEIN / 2); + var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? FadeInDuration : FadeInDuration / 2); var fadeOutTime = repeatStartTime + repeatDuration; var drawableTick = new DrawableSliderTick(tick) @@ -87,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var repeatPoint in s.NestedHitObjects.OfType()) { var repeatStartTime = s.StartTime + repeatPoint.RepeatIndex * repeatDuration; - var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? TIME_FADEIN : TIME_FADEIN / 2); + var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? FadeInDuration : FadeInDuration / 2); var fadeOutTime = repeatStartTime + repeatDuration; var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) @@ -105,11 +106,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private int currentRepeat; public bool Tracking; + public override double FadeInDuration + { + get { return base.FadeInDuration; } + set { InitialCircle.FadeInDuration = base.FadeInDuration = value; } + } + protected override void Update() { base.Update(); - Tracking = ball.Tracking; + Tracking = Ball.Tracking; double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); @@ -117,18 +124,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables progress = slider.ProgressAt(progress); if (repeat > currentRepeat) - { - if (repeat < slider.RepeatCount && ball.Tracking) - PlaySamples(); currentRepeat = repeat; - } //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. - if (!initialCircle.Judgements.Any(j => j.IsHit)) - initialCircle.Position = slider.Curve.PositionAt(progress); + if (!InitialCircle.Judgements.Any(j => j.IsHit)) + InitialCircle.Position = slider.Curve.PositionAt(progress); foreach (var c in components) c.UpdateProgress(progress, repeat); - foreach (var t in ticks.Children) t.Tracking = ball.Tracking; + foreach (var t in ticks.Children) t.Tracking = Ball.Tracking; } protected override void CheckForJudgements(bool userTriggered, double timeOffset) @@ -137,13 +140,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { var judgementsCount = ticks.Children.Count + repeatPoints.Children.Count + 1; var judgementsHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit)) + repeatPoints.Children.Count(t => t.Judgements.Any(j => j.IsHit)); - if (initialCircle.Judgements.Any(j => j.IsHit)) + if (InitialCircle.Judgements.Any(j => j.IsHit)) judgementsHit++; var hitFraction = (double)judgementsHit / judgementsCount; - if (hitFraction == 1 && initialCircle.Judgements.Any(j => j.Result == HitResult.Great)) + if (hitFraction == 1 && InitialCircle.Judgements.Any(j => j.Result == HitResult.Great)) AddJudgement(new OsuJudgement { Result = HitResult.Great }); - else if (hitFraction >= 0.5 && initialCircle.Judgements.Any(j => j.Result >= HitResult.Good)) + else if (hitFraction >= 0.5 && InitialCircle.Judgements.Any(j => j.Result >= HitResult.Good)) AddJudgement(new OsuJudgement { Result = HitResult.Good }); else if (hitFraction > 0) AddJudgement(new OsuJudgement { Result = HitResult.Meh }); @@ -154,26 +157,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateCurrentState(ArmedState state) { - ball.FadeIn(); + Ball.FadeIn(); + Ball.ScaleTo(HitObject.Scale); using (BeginDelayedSequence(slider.Duration, true)) { - body.FadeOut(160); - ball.FadeOut(160); + const float fade_out_time = 450; - this.FadeOut(800) - .Expire(); + // intentionally pile on an extra FadeOut to make it happen much faster. + Ball.FadeOut(fade_out_time / 4, Easing.Out); + + switch (state) + { + case ArmedState.Hit: + Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out); + break; + } + + this.FadeOut(fade_out_time, Easing.OutQuint).Expire(); } } - public Drawable ProxiedLayer => initialCircle.ApproachCircle; + public Drawable ProxiedLayer => InitialCircle.ApproachCircle; - public override Vector2 SelectionPoint => ToScreenSpace(body.Position); - public override Quad SelectionQuad => body.PathDrawQuad; - } - - internal interface ISliderProgress - { - void UpdateProgress(double progress, int repeat); + public override Vector2 SelectionPoint => ToScreenSpace(Body.Position); + public override Quad SelectionQuad => Body.PathDrawQuad; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 7199691ae6..bce7ef6141 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -8,6 +8,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 054a2067ec..bbe6b3a0a0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -13,20 +13,21 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Screens.Ranking; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSpinner : DrawableOsuHitObject { - private readonly Spinner spinner; + protected readonly Spinner Spinner; - private readonly SpinnerDisc disc; - private readonly SpinnerTicks ticks; + public readonly SpinnerDisc Disc; + public readonly SpinnerTicks Ticks; private readonly SpinnerSpmCounter spmCounter; private readonly Container mainContainer; - private readonly SpinnerBackground background; + public readonly SpinnerBackground Background; private readonly Container circleContainer; private readonly CirclePiece circle; private readonly GlowPiece glow; @@ -49,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // we are slightly bigger than our parent, to clip the top and bottom of the circle Height = 1.3f; - spinner = s; + Spinner = s; Children = new Drawable[] { @@ -84,20 +85,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Y, Children = new Drawable[] { - background = new SpinnerBackground + Background = new SpinnerBackground { Alpha = 0.6f, Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - disc = new SpinnerDisc(spinner) + Disc = new SpinnerDisc(Spinner) { Scale = Vector2.Zero, Anchor = Anchor.Centre, Origin = Anchor.Centre, }, circleContainer.CreateProxy(), - ticks = new SpinnerTicks + Ticks = new SpinnerTicks { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -114,28 +115,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; } - public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1); + public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1); protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (Time.Current < HitObject.StartTime) return; - if (Progress >= 1 && !disc.Complete) + if (Progress >= 1 && !Disc.Complete) { - disc.Complete = true; + Disc.Complete = true; const float duration = 200; - disc.FadeAccent(completeColour, duration); + Disc.FadeAccent(completeColour, duration); - background.FadeAccent(completeColour, duration); - background.FadeOut(duration); + Background.FadeAccent(completeColour, duration); + Background.FadeOut(duration); circle.FadeColour(completeColour, duration); glow.FadeColour(completeColour, duration); } - if (!userTriggered && Time.Current >= spinner.EndTime) + if (!userTriggered && Time.Current >= Spinner.EndTime) { if (Progress >= 1) AddJudgement(new OsuJudgement { Result = HitResult.Great }); @@ -143,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AddJudgement(new OsuJudgement { Result = HitResult.Good }); else if (Progress > .75) AddJudgement(new OsuJudgement { Result = HitResult.Meh }); - else if (Time.Current >= spinner.EndTime) + else if (Time.Current >= Spinner.EndTime) AddJudgement(new OsuJudgement { Result = HitResult.Miss }); } } @@ -153,20 +154,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { normalColour = baseColour; - background.AccentColour = normalColour; + Background.AccentColour = normalColour; completeColour = colours.YellowLight.Opacity(0.75f); - disc.AccentColour = fillColour; + Disc.AccentColour = fillColour; circle.Colour = colours.BlueDark; glow.Colour = colours.BlueDark; } protected override void Update() { - disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton); - if (!spmCounter.IsPresent && disc.Tracking) - spmCounter.FadeIn(TIME_FADEIN); + Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton); + if (!spmCounter.IsPresent && Disc.Tracking) + spmCounter.FadeIn(FadeInDuration); base.Update(); } @@ -175,36 +176,36 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateAfterChildren(); - circle.Rotation = disc.Rotation; - ticks.Rotation = disc.Rotation; - spmCounter.SetRotation(disc.RotationAbsolute); + circle.Rotation = Disc.Rotation; + Ticks.Rotation = Disc.Rotation; + spmCounter.SetRotation(Disc.RotationAbsolute); - float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; - disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint); + float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; + Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint); - symbol.RotateTo(disc.Rotation / 2, 500, Easing.OutQuint); + symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint); } protected override void UpdatePreemptState() { base.UpdatePreemptState(); - circleContainer.ScaleTo(spinner.Scale * 0.3f); - circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint); + circleContainer.ScaleTo(Spinner.Scale * 0.3f); + circleContainer.ScaleTo(Spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint); - disc.RotateTo(-720); + Disc.RotateTo(-720); symbol.RotateTo(-720); mainContainer .ScaleTo(0) - .ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint) + .ScaleTo(Spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint) .Then() .ScaleTo(1, 500, Easing.OutQuint); } protected override void UpdateCurrentState(ArmedState state) { - var sequence = this.Delay(spinner.Duration).FadeOut(160); + var sequence = this.Delay(Spinner.Duration).FadeOut(160); switch (state) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 2068ad9205..46b4353f9a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } private readonly Slider slider; - private readonly Box follow; + public readonly Box FollowCircle; private readonly Box ball; public SliderBall(Slider slider) @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Children = new Drawable[] { - follow = new Box + FollowCircle = new Box { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -101,11 +101,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces // If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos); - public override void ClearTransforms(bool propagateChildren = false, string targetMember = null) + public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { // Consider the case of rewinding - children's transforms are handled internally, so propagating down // any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point. - base.ClearTransforms(false, targetMember); + base.ClearTransformsAfter(time, false, targetMember); } private bool tracking; @@ -118,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; tracking = value; - follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint); - follow.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint); + FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint); + FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint); } } @@ -129,11 +129,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { base.Update(); - // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. - Tracking = canCurrentlyTrack - && lastState != null - && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) - && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + if (Time.Current < slider.EndTime) + { + // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. + Tracking = canCurrentlyTrack + && lastState != null + && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) + && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + } } public void UpdateProgress(double progress, int repeat) @@ -141,4 +144,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Position = slider.Curve.PositionAt(progress); } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index ca75a61738..9f54ce3fa3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private const float idle_alpha = 0.2f; private const float tracking_alpha = 0.4f; + public override bool IsPresent => true; // handle input when hidden + public SpinnerDisc(Spinner s) { spinner = s; diff --git a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs new file mode 100644 index 0000000000..cb0d177a60 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Objects +{ + public interface ISliderProgress + { + void UpdateProgress(double progress, int repeat); + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 7532387aa2..a3a6527b31 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -7,7 +7,7 @@ using OpenTK; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 5f9f11c783..ec51a10345 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -151,28 +151,22 @@ namespace osu.Game.Rulesets.Osu.Objects private void createRepeatPoints() { - var length = Curve.Distance; - var repeatPointDistance = Math.Min(Distance, length); - var repeatDuration = length / Velocity; + var repeatDuration = Distance / Velocity; for (var repeat = 1; repeat < RepeatCount; repeat++) { - for (var d = repeatPointDistance; d <= length; d += repeatPointDistance) - { - var repeatStartTime = StartTime + repeat * repeatDuration; - var distanceProgress = d / length; + var repeatStartTime = StartTime + repeat * repeatDuration; - AddNested(new RepeatPoint - { - RepeatIndex = repeat, - StartTime = repeatStartTime, - Position = Curve.PositionAt(distanceProgress), - StackHeight = StackHeight, - Scale = Scale, - ComboColour = ComboColour, - Samples = new List(RepeatSamples[repeat]) - }); - } + AddNested(new RepeatPoint + { + RepeatIndex = repeat, + StartTime = repeatStartTime, + Position = Curve.PositionAt(repeat % 2), + StackHeight = StackHeight, + Scale = Scale, + ComboColour = ComboColour, + Samples = new List(RepeatSamples[repeat]) + }); } } } diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index f82c6ce3b2..ba774e887f 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -9,9 +9,9 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using System; using System.Diagnostics; using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Replays { diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs index 2cf321da50..38c602bc42 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs @@ -41,10 +41,10 @@ namespace osu.Game.Rulesets.Osu.Scoring mods = Score.Mods; accuracy = Score.Accuracy; scoreMaxCombo = Score.MaxCombo; - count300 = Convert.ToInt32(Score.Statistics["300"]); - count100 = Convert.ToInt32(Score.Statistics["100"]); - count50 = Convert.ToInt32(Score.Statistics["50"]); - countMiss = Convert.ToInt32(Score.Statistics["x"]); + count300 = Convert.ToInt32(Score.Statistics[HitResult.Great]); + count100 = Convert.ToInt32(Score.Statistics[HitResult.Good]); + count50 = Convert.ToInt32(Score.Statistics[HitResult.Meh]); + countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]); // Don't count scores made with supposedly unranked mods if (mods.Any(m => !m.Ranked)) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index ad9737af52..7520e1801c 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -33,8 +32,7 @@ namespace osu.Game.Rulesets.Osu.Scoring foreach (var obj in beatmap.HitObjects) { - var slider = obj as Slider; - if (slider != null) + if (obj is Slider slider) { // Head AddJudgement(new OsuJudgement { Result = HitResult.Great }); @@ -64,10 +62,10 @@ namespace osu.Game.Rulesets.Osu.Scoring { base.PopulateScore(score); - score.Statistics[@"300"] = scoreResultCounts.GetOrDefault(HitResult.Great); - score.Statistics[@"100"] = scoreResultCounts.GetOrDefault(HitResult.Good); - score.Statistics[@"50"] = scoreResultCounts.GetOrDefault(HitResult.Meh); - score.Statistics[@"x"] = scoreResultCounts.GetOrDefault(HitResult.Miss); + score.Statistics[HitResult.Great] = scoreResultCounts.GetOrDefault(HitResult.Great); + score.Statistics[HitResult.Good] = scoreResultCounts.GetOrDefault(HitResult.Good); + score.Statistics[HitResult.Meh] = scoreResultCounts.GetOrDefault(HitResult.Meh); + score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss); } protected override void OnNewJudgement(Judgement judgement) diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs new file mode 100644 index 0000000000..f307ff7c70 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs @@ -0,0 +1,117 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Visual; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Rulesets.Osu.Judgements; +using System.Collections.Generic; +using System; +using osu.Game.Rulesets.Mods; +using System.Linq; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseHitCircle : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DrawableHitCircle) + }; + + private readonly Container content; + protected override Container Content => content; + + private int depthIndex; + protected readonly List Mods = new List(); + + public TestCaseHitCircle() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Miss Big Single", () => testSingle(2)); + AddStep("Miss Medium Single", () => testSingle(5)); + AddStep("Miss Small Single", () => testSingle(7)); + AddStep("Hit Big Single", () => testSingle(2, true)); + AddStep("Hit Medium Single", () => testSingle(5, true)); + AddStep("Hit Small Single", () => testSingle(7, true)); + AddStep("Miss Big Stream", () => testStream(2)); + AddStep("Miss Medium Stream", () => testStream(5)); + AddStep("Miss Small Stream", () => testStream(7)); + AddStep("Hit Big Stream", () => testStream(2, true)); + AddStep("Hit Medium Stream", () => testStream(5, true)); + AddStep("Hit Small Stream", () => testStream(7, true)); + } + + private void testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null) + { + positionOffset = positionOffset ?? Vector2.Zero; + + var circle = new HitCircle + { + StartTime = Time.Current + 1000 + timeOffset, + Position = positionOffset.Value, + ComboColour = Color4.LightSeaGreen + }; + + circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); + + var drawable = new TestDrawableHitCircle(circle, auto) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + + foreach (var mod in Mods.OfType()) + mod.ApplyToDrawableHitObjects(new[] { drawable }); + + Add(drawable); + } + + private void testStream(float circleSize, bool auto = false) + { + Vector2 pos = new Vector2(-250, 0); + + for (int i = 0; i <= 1000; i += 100) + { + testSingle(circleSize, auto, i, pos); + pos.X += 50; + } + } + + private class TestDrawableHitCircle : DrawableHitCircle + { + private readonly bool auto; + + public TestDrawableHitCircle(HitCircle h, bool auto) : base(h) + { + this.auto = auto; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (auto && !userTriggered && timeOffset > 0) + { + // force success + AddJudgement(new OsuJudgement + { + Result = HitResult.Great + }); + State.Value = ArmedState.Hit; + } + else + base.CheckForJudgements(userTriggered, timeOffset); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs new file mode 100644 index 0000000000..7cc0c343a2 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseHitCircleHidden : TestCaseHitCircle + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); + + public TestCaseHitCircleHidden() + { + Mods.Add(new OsuModHidden()); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs deleted file mode 100644 index c4932d7803..0000000000 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Timing; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Visual; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - [Ignore("getting CI working")] - public class TestCaseHitObjects : OsuTestCase - { - private FramedClock framedClock; - - private bool auto; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - var rateAdjustClock = new StopwatchClock(true); - framedClock = new FramedClock(rateAdjustClock); - - AddStep(@"circles", () => loadHitobjects(HitObjectType.Circle)); - AddStep(@"slider", () => loadHitobjects(HitObjectType.Slider)); - AddStep(@"spinner", () => loadHitobjects(HitObjectType.Spinner)); - - AddToggleStep("Auto", state => { auto = state; loadHitobjects(mode); }); - AddSliderStep("Playback speed", 0.0, 2.0, 0.5, v => rateAdjustClock.Rate = v); - - framedClock.ProcessFrame(); - - var clockAdjustContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Clock = framedClock, - Children = new[] - { - playfieldContainer = new OsuInputManager(rulesets.GetRuleset(0)) { RelativeSizeAxes = Axes.Both }, - approachContainer = new Container { RelativeSizeAxes = Axes.Both } - } - }; - - Add(clockAdjustContainer); - } - - private HitObjectType mode = HitObjectType.Slider; - - private Container playfieldContainer; - private Container approachContainer; - - private void loadHitobjects(HitObjectType mode) - { - this.mode = mode; - - switch (mode) - { - case HitObjectType.Circle: - const int count = 10; - - for (int i = 0; i < count; i++) - { - var h = new HitCircle - { - StartTime = framedClock.CurrentTime + 600 + i * 80, - Position = new Vector2((i - count / 2) * 14), - }; - - add(new DrawableHitCircle(h)); - } - break; - case HitObjectType.Slider: - add(new DrawableSlider(new Slider - { - StartTime = framedClock.CurrentTime + 600, - ControlPoints = new List - { - new Vector2(-200, 0), - new Vector2(400, 0), - }, - Distance = 400, - Position = new Vector2(-200, 0), - Velocity = 1, - TickDistance = 100, - })); - break; - case HitObjectType.Spinner: - add(new DrawableSpinner(new Spinner - { - StartTime = framedClock.CurrentTime + 600, - EndTime = framedClock.CurrentTime + 1600, - Position = new Vector2(0, 0), - })); - break; - } - } - - private int depth; - - private void add(DrawableOsuHitObject h) - { - h.Anchor = Anchor.Centre; - h.Depth = depth++; - - if (auto) - h.State.Value = ArmedState.Hit; - - playfieldContainer.Add(h); - var proxyable = h as IDrawableHitObjectWithProxiedApproach; - if (proxyable != null) - approachContainer.Add(proxyable.ProxiedLayer.CreateProxy()); - } - - private enum HitObjectType - { - Circle, - Slider, - Spinner - } - } -} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs new file mode 100644 index 0000000000..1238572484 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -0,0 +1,152 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Visual; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Rulesets.Mods; +using System.Linq; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseSlider : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(SliderBall), + typeof(SliderBody), + typeof(DrawableSlider), + typeof(DrawableRepeatPoint), + typeof(DrawableOsuHitObject) + }; + + private readonly Container content; + protected override Container Content => content; + + private int depthIndex; + protected readonly List Mods = new List(); + + public TestCaseSlider() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Big Single", () => testSimpleBig()); + AddStep("Medium Single", () => testSimpleMedium()); + AddStep("Small Single", () => testSimpleSmall()); + AddStep("Big 1 Repeat", () => testSimpleBig(1)); + AddStep("Medium 1 Repeat", () => testSimpleMedium(1)); + AddStep("Small 1 Repeat", () => testSimpleSmall(1)); + AddStep("Big 2 Repeats", () => testSimpleBig(2)); + AddStep("Medium 2 Repeats", () => testSimpleMedium(2)); + AddStep("Small 2 Repeats", () => testSimpleSmall(2)); + + AddStep("Slow Slider", testSlowSpeed); // slow long sliders take ages already so no repeat steps + AddStep("Slow Short Slider", () => testShortSlowSpeed()); + AddStep("Slow Short Slider 1 Repeats", () => testShortSlowSpeed(1)); + AddStep("Slow Short Slider 2 Repeats", () => testShortSlowSpeed(2)); + + AddStep("Fast Slider", () => testHighSpeed()); + AddStep("Fast Slider 1 Repeat", () => testHighSpeed(1)); + AddStep("Fast Slider 2 Repeats", () => testHighSpeed(2)); + AddStep("Fast Short Slider", () => testShortHighSpeed()); + AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1)); + AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2)); + + AddStep("Perfect Curve", testCurve); + // TODO more curve types? + } + + private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); + + private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats); + + private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats); + + private void testSlowSpeed() => createSlider(speedMultiplier: 0.5); + + private void testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5); + + private void testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15); + + private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); + + private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2) + { + repeats++; // The first run through the slider is considered a repeat + + var repeatSamples = new List>(); + if (repeats > 1) + { + for (int i = 0; i < repeats; i++) + repeatSamples.Add(new List()); + } + + var slider = new Slider + { + StartTime = Time.Current + 1000, + Position = new Vector2(-(distance / 2), 0), + ComboColour = Color4.LightSeaGreen, + ControlPoints = new List + { + new Vector2(-(distance / 2), 0), + new Vector2(distance / 2, 0), + }, + Distance = distance, + RepeatCount = repeats, + RepeatSamples = repeatSamples + }; + + addSlider(slider, circleSize, speedMultiplier); + } + + private void testCurve() + { + var slider = new Slider + { + StartTime = Time.Current + 1000, + Position = new Vector2(-200, 0), + ComboColour = Color4.LightSeaGreen, + ControlPoints = new List + { + new Vector2(-200, 0), + new Vector2(0, 200), + new Vector2(200, 0) + }, + Distance = 600 + }; + + addSlider(slider, 2, 3); + } + + private void addSlider(Slider slider, float circleSize, double speedMultiplier) + { + var cpi = new ControlPointInfo(); + cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier }); + + slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize }); + + var drawable = new DrawableSlider(slider) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + + foreach (var mod in Mods.OfType()) + mod.ApplyToDrawableHitObjects(new[] { drawable }); + + Add(drawable); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs new file mode 100644 index 0000000000..016909ad73 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseSliderHidden : TestCaseSlider + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); + + public TestCaseSliderHidden() + { + Mods.Add(new OsuModHidden()); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs new file mode 100644 index 0000000000..752574018c --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs @@ -0,0 +1,88 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseSpinner : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] +{ + typeof(SpinnerDisc), + typeof(DrawableSpinner), + typeof(DrawableOsuHitObject) + }; + + private readonly Container content; + protected override Container Content => content; + + private int depthIndex; + protected readonly List Mods = new List(); + + public TestCaseSpinner() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Miss Big", () => testSingle(2)); + AddStep("Miss Medium", () => testSingle(5)); + AddStep("Miss Small", () => testSingle(7)); + AddStep("Hit Big", () => testSingle(2, true)); + AddStep("Hit Medium", () => testSingle(5, true)); + AddStep("Hit Small", () => testSingle(7, true)); + } + + private void testSingle(float circleSize, bool auto = false) + { + var spinner = new Spinner { StartTime = Time.Current + 1000, EndTime = Time.Current + 4000 }; + + spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); + + var drawable = new TestDrawableSpinner(spinner, auto) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + + foreach (var mod in Mods.OfType()) + mod.ApplyToDrawableHitObjects(new[] { drawable }); + + Add(drawable); + } + + private class TestDrawableSpinner : DrawableSpinner + { + private bool auto; + + public TestDrawableSpinner(Spinner s, bool auto) : base(s) + { + this.auto = auto; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1) + { + // force completion only once to not break human interaction + Disc.RotationAbsolute = Spinner.SpinsRequired * 360; + auto = false; + } + + base.CheckForJudgements(userTriggered, timeOffset); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs new file mode 100644 index 0000000000..9ef94b308f --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseSpinnerHidden : TestCaseSpinner + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); + + public TestCaseSpinnerHidden() + { + Mods.Add(new OsuModHidden()); + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 7c9cbd63fc..f37b87e533 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -35,16 +35,13 @@ namespace osu.Game.Rulesets.Osu.UI protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) { - var circle = h as HitCircle; - if (circle != null) + if (h is HitCircle circle) return new DrawableHitCircle(circle); - var slider = h as Slider; - if (slider != null) + if (h is Slider slider) return new DrawableSlider(slider); - var spinner = h as Spinner; - if (spinner != null) + if (h is Spinner spinner) return new DrawableSpinner(spinner); return null; } diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs index 9d0037b97a..982b339d3a 100644 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Game.Audio; @@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Audio public class DrumSampleMapping { private readonly ControlPointInfo controlPoints; - private readonly Dictionary mappings = new Dictionary(); + private readonly Dictionary mappings = new Dictionary(); public DrumSampleMapping(ControlPointInfo controlPoints, AudioManager audio) { @@ -26,17 +25,17 @@ namespace osu.Game.Rulesets.Taiko.Audio else samplePoints = controlPoints.SamplePoints; - foreach (var s in samplePoints.Distinct()) + foreach (var s in samplePoints) { - mappings[s] = new DrumSample + mappings[s.Time] = new DrumSample { - Centre = s.GetSampleInfo().GetChannel(audio.Sample), - Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample) + Centre = s.GetSampleInfo().GetChannel(audio.Sample, "Taiko"), + Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample, "Taiko") }; } } - public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time)]; + public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time]; public class DrumSample { diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs index c9daef8c99..ce5be8d148 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Judgements { @@ -20,4 +20,4 @@ namespace osu.Game.Rulesets.Taiko.Judgements } } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs index 3cd134f3f7..70cdd1fe0e 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Judgements { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 75e988ced6..f5bafefd0b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index e662f61bbe..a741e35963 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index fd35f0eaec..0c10c7142e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index cda82afe0e..249bb41d91 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -3,7 +3,7 @@ using System; using System.Linq; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 5ca33aaea2..26e6585fb9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -14,6 +14,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 92da3fe09e..cc7dd2fa0f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -41,6 +41,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Normal and clap samples are handled by the drum protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP); + protected override string SampleNamespace => "Taiko"; + protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); public abstract bool OnPressed(TaikoAction action); diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 5a566fd091..a39d627cc4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -3,9 +3,6 @@ using osu.Game.Rulesets.Objects.Types; using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -75,13 +72,7 @@ namespace osu.Game.Rulesets.Taiko.Objects FirstTick = first, TickSpacing = tickSpacing, StartTime = t, - IsStrong = IsStrong, - Samples = new List(Samples.Select(s => new SampleInfo - { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) + IsStrong = IsStrong }); first = false; diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index df1a19267f..c43899ebf1 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Replays { foreach (var tick in drumRoll.NestedHitObjects.OfType()) { - Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2)); + Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2)); hitButton = !hitButton; } } diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index df9ce5e2eb..3848e36fc9 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -4,7 +4,6 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs new file mode 100644 index 0000000000..172c6e9d84 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Audio; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [Ignore("getting CI working")] + public class TestCaseInputDrum : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(InputDrum), + typeof(DrumSampleMapping), + typeof(SampleInfo), + typeof(SampleControlPoint) + }; + + public TestCaseInputDrum() + { + Add(new TaikoInputManager(new RulesetInfo { ID = 1 }) + { + RelativeSizeAxes = Axes.Both, + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200), + Child = new InputDrum(new ControlPointInfo()) + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs index b1e6e9c4ce..1f13864c2a 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Taiko.UI; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Visual; using OpenTK; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Tests { diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index 41b66c286b..0b67613ec3 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Framework.Graphics; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.UI { @@ -49,4 +50,4 @@ namespace osu.Game.Rulesets.Taiko.UI base.LoadComplete(); } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index bf1274256b..9b2ea095d2 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -152,14 +152,14 @@ namespace osu.Game.Rulesets.Taiko.UI target = centreHit; back = centre; - drumSample.Centre.Play(); + drumSample.Centre?.Play(); } else if (action == RimAction) { target = rimHit; back = rim; - drumSample.Rim.Play(); + drumSample.Rim?.Play(); } if (target != null) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 839932c640..a0904ee446 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition); Assert.IsTrue(sprite.IsDrawable); Assert.AreEqual(Anchor.Centre, sprite.Origin); - Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path); + Assert.AreEqual(Path.Combine("SB", "lyric", "ja-21.png"), sprite.Path); var animation = background.Elements.ElementAt(12) as StoryboardAnimation; Assert.NotNull(animation); @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(animation.IsDrawable); Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType); Assert.AreEqual(Anchor.Centre, animation.Origin); - Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path); + Assert.AreEqual(Path.Combine("SB", "red jitter", "red_0000.jpg"), animation.Path); Assert.AreEqual(78993, animation.StartTime); } } diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs index cef8797f20..ad15833569 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs @@ -160,9 +160,9 @@ namespace osu.Game.Tests.Visual }; foreach(var s in scores) { - s.Statistics.Add("300", RNG.Next(2000)); - s.Statistics.Add("100", RNG.Next(2000)); - s.Statistics.Add("50", RNG.Next(2000)); + s.Statistics.Add(HitResult.Great, RNG.Next(2000)); + s.Statistics.Add(HitResult.Good, RNG.Next(2000)); + s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); } anotherScores = new[] @@ -272,9 +272,9 @@ namespace osu.Game.Tests.Visual }; foreach (var s in anotherScores) { - s.Statistics.Add("300", RNG.Next(2000)); - s.Statistics.Add("100", RNG.Next(2000)); - s.Statistics.Add("50", RNG.Next(2000)); + s.Statistics.Add(HitResult.Great, RNG.Next(2000)); + s.Statistics.Add(HitResult.Good, RNG.Next(2000)); + s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); } topScore = new OnlineScore @@ -299,9 +299,9 @@ namespace osu.Game.Tests.Visual TotalScore = 987654321, Accuracy = 0.8487, }; - topScore.Statistics.Add("300", RNG.Next(2000)); - topScore.Statistics.Add("100", RNG.Next(2000)); - topScore.Statistics.Add("50", RNG.Next(2000)); + topScore.Statistics.Add(HitResult.Great, RNG.Next(2000)); + topScore.Statistics.Add(HitResult.Good, RNG.Next(2000)); + topScore.Statistics.Add(HitResult.Meh, RNG.Next(2000)); } [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs index bd5772d3bb..87552c3f17 100644 --- a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual { AddStep("Show overlay", () => failOverlay.Show()); - AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnHover(null)); + AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnMouseMove(null)); AddStep("Hide overlay", () => failOverlay.Hide()); AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected)); @@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual var secondButton = pauseOverlay.Buttons.Skip(1).First(); AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); - AddStep("Hover second button", () => secondButton.TriggerOnHover(null)); + AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected); AddAssert("Second button selected", () => secondButton.Selected); @@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual var secondButton = pauseOverlay.Buttons.Skip(1).First(); - AddStep("Hover second button", () => secondButton.TriggerOnHover(null)); + AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); AddAssert("Second button not selected", () => !secondButton.Selected); AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); @@ -195,7 +195,7 @@ namespace osu.Game.Tests.Visual var secondButton = pauseOverlay.Buttons.Skip(1).First(); - AddStep("Hover second button", () => secondButton.TriggerOnHover(null)); + AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null)); AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs index e535da3fcc..c78d3b1f9f 100644 --- a/osu.Game.Tests/Visual/TestCaseMods.cs +++ b/osu.Game.Tests/Visual/TestCaseMods.cs @@ -15,6 +15,8 @@ using System.Collections.Generic; using osu.Game.Rulesets.Osu; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Mania.Mods; using OpenTK.Graphics; namespace osu.Game.Tests.Visual @@ -68,6 +70,9 @@ namespace osu.Game.Tests.Visual case OsuRuleset or: testOsuMods(or); break; + case ManiaRuleset mr: + testManiaMods(mr); + break; } } } @@ -80,16 +85,27 @@ namespace osu.Game.Tests.Visual var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail); var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden); + var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); + var autoPilotMod = assistMods.FirstOrDefault(m => m is OsuModAutopilot); + var easy = easierMods.FirstOrDefault(m => m is OsuModEasy); + var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock); + testSingleMod(noFailMod); testMultiMod(doubleTimeMod); - testIncompatibleMods(noFailMod, autoPilotMod); + testIncompatibleMods(easy, hardRock); testDeselectAll(easierMods.Where(m => !(m is MultiMod))); testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour); testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour); - testMultiplierTextUnranked(autoPilotMod); + + testUnimplmentedMod(autoPilotMod); + } + + private void testManiaMods(ManiaRuleset ruleset) + { + testMultiplierTextUnranked(ruleset.GetModsFor(ModType.Special).First(m => m is ManiaModRandom)); } private void testSingleMod(Mod mod) @@ -124,6 +140,12 @@ namespace osu.Game.Tests.Visual checkNotSelected(mod); } + private void testUnimplmentedMod(Mod mod) + { + selectNext(mod); + checkNotSelected(mod); + } + private void testIncompatibleMods(Mod modA, Mod modB) { selectNext(modA); @@ -169,9 +191,9 @@ namespace osu.Game.Tests.Visual AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix)); } - private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext()); + private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1)); - private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectPrevious()); + private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1)); private void checkSelected(Mod mod) { diff --git a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs index a83cead213..46deca073f 100644 --- a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using osu.Game.Overlays; @@ -19,11 +20,12 @@ namespace osu.Game.Tests.Visual public override IReadOnlyList RequiredTypes => new[] { - typeof(Notification), + typeof(NotificationSection), + typeof(SimpleNotification), typeof(ProgressNotification), typeof(ProgressCompletionNotification), - typeof(SimpleNotification), typeof(IHasCompletionTarget), + typeof(Notification) }; public TestCaseNotificationOverlay() @@ -40,17 +42,44 @@ namespace osu.Game.Tests.Visual Content.Add(displayedCount); + void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state); + void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected); + manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; }; - AddStep(@"toggle", manager.ToggleVisibility); + + setState(Visibility.Visible); AddStep(@"simple #1", sendHelloNotification); AddStep(@"simple #2", sendAmazingNotification); AddStep(@"progress #1", sendUploadProgress); AddStep(@"progress #2", sendDownloadProgress); - AddStep(@"barrage", () => sendBarrage()); + + checkProgressingCount(2); + + setState(Visibility.Hidden); + + AddRepeatStep(@"add many simple", sendManyNotifications, 3); + AddWaitStep(5); + + checkProgressingCount(0); + + AddStep(@"progress #3", sendUploadProgress); + + checkProgressingCount(1); + + AddAssert("Displayed count is 33", () => manager.UnreadCount.Value == 33); + + AddWaitStep(10); + + checkProgressingCount(0); + + + setState(Visibility.Visible); + + //AddStep(@"barrage", () => sendBarrage()); } - private void sendBarrage(int remaining = 100) + private void sendBarrage(int remaining = 10) { switch (RNG.Next(0, 4)) { @@ -80,7 +109,7 @@ namespace osu.Game.Tests.Visual if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) { - var p = progressingNotifications.FirstOrDefault(n => n.IsAlive && n.State == ProgressNotificationState.Queued); + var p = progressingNotifications.FirstOrDefault(n => n.State == ProgressNotificationState.Queued); if (p != null) p.State = ProgressNotificationState.Active; } @@ -88,7 +117,7 @@ namespace osu.Game.Tests.Visual foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active)) { if (n.Progress < 1) - n.Progress += (float)(Time.Elapsed / 2000) * RNG.NextSingle(); + n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle(); else n.State = ProgressNotificationState.Completed; } @@ -125,5 +154,11 @@ namespace osu.Game.Tests.Visual { manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" }); } + + private void sendManyNotifications() + { + for (int i = 0; i < 10; i++) + manager.Post(new SimpleNotification { Text = @"Spam incoming!!" }); + } } } diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index 18e40db064..3be4a18ec5 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -51,11 +51,12 @@ namespace osu.Game.Tests.Visual private class TestSongSelect : PlaySongSelect { public WorkingBeatmap CurrentBeatmap => Beatmap.Value; + public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; } [BackgroundDependencyLoader] - private void load(BeatmapManager baseManager) + private void load(OsuGameBase game) { TestSongSelect songSelect = null; @@ -69,12 +70,16 @@ namespace osu.Game.Tests.Visual dependencies.Cache(rulesets = new RulesetStore(contextFactory)); dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null) { - DefaultBeatmap = defaultBeatmap = baseManager.GetWorkingBeatmap(null) + DefaultBeatmap = defaultBeatmap = game.Beatmap.Default }); void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () => { - if (deleteMaps) manager.DeleteAll(); + if (deleteMaps) + { + manager.DeleteAll(); + game.Beatmap.SetDefault(); + } if (songSelect != null) { @@ -91,6 +96,8 @@ namespace osu.Game.Tests.Visual AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap); + AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap); + AddStep("import test maps", () => { for (int i = 0; i < 100; i += 10) diff --git a/osu.Game.Tests/Visual/TestCaseResults.cs b/osu.Game.Tests/Visual/TestCaseResults.cs index d0c5aa4939..28ce9524e0 100644 --- a/osu.Game.Tests/Visual/TestCaseResults.cs +++ b/osu.Game.Tests/Visual/TestCaseResults.cs @@ -15,6 +15,15 @@ namespace osu.Game.Tests.Visual { private BeatmapManager beatmaps; + public override IReadOnlyList RequiredTypes => new[] + { + typeof(Score), + typeof(Results), + typeof(ResultsPage), + typeof(ResultsPageScore), + typeof(ResultsPageRanking) + }; + [BackgroundDependencyLoader] private void load(BeatmapManager beatmaps) { @@ -41,12 +50,12 @@ namespace osu.Game.Tests.Visual MaxCombo = 123, Rank = ScoreRank.A, Date = DateTimeOffset.Now, - Statistics = new Dictionary + Statistics = new Dictionary { - { "300", 50 }, - { "100", 20 }, - { "50", 50 }, - { "x", 1 } + { HitResult.Great, 50 }, + { HitResult.Good, 20 }, + { HitResult.Meh, 50 }, + { HitResult.Miss, 1 } }, User = new User { diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 64a9aa50a0..71975bf0fa 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -14,10 +14,20 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; - public SampleChannel GetChannel(SampleManager manager) + public SampleChannel GetChannel(SampleManager manager, string resourceNamespace = null) { - var channel = manager.Get($"Gameplay/{Bank}-{Name}"); - channel.Volume.Value = Volume / 100.0; + SampleChannel channel = null; + + if (resourceNamespace != null) + channel = manager.Get($"Gameplay/{resourceNamespace}/{Bank}-{Name}"); + + // try without namespace as a fallback. + if (channel == null) + channel = manager.Get($"Gameplay/{Bank}-{Name}"); + + if (channel != null) + channel.Volume.Value = Volume / 100.0; + return channel; } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f586e0862d..ea7c4bd954 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -18,6 +18,7 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.IO; using osu.Game.Database; using osu.Game.Graphics; +using osu.Game.Graphics.Textures; using osu.Game.IO; using osu.Game.IPC; using osu.Game.Online.API; @@ -101,15 +102,26 @@ namespace osu.Game.Beatmaps /// public Func GetStableStorage { private get; set; } + private void refreshImportContext() + { + lock (importContextLock) + { + importContext?.Value?.Dispose(); + + importContext = new Lazy(() => + { + var c = createContext(); + c.Database.AutoTransactionsEnabled = false; + return c; + }); + } + } + public BeatmapManager(Storage storage, Func context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) { createContext = context; - importContext = new Lazy(() => - { - var c = createContext(); - c.Database.AutoTransactionsEnabled = false; - return c; - }); + + refreshImportContext(); beatmaps = createBeatmapStore(context); files = new FileStore(context, storage); @@ -174,13 +186,16 @@ namespace osu.Game.Beatmaps { e = e.InnerException ?? e; Logger.Error(e, $@"Could not import beatmap set ({Path.GetFileName(path)})"); + refreshImportContext(); } } notification.State = ProgressNotificationState.Completed; } - private readonly Lazy importContext; + private readonly object importContextLock = new object(); + + private Lazy importContext; /// /// Import a beatmap from an . @@ -189,7 +204,7 @@ namespace osu.Game.Beatmaps public BeatmapSetInfo Import(ArchiveReader archiveReader) { // let's only allow one concurrent import at a time for now. - lock (importContext) + lock (importContextLock) { var context = importContext.Value; @@ -314,7 +329,7 @@ namespace osu.Game.Beatmaps /// The beatmap set to delete. public void Delete(BeatmapSetInfo beatmapSet) { - lock (importContext) + lock (importContextLock) { var context = importContext.Value; @@ -378,7 +393,7 @@ namespace osu.Game.Beatmaps if (beatmapSet.Protected) return; - lock (importContext) + lock (importContextLock) { var context = importContext.Value; @@ -652,7 +667,7 @@ namespace osu.Game.Beatmaps try { - return new TextureStore(new RawTextureLoaderStore(store), false).Get(getPathForFile(Metadata.BackgroundFile)); + return new LargeTextureStore(new RawTextureLoaderStore(store)).Get(getPathForFile(Metadata.BackgroundFile)); } catch { diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 40e45da13c..c2c13e1909 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The default sample volume at this control point. /// - public int SampleVolume; + public int SampleVolume = 100; /// /// Create a SampleInfo based on the sample settings in this control point. diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 8da6a0cefb..168f37e44e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -266,6 +266,6 @@ namespace osu.Game.Beatmaps.Formats throw new InvalidDataException($@"Unknown origin: {value}"); } - private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"')); + private string cleanFilename(string path) => FileSafety.PathSanitise(path.Trim('\"')); } } diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 8eb2ddc0ab..4fb08a41f3 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -5,8 +5,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using OpenTK.Graphics; +using osu.Game.Graphics.Textures; namespace osu.Game.Graphics.Backgrounds { @@ -22,7 +22,6 @@ namespace osu.Game.Graphics.Backgrounds this.textureName = textureName; RelativeSizeAxes = Axes.Both; - Depth = float.MaxValue; Add(Sprite = new Sprite { @@ -35,7 +34,7 @@ namespace osu.Game.Graphics.Backgrounds } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { if (!string.IsNullOrEmpty(textureName)) Sprite.Texture = textures.Get(textureName); diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index c788df3066..f67da52fc0 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -5,6 +5,8 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using OpenTK; namespace osu.Game.Graphics.Containers { @@ -22,6 +24,48 @@ namespace osu.Game.Graphics.Containers StateChanged += onStateChanged; } + /// + /// Whether mouse input should be blocked screen-wide while this overlay is visible. + /// Performing mouse actions outside of the valid extents will hide the overlay but pass the events through. + /// + public virtual bool BlockScreenWideMouse => BlockPassThroughMouse; + + // receive input outside our bounds so we can trigger a close event on ourselves. + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos); + + protected override bool OnWheel(InputState state) + { + // always allow wheel to pass through to stuff outside our DrawRectangle. + if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) + return false; + + return BlockPassThroughMouse; + } + + protected override bool OnClick(InputState state) + { + if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) + { + State = Visibility.Hidden; + return true; + } + + return base.OnClick(state); + } + + protected override bool OnDragStart(InputState state) + { + if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) + { + State = Visibility.Hidden; + return true; + } + + return base.OnDragStart(state); + } + + protected override bool OnDrag(InputState state) => State == Visibility.Hidden; + private void onStateChanged(Visibility visibility) { switch (visibility) diff --git a/osu.Game/Graphics/Textures/LargeTextureStore.cs b/osu.Game/Graphics/Textures/LargeTextureStore.cs new file mode 100644 index 0000000000..166364c8dd --- /dev/null +++ b/osu.Game/Graphics/Textures/LargeTextureStore.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; + +namespace osu.Game.Graphics.Textures +{ + /// + /// A texture store that bypasses atlasing. + /// + public class LargeTextureStore : TextureStore + { + public LargeTextureStore(IResourceStore store = null) : base(store, false) + { + } + } +} diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 065c770738..14605081b6 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -122,26 +122,26 @@ namespace osu.Game.Online.API.Requests { foreach (var kvp in value) { - string key = kvp.Key; - switch (key) + HitResult newKey; + switch (kvp.Key) { case @"count_300": - key = @"300"; + newKey = HitResult.Great; break; case @"count_100": - key = @"100"; + newKey = HitResult.Good; break; case @"count_50": - key = @"50"; + newKey = HitResult.Meh; break; case @"count_miss": - key = @"x"; + newKey = HitResult.Miss; break; default: continue; } - Statistics.Add(key, kvp.Value); + Statistics.Add(newKey, kvp.Value); } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1dc4c487f3..257b78ea0a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -27,6 +27,7 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Screens.Play; using osu.Game.Input.Bindings; +using OpenTK.Graphics; namespace osu.Game { @@ -65,6 +66,8 @@ namespace osu.Game public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight; + public readonly BindableBool ShowOverlays = new BindableBool(); + private OsuScreen screenStack; private VolumeControl volume; @@ -280,6 +283,21 @@ namespace osu.Game settings.StateChanged += _ => updateScreenOffset(); notifications.StateChanged += _ => updateScreenOffset(); + notifications.Enabled.BindTo(ShowOverlays); + + ShowOverlays.ValueChanged += show => + { + //central game screen change logic. + if (!show) + { + hideAllOverlays(); + musicController.State = Visibility.Hidden; + Toolbar.State = Visibility.Hidden; + } + else + Toolbar.State = Visibility.Visible; + }; + Cursor.State = Visibility.Hidden; } @@ -314,10 +332,21 @@ namespace osu.Game } private Task asyncLoadStream; + private int visibleOverlayCount; private void loadComponentSingleFile(T d, Action add) where T : Drawable { + var focused = d as FocusedOverlayContainer; + if (focused != null) + { + focused.StateChanged += s => + { + visibleOverlayCount += s == Visibility.Visible ? 1 : -1; + screenStack.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint); + }; + } + // schedule is here to ensure that all component loads are done after LoadComplete is run (and thus all dependencies are cached). // with some better organisation of LoadComplete to do construction and dependency caching in one step, followed by calls to loadComponentSingleFile, // we could avoid the need for scheduling altogether. @@ -361,8 +390,6 @@ namespace osu.Game public bool OnReleased(GlobalAction action) => false; - public event Action ScreenChanged; - private Container mainContent; private Container overlayContent; @@ -380,29 +407,6 @@ namespace osu.Game notifications.State = Visibility.Hidden; } - private void screenChanged(Screen newScreen) - { - currentScreen = newScreen as OsuScreen; - - if (currentScreen == null) - { - Exit(); - return; - } - - //central game screen change logic. - if (!currentScreen.ShowOverlays) - { - hideAllOverlays(); - musicController.State = Visibility.Hidden; - Toolbar.State = Visibility.Hidden; - } - else - Toolbar.State = Visibility.Visible; - - ScreenChanged?.Invoke(newScreen); - } - protected override bool OnExiting() { if (screenStack.ChildScreen == null) return false; @@ -448,15 +452,18 @@ namespace osu.Game private void screenAdded(Screen newScreen) { + currentScreen = (OsuScreen)newScreen; + newScreen.ModePushed += screenAdded; newScreen.Exited += screenRemoved; - - screenChanged(newScreen); } private void screenRemoved(Screen newScreen) { - screenChanged(newScreen); + currentScreen = (OsuScreen)newScreen; + + if (newScreen == null) + Exit(); } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ea0bf22112..e311aea8e4 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -18,8 +18,10 @@ using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Online.API; using osu.Framework.Graphics.Performance; +using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Game.Database; +using osu.Game.Graphics.Textures; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; @@ -89,6 +91,8 @@ namespace osu.Game { dependencies.Cache(contextFactory = new DatabaseContextFactory(Host)); + dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); + dependencies.Cache(this); dependencies.Cache(LocalConfig); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs index 5a3aba7b43..2d5913d8ca 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; @@ -48,7 +49,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Font = @"Exo2.0-RegularItalic", Margin = new MarginPadding { Left = side_margin } }, - new DrawableFlag(score.User.Country?.FlagName) + new DrawableFlag(score.User.Country) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -104,7 +105,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Text = $"{score.Statistics["300"]}/{score.Statistics["100"]}/{score.Statistics["50"]}", + Text = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}", Font = @"Exo2.0-RegularItalic", Margin = new MarginPadding { Right = side_margin } }, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs index 833ed94c0f..e3b878587d 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs @@ -52,13 +52,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores score = value; avatar.User = username.User = score.User; - flag.FlagName = score.User.Country?.FlagName; + flag.Country = score.User.Country; date.Text = $@"achieved {score.Date:MMM d, yyyy}"; rank.UpdateRank(score.Rank); totalScore.Value = $@"{score.TotalScore:N0}"; accuracy.Value = $@"{score.Accuracy:P2}"; - statistics.Value = $"{score.Statistics["300"]}/{score.Statistics["100"]}/{score.Statistics["50"]}"; + statistics.Value = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}"; modsContainer.Clear(); foreach (Mod mod in score.Mods) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index dcabad8ed6..20c1cf9332 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -241,8 +241,6 @@ namespace osu.Game.Overlays public override bool AcceptsFocus => true; - protected override bool OnClick(InputState state) => true; - protected override void OnFocus(InputState state) { //this is necessary as textbox is masked away and therefore can't get focus :( diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 77b7c3add2..21f07bb0a4 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -32,7 +32,10 @@ namespace osu.Game.Overlays.Mods private readonly Container iconsContainer; private SampleChannel sampleOn, sampleOff; - public Action Action; // Passed the selected mod or null if none + /// + /// Fired when the selection changes. + /// + public Action SelectionChanged; public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty; @@ -42,71 +45,73 @@ namespace osu.Game.Overlays.Mods // A selected index of -1 means not selected. private int selectedIndex = -1; - protected int SelectedIndex + /// + /// Change the selected mod index of this button. + /// + /// The new index. + /// Whether the selection changed. + private bool changeSelectedIndex(int newIndex) { - get + if (newIndex == selectedIndex) return false; + + int direction = newIndex < selectedIndex ? -1 : 1; + bool beforeSelected = Selected; + + Mod modBefore = SelectedMod ?? Mods[0]; + + if (newIndex >= Mods.Length) + newIndex = -1; + else if (newIndex < -1) + newIndex = Mods.Length - 1; + + if (newIndex >= 0 && !Mods[newIndex].HasImplementation) + return false; + + selectedIndex = newIndex; + Mod modAfter = SelectedMod ?? Mods[0]; + + if (beforeSelected != Selected) { - return selectedIndex; + iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); + iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); } - set + + if (modBefore != modAfter) { - if (value == selectedIndex) return; + const float rotate_angle = 16; - int direction = value < selectedIndex ? -1 : 1; - bool beforeSelected = Selected; + foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); + backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); - Mod modBefore = SelectedMod ?? Mods[0]; - - if (value >= Mods.Length) - selectedIndex = -1; - else if (value < -1) - selectedIndex = Mods.Length - 1; - else - selectedIndex = value; - - Mod modAfter = SelectedMod ?? Mods[0]; - - if (beforeSelected != Selected) + backgroundIcon.Icon = modAfter.Icon; + using (BeginDelayedSequence(mod_switch_duration, true)) { - iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); - iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); + foregroundIcon + .RotateTo(-rotate_angle * direction) + .RotateTo(0f, mod_switch_duration, mod_switch_easing); + + backgroundIcon + .RotateTo(rotate_angle * direction) + .RotateTo(0f, mod_switch_duration, mod_switch_easing); + + Schedule(() => displayMod(modAfter)); } - - if (modBefore != modAfter) - { - const float rotate_angle = 16; - - 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; - using (BeginDelayedSequence(mod_switch_duration, true)) - { - foregroundIcon - .RotateTo(-rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - backgroundIcon - .RotateTo(rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - Schedule(() => displayMod(modAfter)); - } - } - - foregroundIcon.Highlighted = Selected; } + + foregroundIcon.Highlighted = Selected; + + (selectedIndex == -1 ? sampleOff : sampleOn).Play(); + SelectionChanged?.Invoke(SelectedMod); + return true; } - public bool Selected => SelectedIndex != -1; + public bool Selected => selectedIndex != -1; private Color4 selectedColour; + public Color4 SelectedColour { - get - { - return selectedColour; - } + get { return selectedColour; } set { if (value == selectedColour) return; @@ -116,12 +121,10 @@ namespace osu.Game.Overlays.Mods } private Mod mod; + public Mod Mod { - get - { - return mod; - } + get { return mod; } set { mod = value; @@ -147,9 +150,7 @@ namespace osu.Game.Overlays.Mods public Mod[] Mods { get; private set; } - // the mods from Mod, only multiple if Mod is a MultiMod - - public virtual Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex); + public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); [BackgroundDependencyLoader] private void load(AudioManager audio) @@ -163,31 +164,42 @@ namespace osu.Game.Overlays.Mods switch (args.Button) { case MouseButton.Left: - SelectNext(); + SelectNext(1); break; case MouseButton.Right: - SelectPrevious(); + SelectNext(-1); break; } + return true; } - public void SelectNext() + /// + /// Select the next available mod in a specified direction. + /// + /// 1 for forwards, -1 for backwards. + public void SelectNext(int direction) { - (++SelectedIndex == Mods.Length ? sampleOff : sampleOn).Play(); - Action?.Invoke(SelectedMod); + int start = selectedIndex + direction; + // wrap around if we are at an extremity. + if (start >= Mods.Length) + start = -1; + else if (start < -1) + start = Mods.Length - 1; + + for (int i = start; i < Mods.Length && i >= 0; i += direction) + { + if (Mods[i].HasImplementation) + { + changeSelectedIndex(i); + return; + } + } + + Deselect(); } - public void SelectPrevious() - { - (--SelectedIndex == -1 ? sampleOff : sampleOn).Play(); - Action?.Invoke(SelectedMod); - } - - public void Deselect() - { - SelectedIndex = -1; - } + public void Deselect() => changeSelectedIndex(-1); private void displayMod(Mod mod) { @@ -195,6 +207,7 @@ namespace osu.Game.Overlays.Mods backgroundIcon.Icon = foregroundIcon.Icon; foregroundIcon.Icon = mod.Icon; text.Text = mod.Name; + Colour = mod.HasImplementation ? Color4.White : Color4.Gray; } private void createIcons() @@ -204,13 +217,13 @@ namespace osu.Game.Overlays.Mods { iconsContainer.AddRange(new[] { - backgroundIcon = new ModIcon(Mods[1]) + backgroundIcon = new PassThroughTooltipModIcon(Mods[1]) { Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, Position = new Vector2(1.5f), }, - foregroundIcon = new ModIcon(Mods[0]) + foregroundIcon = new PassThroughTooltipModIcon(Mods[0]) { Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, @@ -220,7 +233,7 @@ namespace osu.Game.Overlays.Mods } else { - iconsContainer.Add(foregroundIcon = new ModIcon(Mod) + iconsContainer.Add(foregroundIcon = new PassThroughTooltipModIcon(Mod) { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -259,5 +272,15 @@ namespace osu.Game.Overlays.Mods Mod = mod; } + + private class PassThroughTooltipModIcon : ModIcon + { + public override string TooltipText => null; + + public PassThroughTooltipModIcon(Mod mod) + : base(mod) + { + } + } } } diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index aac8a72dd7..50310d1b27 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Mods return new ModButton(m) { SelectedColour = selectedColour, - Action = Action, + SelectionChanged = Action, }; }).ToArray(); @@ -83,26 +83,33 @@ namespace osu.Game.Overlays.Mods { var index = Array.IndexOf(ToggleKeys, args.Key); if (index > -1 && index < buttons.Length) - buttons[index].SelectNext(); + buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1); return base.OnKeyDown(state, args); } - public void DeselectAll() - { - foreach (ModButton button in buttons) - button.Deselect(); - } + public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); - public void DeselectTypes(Type[] modTypes) + /// + /// Deselect one or more mods in this section. + /// + /// The types of s which should be deselected. + /// Set to true to bypass animations and update selections immediately. + public void DeselectTypes(IEnumerable modTypes, bool immediate = false) { + int delay = 0; foreach (var button in buttons) { Mod selected = button.SelectedMod; if (selected == null) continue; foreach (Type type in modTypes) if (type.IsInstanceOfType(selected)) - button.Deselect(); + { + if (immediate) + button.Deselect(); + else + Scheduler.AddDelayed(() => button.Deselect(), delay += 50); + } } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 9639907914..cc5a17358d 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -100,17 +100,22 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } - public void DeselectTypes(Type[] modTypes) + /// + /// Deselect one or more mods. + /// + /// The types of s which should be deselected. + /// Set to true to bypass animations and update selections immediately. + public void DeselectTypes(Type[] modTypes, bool immediate = false) { if (modTypes.Length == 0) return; foreach (ModSection section in ModSectionsContainer.Children) - section.DeselectTypes(modTypes); + section.DeselectTypes(modTypes, immediate); } private void modButtonPressed(Mod selectedMod) { if (selectedMod != null) - DeselectTypes(selectedMod.IncompatibleMods); + DeselectTypes(selectedMod.IncompatibleMods, true); refreshSelectedMods(); } @@ -127,10 +132,6 @@ namespace osu.Game.Overlays.Mods ranked &= mod.Ranked; } - // 1.00x - // 1.05x - // 1.20x - MultiplierLabel.Text = $"{multiplier:N2}x"; if (!ranked) MultiplierLabel.Text += " (Unranked)"; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index d7c553aa7d..685fb5c6ab 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -65,10 +65,10 @@ namespace osu.Game.Overlays AlwaysPresent = true; } - protected override bool OnDragStart(InputState state) => true; - protected override bool OnDrag(InputState state) { + if (base.OnDrag(state)) return true; + Trace.Assert(state.Mouse.PositionMouseDown != null, "state.Mouse.PositionMouseDown != null"); // ReSharper disable once PossibleInvalidOperationException @@ -78,7 +78,7 @@ namespace osu.Game.Overlays change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length; dragContainer.MoveTo(change); - return base.OnDrag(state); + return true; } protected override bool OnDragEnd(InputState state) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 2f1c3285ef..5744e8c189 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,7 +10,9 @@ using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; using System; +using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Threading; namespace osu.Game.Overlays { @@ -21,6 +22,11 @@ namespace osu.Game.Overlays public const float TRANSITION_LENGTH = 600; + /// + /// Whether posted notifications should be processed. + /// + public readonly BindableBool Enabled = new BindableBool(true); + private FlowContainer sections; /// @@ -28,6 +34,27 @@ namespace osu.Game.Overlays /// public Func GetToolbarHeight; + public NotificationOverlay() + { + ScheduledDelegate notificationsEnabler = null; + Enabled.ValueChanged += v => + { + if (!IsLoaded) + { + processingPosts = v; + return; + } + + notificationsEnabler?.Cancel(); + + if (v) + // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. + notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000); + else + processingPosts = false; + }; + } + [BackgroundDependencyLoader] private void load() { @@ -85,14 +112,21 @@ namespace osu.Game.Overlays private void notificationClosed() { - // hide ourselves if all notifications have been dismissed. - if (totalCount == 0) - State = Visibility.Hidden; + Schedule(() => + { + // hide ourselves if all notifications have been dismissed. + if (totalCount == 0) + State = Visibility.Hidden; + }); updateCounts(); } - public void Post(Notification notification) => Schedule(() => + private readonly Scheduler postScheduler = new Scheduler(); + + private bool processingPosts = true; + + public void Post(Notification notification) => postScheduler.Add(() => { ++runningDepth; notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; @@ -109,6 +143,13 @@ namespace osu.Game.Overlays updateCounts(); }); + protected override void Update() + { + base.Update(); + if (processingPosts) + postScheduler.Update(); + } + protected override void PopIn() { base.PopIn(); diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 2bd0321d12..42fcc3aa0f 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -15,7 +15,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Notifications { - public class NotificationSection : FillFlowContainer + public class NotificationSection : AlwaysUpdateFillFlowContainer { private OsuSpriteText titleText; private OsuSpriteText countText; @@ -33,6 +33,7 @@ namespace osu.Game.Overlays.Notifications public IEnumerable AcceptTypes; private string clearText; + public string ClearText { get { return clearText; } @@ -110,7 +111,7 @@ namespace osu.Game.Overlays.Notifications }, }, }, - notifications = new FillFlowContainer + notifications = new AlwaysUpdateFillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, @@ -159,4 +160,13 @@ namespace osu.Game.Overlays.Notifications notifications?.Children.ForEach(n => n.Read = true); } } + + public class AlwaysUpdateFillFlowContainer : FillFlowContainer + where T : Drawable + { + // this is required to ensure correct layout and scheduling on children. + // the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297). + protected override bool RequiresChildrenUpdate => true; + } + } diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index c39c630858..d797372390 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -95,8 +95,8 @@ namespace osu.Game.Overlays.Notifications protected virtual void Completed() { - base.Close(); CompletionTarget?.Invoke(CreateCompletionNotification()); + base.Close(); } public override bool DisplayOnTop => false; diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 18e77cf186..960bb60287 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -109,7 +109,7 @@ namespace osu.Game.Overlays.Profile Origin = Anchor.BottomLeft, Y = -48, }, - countryFlag = new DrawableFlag(user.Country?.FlagName) + countryFlag = new DrawableFlag(user.Country) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -333,7 +333,7 @@ namespace osu.Game.Overlays.Profile { infoTextLeft.AddText("from "); infoTextLeft.AddText(user.Country.FullName, boldItalic); - countryFlag.FlagName = user.Country.FlagName; + countryFlag.Country = user.Country; } infoTextLeft.NewParagraph(); diff --git a/osu.Game/Overlays/Profile/SupporterIcon.cs b/osu.Game/Overlays/Profile/SupporterIcon.cs index 570d5a13bb..b5cd94e54b 100644 --- a/osu.Game/Overlays/Profile/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/SupporterIcon.cs @@ -1,20 +1,23 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . +// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; namespace osu.Game.Overlays.Profile { - public class SupporterIcon : CircularContainer + public class SupporterIcon : CircularContainer, IHasTooltip { private readonly Box background; + public string TooltipText => "osu!supporter"; + public SupporterIcon() { Masking = true; diff --git a/osu.Game/Overlays/Settings/Sections/AudioSection.cs b/osu.Game/Overlays/Settings/Sections/AudioSection.cs index c994a6296c..738c9e94d7 100644 --- a/osu.Game/Overlays/Settings/Sections/AudioSection.cs +++ b/osu.Game/Overlays/Settings/Sections/AudioSection.cs @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Settings.Sections public class AudioSection : SettingsSection { public override string Header => "Audio"; - public override FontAwesome Icon => FontAwesome.fa_headphones; + public override FontAwesome Icon => FontAwesome.fa_volume_up; public AudioSection() { @@ -23,4 +23,4 @@ namespace osu.Game.Overlays.Settings.Sections }; } } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index a80f6d4da8..f798d63e5a 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -177,8 +177,6 @@ namespace osu.Game.Overlays public override bool AcceptsFocus => true; - protected override bool OnClick(InputState state) => true; - protected override void OnFocus(InputState state) { GetContainingInputManager().ChangeFocus(searchTextBox); diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index e36db1f9da..ef1f7c8c70 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -62,10 +62,10 @@ namespace osu.Game.Overlays.Toolbar new ToolbarChatButton(), new ToolbarSocialButton(), new ToolbarMusicButton(), - new ToolbarButton - { - Icon = FontAwesome.fa_search - }, + //new ToolbarButton + //{ + // Icon = FontAwesome.fa_search + //}, userArea = new ToolbarUserArea(), new ToolbarNotificationButton(), } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 9aa660147a..c2e7fc5b44 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -34,15 +33,6 @@ namespace osu.Game.Overlays public const float CONTENT_X_MARGIN = 50; - // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - - protected override bool OnClick(InputState state) - { - State = Visibility.Hidden; - return true; - } - public UserProfileOverlay() { FirstWaveColour = OsuColour.Gray(0.4f); diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 5ab4b7636b..3d7880f56f 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Judgements { diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index d804111a7f..f8c9b9734f 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Judgements { diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs new file mode 100644 index 0000000000..2d7cda5f1f --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + /// + /// Represents a mod which can override (and block) a fail. + /// + public interface IApplicableFailOverride : IApplicableMod + { + /// + /// Whether we should allow failing at the current point in time. + /// + bool AllowFail { get; } + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableMod.cs b/osu.Game/Rulesets/Mods/IApplicableMod.cs new file mode 100644 index 0000000000..ed2b652598 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableMod.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + /// + /// The base interface for a mod which can be applied in some way. + /// If this is not implemented by a mod, it will not be available for use in-game. + /// + public interface IApplicableMod + { + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToClock.cs b/osu.Game/Rulesets/Mods/IApplicableToClock.cs index f0502cf346..8bb4e2b97d 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToClock.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToClock.cs @@ -8,8 +8,8 @@ namespace osu.Game.Rulesets.Mods /// /// An interface for mods that make adjustments to the track. /// - public interface IApplicableToClock + public interface IApplicableToClock : IApplicableMod { void ApplyToClock(IAdjustableClock clock); } -} \ No newline at end of file +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs index 58f5defb5e..a95aa4370c 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs @@ -8,8 +8,8 @@ namespace osu.Game.Rulesets.Mods /// /// An interface for mods that make general adjustments to difficulty. /// - public interface IApplicableToDifficulty + public interface IApplicableToDifficulty : IApplicableMod { void ApplyToDifficulty(BeatmapDifficulty difficulty); } -} \ No newline at end of file +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs new file mode 100644 index 0000000000..66dbc85095 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for s that can be applied to s. + /// + public interface IApplicableToDrawableHitObjects : IApplicableMod + { + /// + /// Applies this to a list of s. + /// + /// The list of s to apply to. + void ApplyToDrawableHitObjects(IEnumerable drawables); + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs index 7f39def343..1964ad728f 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods /// /// An interface for s that can be applied to s. /// - public interface IApplicableToHitObject + public interface IApplicableToHitObject : IApplicableMod where TObject : HitObject { /// diff --git a/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs b/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs index 9b23dd58f9..eae8c9d15c 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mods /// /// An interface for s that can be applied to s. /// - public interface IApplicableToRulesetContainer + public interface IApplicableToRulesetContainer : IApplicableMod where TObject : HitObject { /// diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs index db9b713c59..2314999d4f 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods /// /// An interface for mods that make general adjustments to score processor. /// - public interface IApplicableToScoreProcessor + public interface IApplicableToScoreProcessor : IApplicableMod { void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); } diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 7b0034863e..68ed545701 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -41,6 +41,11 @@ namespace osu.Game.Rulesets.Mods /// public abstract double ScoreMultiplier { get; } + /// + /// Returns true if this mod is implemented (and playable). + /// + public virtual bool HasImplementation => this is IApplicableMod; + /// /// Returns if this mod is ranked. /// @@ -50,10 +55,5 @@ namespace osu.Game.Rulesets.Mods /// The mods this mod cannot be enabled with. /// public virtual Type[] IncompatibleMods => new Type[] { }; - - /// - /// Whether we should allow failing at the current point in time. - /// - public virtual bool AllowFail => true; } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index d94d4ba0db..bddb97f374 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods } } - public class ModAutoplay : Mod + public class ModAutoplay : Mod, IApplicableFailOverride { public override string Name => "Autoplay"; public override string ShortenedName => "AT"; @@ -29,5 +29,6 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Watch a perfect automated play through the song"; public override double ScoreMultiplier => 0; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; + public bool AllowFail => false; } } diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 3a3878d77e..8aefd1b88e 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -6,7 +6,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModNoFail : Mod + public abstract class ModNoFail : Mod, IApplicableFailOverride { public override string Name => "NoFail"; public override string ShortenedName => "NF"; @@ -20,6 +20,6 @@ namespace osu.Game.Rulesets.Mods /// /// We never fail, 'yo. /// - public override bool AllowFail => false; + public bool AllowFail => false; } -} \ No newline at end of file +} diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ab4c04be3f..45a7275c53 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics; using osu.Framework.Configuration; using OpenTK; using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Drawables { @@ -74,6 +75,9 @@ namespace osu.Game.Rulesets.Objects.Drawables protected List Samples = new List(); protected virtual IEnumerable GetSamples() => HitObject.Samples; + // Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first + protected virtual string SampleNamespace => null; + public readonly Bindable State = new Bindable(); protected DrawableHitObject(TObject hitObject) @@ -101,7 +105,7 @@ namespace osu.Game.Rulesets.Objects.Drawables Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume }; - SampleChannel channel = localSampleInfo.GetChannel(audio.Sample); + SampleChannel channel = localSampleInfo.GetChannel(audio.Sample, SampleNamespace); if (channel == null) continue; @@ -119,6 +123,9 @@ namespace osu.Game.Rulesets.Objects.Drawables { UpdateState(state); + // apply any custom state overrides + ApplyCustomUpdateState?.Invoke(this, state); + if (State == ArmedState.Hit) PlaySamples(); }; @@ -240,9 +247,15 @@ namespace osu.Game.Rulesets.Objects.Drawables h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j); h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j); + h.ApplyCustomUpdateState += (d, s) => ApplyCustomUpdateState?.Invoke(d, s); nestedHitObjects.Add(h); } + /// + /// Bind to apply a custom state which can override the default implementation. + /// + public event Action ApplyCustomUpdateState; + protected abstract void UpdateState(ArmedState state); } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 5cdf46ee46..3038c51e64 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -38,6 +38,13 @@ namespace osu.Game.Rulesets /// A ruleset, if available, else null. public RulesetInfo GetRuleset(int id) => AvailableRulesets.FirstOrDefault(r => r.ID == id); + /// + /// Retrieve a ruleset using a known short name. + /// + /// The ruleset's short name. + /// A ruleset, if available, else null. + public RulesetInfo GetRuleset(string shortName) => AvailableRulesets.FirstOrDefault(r => r.ShortName == shortName); + /// /// All available rulesets. /// diff --git a/osu.Game/Rulesets/Objects/Drawables/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs similarity index 91% rename from osu.Game/Rulesets/Objects/Drawables/HitResult.cs rename to osu.Game/Rulesets/Scoring/HitResult.cs index 961843cbd7..49ab9fd2f0 100644 --- a/osu.Game/Rulesets/Objects/Drawables/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -3,7 +3,7 @@ using System.ComponentModel; -namespace osu.Game.Rulesets.Objects.Drawables +namespace osu.Game.Rulesets.Scoring { public enum HitResult { diff --git a/osu.Game/Rulesets/Scoring/Score.cs b/osu.Game/Rulesets/Scoring/Score.cs index 6a06f364c6..025335ba55 100644 --- a/osu.Game/Rulesets/Scoring/Score.cs +++ b/osu.Game/Rulesets/Scoring/Score.cs @@ -40,6 +40,6 @@ namespace osu.Game.Rulesets.Scoring public DateTimeOffset Date; - public Dictionary Statistics = new Dictionary(); + public Dictionary Statistics = new Dictionary(); } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index e129a81116..23c4464bb1 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -8,7 +8,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Scoring { diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 5ca3d9521b..90f63aeab6 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.UI private readonly ModType type; - public string TooltipText { get; } + public virtual string TooltipText { get; } public ModIcon(Mod mod) { diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 8d09b554dd..3706033b68 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -261,6 +261,9 @@ namespace osu.Game.Rulesets.UI } Playfield.PostProcess(); + + foreach (var mod in Mods.OfType()) + mod.ApplyToDrawableHitObjects(Playfield.HitObjects.Objects); } protected override void Update() diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index de84e90baf..36867a84d5 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Threading; using osu.Game.Graphics.Backgrounds; namespace osu.Game.Screens.Backgrounds @@ -24,16 +25,22 @@ namespace osu.Game.Screens.Backgrounds private void display(Background newBackground) { - current?.FadeOut(800, Easing.OutQuint); + current?.FadeOut(800, Easing.InOutSine); current?.Expire(); Add(current = newBackground); + currentDisplay++; } + private ScheduledDelegate nextTask; + public void Next() { - currentDisplay++; - LoadComponentAsync(new Background(backgroundName) { Depth = currentDisplay }, display); + nextTask?.Cancel(); + nextTask = Scheduler.AddDelayed(() => + { + LoadComponentAsync(new Background(backgroundName) { Depth = currentDisplay }, display); + }, 100); } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 19d00f3477..76f51d1c33 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit { protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; private readonly Box bottomBackground; private readonly Container screenContainer; diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index ec2e8e0cb1..c96194f63d 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens { private bool showDisclaimer; - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; public Loader() { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index ce7856c5a9..c82d90d16c 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -11,12 +11,12 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics; -using osu.Game.Overlays.Toolbar; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; using osu.Framework.Audio.Sample; using osu.Framework.Audio; +using osu.Framework.Configuration; using osu.Framework.Threading; namespace osu.Game.Screens.Menu @@ -25,6 +25,8 @@ namespace osu.Game.Screens.Menu { public event Action StateChanged; + private readonly BindableBool showOverlays = new BindableBool(); + public Action OnEdit; public Action OnExit; public Action OnDirect; @@ -34,8 +36,6 @@ namespace osu.Game.Screens.Menu public Action OnChart; public Action OnTest; - private Toolbar toolbar; - private readonly FlowContainerWithOrigin buttonFlow; //todo: make these non-internal somehow. @@ -131,9 +131,9 @@ namespace osu.Game.Screens.Menu } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, OsuGame game = null) + private void load(AudioManager audio, OsuGame game) { - toolbar = game?.Toolbar; + if (game != null) showOverlays.BindTo(game.ShowOverlays); sampleBack = audio.Sample.Get(@"Menu/button-back-select"); } @@ -300,7 +300,7 @@ namespace osu.Game.Screens.Menu logoDelayedAction = Scheduler.AddDelayed(() => { - toolbar?.Hide(); + showOverlays.Value = false; logo.ClearTransforms(targetMember: nameof(Position)); logo.RelativePositionAxes = Axes.Both; @@ -329,7 +329,7 @@ namespace osu.Game.Screens.Menu logoTracking = true; logo.Impact(); - toolbar?.Show(); + showOverlays.Value = true; }, 200); break; default: diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 532ee71b72..d0ad613640 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Menu private readonly SpriteIcon icon; private Color4 iconColour; - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; public override bool HasLocalCursorDisplayed => true; diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index d7beb34a2f..a6a1afa320 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Menu public override bool HasLocalCursorDisplayed => true; - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty(); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 90f68ba9f1..fac0ec1422 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Menu { private readonly ButtonSystem buttons; - public override bool ShowOverlays => buttons.State != MenuState.Initial; + public override bool ShowOverlaysOnEnter => buttons.State != MenuState.Initial; private readonly BackgroundScreenDefault background; private Screen songSelect; diff --git a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs b/osu.Game/Screens/Multiplayer/ParticipantInfo.cs index fa48287ce1..2197b7477c 100644 --- a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs +++ b/osu.Game/Screens/Multiplayer/ParticipantInfo.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Multiplayer set { host.Text = value.Username; - flagContainer.Children = new[] { new DrawableFlag(value.Country?.FlagName) { RelativeSizeAxes = Axes.Both } }; + flagContainer.Children = new[] { new DrawableFlag(value.Country) { RelativeSizeAxes = Axes.Both } }; } } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 4a27c7f1ea..0013d1a882 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -28,7 +28,12 @@ namespace osu.Game.Screens /// protected virtual BackgroundScreen CreateBackground() => null; - public virtual bool ShowOverlays => true; + protected BindableBool ShowOverlays = new BindableBool(); + + /// + /// Whether overlays should be shown when this screen is entered or resumed. + /// + public virtual bool ShowOverlaysOnEnter => true; protected new OsuGameBase Game => base.Game as OsuGameBase; @@ -70,7 +75,10 @@ namespace osu.Game.Screens } if (osuGame != null) + { Ruleset.BindTo(osuGame.Ruleset); + ShowOverlays.BindTo(osuGame.ShowOverlays); + } sampleExit = audio.Sample.Get(@"UI/screen-back"); } @@ -94,6 +102,8 @@ namespace osu.Game.Screens base.OnResuming(last); logo.AppendAnimatingAction(() => LogoArriving(logo, true), true); sampleExit?.Play(); + + ShowOverlays.Value = ShowOverlaysOnEnter; } protected override void OnSuspending(Screen next) @@ -139,6 +149,8 @@ namespace osu.Game.Screens logo.AppendAnimatingAction(() => LogoArriving(logo, false), true); base.OnEntering(last); + + ShowOverlays.Value = ShowOverlaysOnEnter; } protected override bool OnExiting(Screen next) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 6969cd915b..094c0331f4 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -263,6 +263,14 @@ namespace osu.Game.Screens.Play private class Button : DialogButton { + protected override bool OnHover(InputState state) => true; + + protected override bool OnMouseMove(InputState state) + { + Selected.Value = true; + return base.OnMouseMove(state); + } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { if (args.Repeat || args.Key != Key.Enter || !Selected) diff --git a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs index 06ef87276a..351db533f3 100644 --- a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs @@ -7,10 +7,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HUD { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2cbb203c37..d4cfa0b3d4 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor; @@ -46,6 +46,8 @@ namespace osu.Game.Screens.Play public bool HasFailed { get; private set; } public bool AllowPause { get; set; } = true; + public bool AllowLeadIn { get; set; } = true; + public bool AllowResults { get; set; } = true; public int RestartCount; @@ -136,7 +138,10 @@ namespace osu.Game.Screens.Play decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = RulesetContainer.Objects.First().StartTime; - decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))); + decoupledClock.Seek(AllowLeadIn + ? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn)) + : firstObjectTime); + decoupledClock.ProcessFrame(); offsetClock = new FramedOffsetClock(decoupledClock); @@ -273,6 +278,8 @@ namespace osu.Game.Screens.Play ValidForResume = false; + if (!AllowResults) return; + using (BeginDelayedSequence(1000)) { onCompletionEvent = Schedule(delegate @@ -291,7 +298,7 @@ namespace osu.Game.Screens.Play private bool onFail() { - if (Beatmap.Value.Mods.Value.Any(m => !m.AllowFail)) + if (Beatmap.Value.Mods.Value.OfType().Any(m => !m.AllowFail)) return false; decoupledClock.Stop(); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index a220dcee0e..15a97096e7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play private BeatmapMetadataDisplay info; private bool showOverlays = true; - public override bool ShowOverlays => showOverlays; + public override bool ShowOverlaysOnEnter => showOverlays; public override bool AllowBeatmapRulesetChange => false; diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 25a42cae1c..0d29b74dfd 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -23,6 +23,7 @@ using osu.Game.Screens.Play; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; using osu.Framework.Graphics.Shapes; +using osu.Framework.Extensions; namespace osu.Game.Screens.Ranking { @@ -163,7 +164,7 @@ namespace osu.Game.Screens.Ranking } }; - statisticsContainer.ChildrenEnumerable = Score.Statistics.Select(s => new DrawableScoreStatistic(s)); + statisticsContainer.ChildrenEnumerable = Score.Statistics.OrderByDescending(p => p.Key).Select(s => new DrawableScoreStatistic(s)); } protected override void LoadComplete() @@ -186,9 +187,9 @@ namespace osu.Game.Screens.Ranking private class DrawableScoreStatistic : Container { - private readonly KeyValuePair statistic; + private readonly KeyValuePair statistic; - public DrawableScoreStatistic(KeyValuePair statistic) + public DrawableScoreStatistic(KeyValuePair statistic) { this.statistic = statistic; @@ -209,7 +210,7 @@ namespace osu.Game.Screens.Ranking Origin = Anchor.TopCentre, }, new OsuSpriteText { - Text = statistic.Key, + Text = statistic.Key.GetDescription(), Colour = colours.Gray7, Font = @"Exo2.0-Bold", Y = 26, @@ -250,16 +251,16 @@ namespace osu.Game.Screens.Ranking { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Text = datetime.ToString("HH:mm"), - Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 }, + Text = datetime.ToShortDateString(), + Padding = new MarginPadding { Horizontal = 10, Vertical = 5 }, Colour = Color4.White, }, new OsuSpriteText { Origin = Anchor.CentreRight, Anchor = Anchor.CentreRight, - Text = datetime.ToString("yyyy/MM/dd"), - Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 }, + Text = datetime.ToShortTimeString(), + Padding = new MarginPadding { Horizontal = 10, Vertical = 5 }, Colour = Color4.White, } }; diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 9d7867659d..98acd0815d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -53,6 +53,11 @@ namespace osu.Game.Screens.Select public override bool HandleInput => AllowSelection; + /// + /// Used to avoid firing null selections before the initial beatmaps have been loaded via . + /// + private bool initialLoadComplete; + private IEnumerable beatmapSets => root.Children.OfType(); public IEnumerable BeatmapSets @@ -76,7 +81,11 @@ namespace osu.Game.Screens.Select itemsCache.Invalidate(); scrollPositionCache.Invalidate(); - Schedule(() => BeatmapSetsChanged?.Invoke()); + Schedule(() => + { + BeatmapSetsChanged?.Invoke(); + initialLoadComplete = true; + }); })); } } @@ -185,7 +194,14 @@ namespace osu.Game.Screens.Select if (!Items.Any()) return; - int originalIndex = Items.IndexOf(selectedBeatmap?.Drawables.First()); + DrawableCarouselItem drawable = null; + + if (selectedBeatmap != null && (drawable = selectedBeatmap.Drawables.FirstOrDefault()) == null) + // if the selected beatmap isn't present yet, we can't correctly change selection. + // we can fix this by changing this method to not reference drawables / Items in the first place. + return; + + int originalIndex = Items.IndexOf(drawable); int currentIndex = originalIndex; // local function to increment the index in the required direction, wrapping over extremities. @@ -213,11 +229,15 @@ namespace osu.Game.Screens.Select } } - public void SelectNextRandom() + /// + /// Select the next beatmap in the random sequence. + /// + /// True if a selection could be made, else False. + public bool SelectNextRandom() { var visible = beatmapSets.Where(s => !s.Filtered).ToList(); if (!visible.Any()) - return; + return false; if (selectedBeatmap != null) { @@ -247,6 +267,7 @@ namespace osu.Game.Screens.Select set = visible.ElementAt(RNG.Next(visible.Count)); select(set.Beatmaps.Skip(RNG.Next(set.Beatmaps.Count())).FirstOrDefault()); + return true; } public void SelectPreviousRandom() @@ -513,7 +534,7 @@ namespace osu.Game.Screens.Select currentY += DrawHeight / 2; scrollableContent.Height = currentY; - if (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected) + if (initialLoadComplete && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) { selectedBeatmapSet = null; SelectionChanged?.Invoke(null); diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index fa1326de8c..c25ba66475 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; namespace osu.Game.Screens.Select { @@ -71,10 +72,6 @@ namespace osu.Game.Screens.Select { State = beatmap == null ? Visibility.Hidden : Visibility.Visible; - // ensure we ourselves are visible if not already. - if (!IsPresent) - State = Visibility.Visible; - Info?.FadeOut(250); Info?.Expire(); @@ -90,6 +87,8 @@ namespace osu.Game.Screens.Select public OsuSpriteText ArtistLabel { get; private set; } public FillFlowContainer MapperContainer { get; private set; } public FillFlowContainer InfoLabelContainer { get; private set; } + private UnicodeBindableString titleBinding; + private UnicodeBindableString artistBinding; public BufferedWedgeInfo(WorkingBeatmap working) { @@ -97,7 +96,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader] - private void load() + private void load(LocalisationEngine localisation) { var beatmapInfo = working.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); @@ -106,6 +105,9 @@ namespace osu.Game.Screens.Select CacheDrawnFrameBuffer = true; RelativeSizeAxes = Axes.Both; + titleBinding = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); + artistBinding = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); + Children = new Drawable[] { // We will create the white-to-black gradient by modulating transparency and having @@ -171,13 +173,11 @@ namespace osu.Game.Screens.Select TitleLabel = new OsuSpriteText { Font = @"Exo2.0-MediumItalic", - Text = string.IsNullOrEmpty(metadata.Source) ? metadata.Title : metadata.Source + " — " + metadata.Title, TextSize = 28, }, ArtistLabel = new OsuSpriteText { Font = @"Exo2.0-MediumItalic", - Text = metadata.Artist, TextSize = 17, }, MapperContainer = new FillFlowContainer @@ -197,6 +197,15 @@ namespace osu.Game.Screens.Select } } }; + artistBinding.ValueChanged += value => setMetadata(metadata.Source); + artistBinding.TriggerChange(); + } + + private void setMetadata(string source) + { + ArtistLabel.Text = artistBinding.Value; + TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; + ForceRedraw(); } private InfoLabel[] getInfoLabels() diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 6c0cc341fd..cea658b06c 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Select.Carousel { if (songSelect != null) { - startRequested = songSelect.Start; + startRequested = songSelect.FinaliseSelection; editRequested = songSelect.Edit; } diff --git a/osu.Game/Screens/Select/EditSongSelect.cs b/osu.Game/Screens/Select/EditSongSelect.cs index f02d25501e..37f2663d91 100644 --- a/osu.Game/Screens/Select/EditSongSelect.cs +++ b/osu.Game/Screens/Select/EditSongSelect.cs @@ -7,6 +7,10 @@ namespace osu.Game.Screens.Select { protected override bool ShowFooter => false; - protected override void Start() => Exit(); + protected override bool OnSelectionFinalised() + { + Exit(); + return true; + } } } diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index 489ab79fa4..03e9462636 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Select HeaderText = @"You have no beatmaps!"; BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps?"; - Icon = FontAwesome.fa_trash_o; + Icon = FontAwesome.fa_plane; Buttons = new PopupDialogButton[] { diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index c15a179e8c..ac47fade48 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -109,6 +109,13 @@ namespace osu.Game.Screens.Select.Leaderboards { if (value == placeholderState) return; + if (value != PlaceholderState.Successful) + { + getScoresRequest?.Cancel(); + getScoresRequest = null; + Scores = null; + } + switch (placeholderState = value) { case PlaceholderState.NetworkFailure: @@ -211,10 +218,6 @@ namespace osu.Game.Screens.Select.Leaderboards private void updateScores() { - getScoresRequest?.Cancel(); - getScoresRequest = null; - Scores = null; - if (Scope == LeaderboardScope.Local) { // TODO: get local scores from wherever here. @@ -234,16 +237,15 @@ namespace osu.Game.Screens.Select.Leaderboards return; } - PlaceholderState = PlaceholderState.Retrieving; - loading.Show(); - if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) { - loading.Hide(); PlaceholderState = PlaceholderState.NotSupporter; return; } + PlaceholderState = PlaceholderState.Retrieving; + loading.Show(); + getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope); getScoresRequest.Success += r => { diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index 377f32184d..857a716ab7 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Select.Leaderboards Masking = true, Children = new Drawable[] { - new DrawableFlag(Score.User?.Country?.FlagName) + new DrawableFlag(Score.User?.Country) { Width = 30, RelativeSizeAxes = Axes.Y, diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 898c195432..9143da326d 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -5,6 +5,10 @@ namespace osu.Game.Screens.Select { public class MatchSongSelect : SongSelect { - protected override void Start() => Exit(); + protected override bool OnSelectionFinalised() + { + Exit(); + return true; + } } } diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index c66cc7beff..789064a5f1 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -25,6 +25,8 @@ namespace osu.Game.Screens.Select.Options private readonly Box holder; private readonly FillFlowContainer buttonsContainer; + public override bool BlockScreenWideMouse => false; + protected override void PopIn() { base.PopIn(); diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 727cdb9959..a3997640ba 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Select { private OsuScreen player; private readonly ModSelectOverlay modSelect; - private readonly BeatmapDetailArea beatmapDetails; + protected readonly BeatmapDetailArea BeatmapDetails; private bool removeAutoModOnResume; public PlaySongSelect() @@ -35,13 +35,13 @@ namespace osu.Game.Screens.Select Anchor = Anchor.BottomCentre, }); - LeftContent.Add(beatmapDetails = new BeatmapDetailArea + LeftContent.Add(BeatmapDetails = new BeatmapDetailArea { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 10, Right = 5 }, }); - beatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s)); + BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s)); } private SampleChannel sampleConfirm; @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Select beatmap.Mods.BindTo(modSelect.SelectedMods); - beatmapDetails.Beatmap = beatmap; + BeatmapDetails.Beatmap = beatmap; if (beatmap.Track != null) beatmap.Track.Looping = true; @@ -124,9 +124,9 @@ namespace osu.Game.Screens.Select return false; } - protected override void Start() + protected override bool OnSelectionFinalised() { - if (player != null) return; + if (player != null) return false; // Ctrl+Enter should start map with autoplay enabled. if (GetContainingInputManager().CurrentState?.Keyboard.ControlPressed == true) @@ -147,7 +147,12 @@ namespace osu.Game.Screens.Select sampleConfirm?.Play(); - LoadComponentAsync(player = new PlayerLoader(new Player()), l => Push(player)); + LoadComponentAsync(player = new PlayerLoader(new Player()), l => + { + if (IsCurrentScreen) Push(player); + }); + + return true; } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 9e5a2fa633..919cfcfbe4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -227,13 +227,18 @@ namespace osu.Game.Screens.Select Push(new Editor()); } - public void Start(BeatmapInfo beatmap) + /// + /// Call to make a selection and perform the default action for this SongSelect. + /// + /// An optional beatmap to override the current carousel selection. + public void FinaliseSelection(BeatmapInfo beatmap = null) { // if we have a pending filter operation, we want to run it now. // it could change selection (ie. if the ruleset has been changed). Carousel.FlushPendingFilterOperations(); - Carousel.SelectBeatmap(beatmap); + if (beatmap != null) + Carousel.SelectBeatmap(beatmap); if (selectionChangedDebounce?.Completed == false) { @@ -242,13 +247,14 @@ namespace osu.Game.Screens.Select selectionChangedDebounce = null; } - Start(); + OnSelectionFinalised(); } /// /// Called when a selection is made. /// - protected abstract void Start(); + /// If a resultant action occurred that takes the user away from SongSelect. + protected abstract bool OnSelectionFinalised(); private ScheduledDelegate selectionChangedDebounce; @@ -339,7 +345,7 @@ namespace osu.Game.Screens.Select logo.Action = () => { - Start(); + FinaliseSelection(); return false; }; } @@ -443,9 +449,16 @@ namespace osu.Game.Screens.Select private void carouselBeatmapsLoaded() { if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false) + { Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo); + } else if (Carousel.SelectedBeatmapSet == null) - Carousel.SelectNextRandom(); + { + if (!Carousel.SelectNextRandom()) + // in the case random selection failed, we want to trigger selectionChanged + // to show the dummy beatmap (we have nothing else to display). + carouselSelectionChanged(null); + } } private void delete(BeatmapSetInfo beatmap) @@ -462,7 +475,7 @@ namespace osu.Game.Screens.Select { case Key.KeypadEnter: case Key.Enter: - Start(); + FinaliseSelection(); return true; case Key.Delete: if (state.Keyboard.ShiftPressed) diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 6a8a361ea0..08f27dfde9 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Tournament { private const string results_filename = "drawings_results.txt"; - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index da139775b1..82248c2cb8 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -19,6 +19,6 @@ namespace osu.Game.Tests.Beatmaps protected override Beatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; - protected override Track GetTrack() => null; + protected override Track GetTrack() => new TrackVirtual(); } } diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index 8984fc843f..8b93be9bb5 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -7,6 +7,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Caching; +using osu.Framework.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -203,6 +204,8 @@ namespace osu.Game.Tests.Visual private readonly FillFlowContainer scores; private APIAccess api; + private readonly Bindable currentBeatmap = new Bindable(); + public PerformanceList() { RelativeSizeAxes = Axes.X; @@ -231,12 +234,15 @@ namespace osu.Game.Tests.Visual }; } - osuGame.Beatmap.ValueChanged += beatmapChanged; + currentBeatmap.ValueChanged += beatmapChanged; + currentBeatmap.BindTo(osuGame.Beatmap); } private GetScoresRequest lastRequest; private void beatmapChanged(WorkingBeatmap newBeatmap) { + if (!IsAlive) return; + lastRequest?.Cancel(); scores.Clear(); diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 106f0fa8f3..933781890f 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -22,6 +22,8 @@ namespace osu.Game.Tests.Visual protected Player Player; + private TestWorkingBeatmap working; + /// /// Create a TestCase which runs through the Player screen. /// @@ -75,7 +77,7 @@ namespace osu.Game.Tests.Visual var instance = r.CreateInstance(); - WorkingBeatmap working = new TestWorkingBeatmap(beatmap); + working = new TestWorkingBeatmap(beatmap); working.Mods.Value = new[] { instance.GetAllMods().First(m => m is ModNoFail) }; if (Player != null) @@ -88,10 +90,21 @@ namespace osu.Game.Tests.Visual return player; } + protected override void Update() + { + base.Update(); + + if (working != null) + // note that this will override any mod rate application + working.Track.Rate = Clock.Rate; + } + protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) => new Player { InitialBeatmap = beatmap, - AllowPause = false + AllowPause = false, + AllowLeadIn = false, + AllowResults = false, }; private const string test_beatmap_data = diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs index 0c0d12c1cc..46ddaee637 100644 --- a/osu.Game/Users/Country.cs +++ b/osu.Game/Users/Country.cs @@ -6,6 +6,7 @@ using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; @@ -26,36 +27,30 @@ namespace osu.Game.Users public string FlagName; } - public class DrawableFlag : Container + public class DrawableFlag : Container, IHasTooltip { private readonly Sprite sprite; private TextureStore textures; - private string flagName; - public string FlagName + private Country country; + public Country Country { - get { return flagName; } + get { return country; } set { - if (value == flagName) return; - flagName = value; - sprite.Texture = textures.Get($@"Flags/{flagName}"); + if (value == country) + return; + + country = value; + sprite.Texture = getFlagTexture(); } } - [BackgroundDependencyLoader] - private void load(TextureStore ts) - { - if (ts == null) - throw new ArgumentNullException(nameof(ts)); + public string TooltipText => country?.FullName; - textures = ts; - sprite.Texture = textures.Get($@"Flags/{flagName}"); - } - - public DrawableFlag(string name = null) + public DrawableFlag(Country country = null) { - flagName = name ?? @"__"; + this.country = country; Children = new Drawable[] { @@ -65,5 +60,17 @@ namespace osu.Game.Users }, }; } + + [BackgroundDependencyLoader] + private void load(TextureStore ts) + { + if (ts == null) + throw new ArgumentNullException(nameof(ts)); + + textures = ts; + sprite.Texture = getFlagTexture(); + } + + private Texture getFlagTexture() => textures.Get($@"Flags/{country?.FlagName ?? @"__"}"); } } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index a2cc8e8d49..e0a4e3184d 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -114,7 +114,7 @@ namespace osu.Game.Users Spacing = new Vector2(5f, 0f), Children = new Drawable[] { - new DrawableFlag(user.Country?.FlagName) + new DrawableFlag(user.Country) { Width = 30f, RelativeSizeAxes = Axes.Y, From e8ef21c0a441778a5f0a5e3bdc8c44c47abd67cb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Jan 2018 12:09:33 +0900 Subject: [PATCH 033/537] Update submodules --- osu-framework | 2 +- osu-resources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 4b819607f3..15fd489d2b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 4b819607f36f6a87472c7d654e02f2c8e83fac70 +Subproject commit 15fd489d2b4dc1d71a6b98b3001c84f1e60c7c10 diff --git a/osu-resources b/osu-resources index c79d917605..4cc3e92d55 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit c79d917605fa792a5f046fd70d7aa75e58dc9fbc +Subproject commit 4cc3e92d5576c706060e3d1589bd2a81cf20bfdb From 1d3e1bdc1da3b88980ba83a70292c00c1b8f4bbe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 8 Feb 2018 23:37:16 +0900 Subject: [PATCH 034/537] Update submodules --- osu-framework | 2 +- osu-resources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 15fd489d2b..6febfb7329 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 15fd489d2b4dc1d71a6b98b3001c84f1e60c7c10 +Subproject commit 6febfb7329cd8092c1e9425f0190d3d91b7a2ba6 diff --git a/osu-resources b/osu-resources index 4cc3e92d55..883ff04e79 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 4cc3e92d5576c706060e3d1589bd2a81cf20bfdb +Subproject commit 883ff04e79a76f5eb1f89c4f59bd7476a803b42b From 6fe62de26471fffed4365c999033a2fd1c368f78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Feb 2018 12:44:41 +0900 Subject: [PATCH 035/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 6febfb7329..86079714a5 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 6febfb7329cd8092c1e9425f0190d3d91b7a2ba6 +Subproject commit 86079714a5f5c2678c4cc91df6c0257f33c43bcf From 0dab70cc05be0711ba0f4efa6ebcf89f8ca9888b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Feb 2018 12:48:22 +0900 Subject: [PATCH 036/537] Fix license header --- osu.Game/Utils/ZipUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/ZipUtils.cs b/osu.Game/Utils/ZipUtils.cs index c9a16eb84b..ad22359c20 100644 --- a/osu.Game/Utils/ZipUtils.cs +++ b/osu.Game/Utils/ZipUtils.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; From 31cf00e3b83f04aee8c2d28325239f0b1060431d Mon Sep 17 00:00:00 2001 From: Poyo Date: Sun, 25 Feb 2018 23:52:38 -0800 Subject: [PATCH 037/537] Implement mania star difficulty calculation --- .../ManiaDifficultyCalculator.cs | 124 +++++++++++++++++- .../Objects/ManiaHitObjectDifficulty.cs | 114 ++++++++++++++++ .../osu.Game.Rulesets.Mania.csproj | 1 + 3 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index 75a8543548..62d2929f27 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -4,18 +4,140 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using System; using System.Collections.Generic; namespace osu.Game.Rulesets.Mania { public class ManiaDifficultyCalculator : DifficultyCalculator { + private const double star_scaling_factor = 0.018; + + /// + /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step. + /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. + /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. + /// + protected const double strain_step = 400; + + /// + /// The weighting of each strain value decays to this number * it's previous value + /// + protected const double decay_weight = 0.9; + + /// + /// HitObjects are stored as a member variable. + /// + private readonly List difficultyHitObjects = new List(); + public ManiaDifficultyCalculator(Beatmap beatmap) : base(beatmap) { } - public override double Calculate(Dictionary categoryDifficulty = null) => 0; + public override double Calculate(Dictionary categoryDifficulty = null) + { + // Fill our custom DifficultyHitObject class, that carries additional information + difficultyHitObjects.Clear(); + + int columnCount = (Beatmap as ManiaBeatmap).TotalColumns; + + foreach (var hitObject in Beatmap.HitObjects) + difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount)); + + // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. + difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); + + if (!calculateStrainValues()) + return 0; + + double starRating = calculateDifficulty() * star_scaling_factor; + + if (categoryDifficulty != null) + { + categoryDifficulty.Add("Strain", starRating); + // categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate); + } + + return starRating; + } + + private bool calculateStrainValues() + { + // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. + using (List.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) + { + if (!hitObjectsEnumerator.MoveNext()) + return false; + + ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current; + + // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. + while (hitObjectsEnumerator.MoveNext()) + { + var next = hitObjectsEnumerator.Current; + next?.CalculateStrains(current, TimeRate); + current = next; + } + + return true; + } + } + + private double calculateDifficulty() + { + double actualStrainStep = strain_step * TimeRate; + + // Find the highest strain value within each strain step + List highestStrains = new List(); + double intervalEndTime = actualStrainStep; + double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval + + ManiaHitObjectDifficulty previousHitObject = null; + foreach (var hitObject in difficultyHitObjects) + { + // While we are beyond the current interval push the currently available maximum to our strain list + while (hitObject.BaseHitObject.StartTime > intervalEndTime) + { + highestStrains.Add(maximumStrain); + + // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay + // until the beginning of the next interval. + if (previousHitObject == null) + { + maximumStrain = 0; + } + else + { + double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); + double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); + maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay; + } + + // Go to the next time interval + intervalEndTime += actualStrainStep; + } + + // Obtain maximum strain + double strain = hitObject.IndividualStrain + hitObject.OverallStrain; + maximumStrain = Math.Max(strain, maximumStrain); + + previousHitObject = hitObject; + } + + // Build the weighted sum over the highest strains for each interval + double difficulty = 0; + double weight = 1; + highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + foreach (double strain in highestStrains) + { + difficulty += weight * strain; + weight *= decay_weight; + } + + return difficulty; + } protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap); } diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs new file mode 100644 index 0000000000..e8b47092f6 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs @@ -0,0 +1,114 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using System; + +namespace osu.Game.Rulesets.Mania.Objects +{ + class ManiaHitObjectDifficulty + { + /// + /// Factor by how much individual / overall strain decays per second. + /// + /// + /// These values are results of tweaking a lot and taking into account general feedback. + /// + internal const double INDIVIDUAL_DECAY_BASE = 0.125; + internal const double OVERALL_DECAY_BASE = 0.30; + + internal ManiaHitObject BaseHitObject; + + private int beatmapColumnCount; + + + private double endTime; + private double[] heldUntil; + + /// + /// Measures jacks or more generally: repeated presses of the same button + /// + private double[] individualStrains; + + internal double IndividualStrain + { + get + { + return individualStrains[BaseHitObject.Column]; + } + + set + { + individualStrains[BaseHitObject.Column] = value; + } + } + + /// + /// Measures note density in a way + /// + internal double OverallStrain = 1; + + public ManiaHitObjectDifficulty(ManiaHitObject baseHitObject, int columnCount) + { + BaseHitObject = baseHitObject; + + endTime = (baseHitObject as IHasEndTime)?.EndTime ?? baseHitObject.StartTime; + + beatmapColumnCount = columnCount; + heldUntil = new double[beatmapColumnCount]; + individualStrains = new double[beatmapColumnCount]; + + for (int i = 0; i < beatmapColumnCount; ++i) + { + individualStrains[i] = 0; + heldUntil[i] = 0; + } + } + + internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate) + { + // TODO: Factor in holds + double addition = 1.0; + double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; + double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000); + double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000); + + double holdFactor = 1.0; // Factor to all additional strains in case something else is held + double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly + + // Fill up the heldUntil array + for (int i = 0; i < beatmapColumnCount; ++i) + { + heldUntil[i] = previousHitObject.heldUntil[i]; + + // If there is at least one other overlapping end or note, then we get an addition, buuuuuut... + if (BaseHitObject.StartTime < heldUntil[i] && endTime > heldUntil[i]) + { + holdAddition = 1.0; + } + + // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 + if (endTime == heldUntil[i]) + { + holdAddition = 0; + } + + // We give a slight bonus to everything if something is held meanwhile + if (heldUntil[i] > endTime) + { + holdFactor = 1.25; + } + + // Decay individual strains + individualStrains[i] = previousHitObject.individualStrains[i] * individualDecay; + } + + heldUntil[BaseHitObject.Column] = endTime; + + // Increase individual strain in own column + IndividualStrain += (2.0/* + (double)SpeedMania.Column / 8.0*/) * holdFactor; + + OverallStrain = previousHitObject.OverallStrain * overallDecay + (addition + holdAddition) * holdFactor; + } + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index a2e21e2053..b9c62cf40b 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -114,6 +114,7 @@ + From 96f416fef382bad61da66454de0f8f688ae75d27 Mon Sep 17 00:00:00 2001 From: Poyo Date: Mon, 26 Feb 2018 00:18:54 -0800 Subject: [PATCH 038/537] Update code style Sorry, bot overlords. --- osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs | 8 ++++---- .../Objects/ManiaHitObjectDifficulty.cs | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index 62d2929f27..e1d3b6212f 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -9,7 +9,7 @@ using System.Collections.Generic; namespace osu.Game.Rulesets.Mania { - public class ManiaDifficultyCalculator : DifficultyCalculator + internal class ManiaDifficultyCalculator : DifficultyCalculator { private const double star_scaling_factor = 0.018; @@ -18,12 +18,12 @@ namespace osu.Game.Rulesets.Mania /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. /// - protected const double strain_step = 400; + private const double strain_step = 400; /// /// The weighting of each strain value decays to this number * it's previous value /// - protected const double decay_weight = 0.9; + private const double decay_weight = 0.9; /// /// HitObjects are stored as a member variable. @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania // Fill our custom DifficultyHitObject class, that carries additional information difficultyHitObjects.Clear(); - int columnCount = (Beatmap as ManiaBeatmap).TotalColumns; + int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7; foreach (var hitObject in Beatmap.HitObjects) difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount)); diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs index e8b47092f6..0b5e7d7e4c 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs @@ -6,7 +6,7 @@ using System; namespace osu.Game.Rulesets.Mania.Objects { - class ManiaHitObjectDifficulty + internal class ManiaHitObjectDifficulty { /// /// Factor by how much individual / overall strain decays per second. @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Objects internal ManiaHitObject BaseHitObject; - private int beatmapColumnCount; + private readonly int beatmapColumnCount; private double endTime; @@ -68,7 +68,6 @@ namespace osu.Game.Rulesets.Mania.Objects internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate) { // TODO: Factor in holds - double addition = 1.0; double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000); double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000); @@ -106,9 +105,9 @@ namespace osu.Game.Rulesets.Mania.Objects heldUntil[BaseHitObject.Column] = endTime; // Increase individual strain in own column - IndividualStrain += (2.0/* + (double)SpeedMania.Column / 8.0*/) * holdFactor; + IndividualStrain += 2.0 * holdFactor; - OverallStrain = previousHitObject.OverallStrain * overallDecay + (addition + holdAddition) * holdFactor; + OverallStrain = previousHitObject.OverallStrain * overallDecay + (1.0 + holdAddition) * holdFactor; } } } From fb724ca8a705ac6f093be9c96c7e2f30c41b5a07 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 28 Feb 2018 08:32:30 +0530 Subject: [PATCH 039/537] Make song select ensure current beatmap is always playable in the active ruleset. - Add a to TestCasePlaySongSelect testing this scenario --- .../Visual/TestCasePlaySongSelect.cs | 38 ++++++++++++++--- osu.Game/Screens/Select/SongSelect.cs | 41 ++++++++++++++++++- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index 13b2be9fdb..a4086ea2cd 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.MathUtils; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Screens.Select; @@ -53,10 +54,14 @@ namespace osu.Game.Tests.Visual public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; + + public void SetRuleset(RulesetInfo ruleset) => Ruleset.Value = ruleset; + + public int? RulesetID => Ruleset.Value.ID; } [BackgroundDependencyLoader] - private void load(OsuGameBase game) + private void load(OsuGameBase game, OsuConfigManager config) { TestSongSelect songSelect = null; @@ -113,6 +118,24 @@ namespace osu.Game.Tests.Visual AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); + + AddWaitStep(5); + + AddStep(@"Set unplayable WorkingBeatmap", () => + { + var testMap = manager.GetAllUsableBeatmapSets().First().Beatmaps.First(b => b.RulesetID != 0); + songSelect.SetRuleset(rulesets.AvailableRulesets.First()); + game.Beatmap.Value = manager.GetWorkingBeatmap(testMap); + }); + AddAssert(@"WorkingBeatmap changed to playable ruleset", () => songSelect.RulesetID == 0 && game.Beatmap.Value.BeatmapInfo.RulesetID == 0); + AddStep(@"Disallow beatmap conversion", () => + { + config.GetBindable(OsuSetting.ShowConvertedBeatmaps).Value = false; + game.Beatmap.Value = manager.GetWorkingBeatmap(manager.GetAllUsableBeatmapSets().First().Beatmaps.First()); + }); + loadNewSongSelect(); + AddWaitStep(3); + AddAssert(@"Ruleset matches beatmap", () => songSelect.RulesetID == game.Beatmap.Value.BeatmapInfo.RulesetID); } private BeatmapSetInfo createTestBeatmapSet(int i) @@ -134,7 +157,8 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1234 + i, - Ruleset = rulesets.AvailableRulesets.First(), + Ruleset = rulesets.AvailableRulesets.ElementAt(0), + RulesetID = 0, Path = "normal.osu", Version = "Normal", BaseDifficulty = new BeatmapDifficulty @@ -145,8 +169,9 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1235 + i, - Ruleset = rulesets.AvailableRulesets.First(), - Path = "hard.osu", + Ruleset = rulesets.AvailableRulesets.First(r => r.ID != 0), + RulesetID = 1, + Path = "hard.taiko", Version = "Hard", BaseDifficulty = new BeatmapDifficulty { @@ -156,8 +181,9 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1236 + i, - Ruleset = rulesets.AvailableRulesets.First(), - Path = "insane.osu", + Ruleset = rulesets.AvailableRulesets.ElementAt(2), + RulesetID = 2, + Path = "insane.fruits", Version = "Insane", BaseDifficulty = new BeatmapDifficulty { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index de6847d866..6e1d95d42e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Linq; using System.Threading; using OpenTK; using OpenTK.Input; @@ -9,12 +10,14 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; @@ -63,6 +66,8 @@ namespace osu.Game.Screens.Select private SampleChannel sampleChangeDifficulty; private SampleChannel sampleChangeBeatmap; + private Bindable rulesetConversionAllowed; + private CancellationTokenSource initialAddSetsTask; private DependencyContainer dependencies; @@ -179,7 +184,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours) + private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours, OsuConfigManager config) { dependencies.CacheAs(this); @@ -194,6 +199,8 @@ namespace osu.Game.Screens.Select if (this.beatmaps == null) this.beatmaps = beatmaps; + rulesetConversionAllowed = config.GetBindable(OsuSetting.ShowConvertedBeatmaps); + if (osu != null) Ruleset.BindTo(osu.Ruleset); @@ -217,7 +224,10 @@ namespace osu.Game.Screens.Select Beatmap.ValueChanged += b => { if (IsCurrentScreen) + { Carousel.SelectBeatmap(b?.BeatmapInfo); + ensurePlayableRuleset(); + } }; } @@ -316,6 +326,7 @@ namespace osu.Game.Screens.Select { base.OnEntering(last); + ensurePlayableRuleset(); Content.FadeInFromZero(250); FilterControl.Activate(); } @@ -441,6 +452,34 @@ namespace osu.Game.Screens.Select } } + private void ensurePlayableRuleset() + { + if (Beatmap.IsDefault) + // DummyBeatmap won't be playable anyway + return; + + bool conversionAllowed = rulesetConversionAllowed.Value; + int? currentRuleset = Ruleset.Value.ID; + int beatmapRuleset = Beatmap.Value.BeatmapInfo.RulesetID; + + if (currentRuleset == beatmapRuleset || conversionAllowed && beatmapRuleset == 0) + // Current beatmap is playable, nothing more to do + return; + + // Otherwise, first check if the current beatmapset has any playable beatmaps + BeatmapInfo beatmap = Beatmap.Value.BeatmapSetInfo.Beatmaps?.FirstOrDefault(b => b.RulesetID == currentRuleset || conversionAllowed && b.RulesetID == 0); + + // If it does then update the WorkingBeatmap + if (beatmap != null) + { + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); + return; + } + + // If it doesn't, then update the current ruleset so that the current beatmap is playable + Ruleset.Value = Beatmap.Value.BeatmapInfo.Ruleset; + } + private void onBeatmapSetAdded(BeatmapSetInfo s) => Carousel.UpdateBeatmapSet(s); private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s); private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); From a57dc154f9aa323c6ab367442c417b7bb48363ae Mon Sep 17 00:00:00 2001 From: naoey Date: Sat, 3 Mar 2018 19:54:54 +0530 Subject: [PATCH 040/537] More specific tests. --- .../Visual/TestCasePlaySongSelect.cs | 80 ++++++++++++++++--- osu.Game/Screens/Select/SongSelect.cs | 18 +++-- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index d895080afe..8532962389 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -60,6 +60,14 @@ namespace osu.Game.Tests.Visual public void SetRuleset(RulesetInfo ruleset) => Ruleset.Value = ruleset; public int? RulesetID => Ruleset.Value.ID; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + // Necessary while running tests because gc is moody and uncollected object interferes with OnEntering test + Beatmap.ValueChanged -= WorkingBeatmapChanged; + } } [BackgroundDependencyLoader] @@ -82,6 +90,7 @@ namespace osu.Game.Tests.Visual { if (deleteMaps) { + // TODO: check why this alone doesn't allow import test to run twice in the same session, probably because the delete op is not saved? manager.Delete(manager.GetAllUsableBeatmapSets()); game.Beatmap.SetDefault(); } @@ -93,6 +102,8 @@ namespace osu.Game.Tests.Visual } Add(songSelect = new TestSongSelect()); + + songSelect?.SetRuleset(rulesets.AvailableRulesets.First()); }); loadNewSongSelect(true); @@ -107,6 +118,36 @@ namespace osu.Game.Tests.Visual { for (int i = 0; i < 100; i += 10) manager.Import(createTestBeatmapSet(i)); + + // also import a set which has a single non - osu ruleset beatmap + manager.Import(new BeatmapSetInfo + { + OnlineBeatmapSetID = 1993, + Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(), + Metadata = new BeatmapMetadata + { + OnlineBeatmapSetID = 1993, + // Create random metadata, then we can check if sorting works based on these + Artist = "MONACA " + RNG.Next(0, 9), + Title = "Black Song " + RNG.Next(0, 9), + AuthorString = "Some Guy " + RNG.Next(0, 9), + }, + Beatmaps = new List + { + new BeatmapInfo + { + OnlineBeatmapID = 1994, + Ruleset = rulesets.AvailableRulesets.ElementAt(3), + RulesetID = 3, + Path = "normal.fruits", + Version = "Normal", + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 3.5f, + } + }, + } + }); }); AddWaitStep(3); @@ -121,23 +162,44 @@ namespace osu.Game.Tests.Visual AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); - AddWaitStep(5); + // Test that song select sets a playable beatmap while entering + AddStep(@"Remove song select", () => + { + Remove(songSelect); + songSelect.Dispose(); + songSelect = null; + }); + AddStep(@"Set non-osu beatmap", () => game.Beatmap.Value = manager.GetWorkingBeatmap(manager.GetAllUsableBeatmapSets().First().Beatmaps.First(b => b.RulesetID != 0))); + AddAssert(@"Non-osu beatmap set", () => game.Beatmap.Value.BeatmapInfo.RulesetID != 0); + loadNewSongSelect(); + AddWaitStep(3); + AddAssert(@"osu beatmap set", () => game.Beatmap.Value.BeatmapInfo.RulesetID == 0); - AddStep(@"Set unplayable WorkingBeatmap", () => + // Test that song select changes WorkingBeatmap to be playable in current ruleset when updated externally + AddStep(@"Try set non-osu beatmap", () => { var testMap = manager.GetAllUsableBeatmapSets().First().Beatmaps.First(b => b.RulesetID != 0); songSelect.SetRuleset(rulesets.AvailableRulesets.First()); game.Beatmap.Value = manager.GetWorkingBeatmap(testMap); }); - AddAssert(@"WorkingBeatmap changed to playable ruleset", () => songSelect.RulesetID == 0 && game.Beatmap.Value.BeatmapInfo.RulesetID == 0); - AddStep(@"Disallow beatmap conversion", () => + AddAssert(@"Beatmap changed to osu", () => songSelect.RulesetID == 0 && game.Beatmap.Value.BeatmapInfo.RulesetID == 0); + + // Test that song select updates WorkingBeatmap when ruleset conversion is disabled + AddStep(@"Disable beatmap conversion", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false)); + AddStep(@"Set osu beatmap taiko rs", () => { - config.GetBindable(OsuSetting.ShowConvertedBeatmaps).Value = false; - game.Beatmap.Value = manager.GetWorkingBeatmap(manager.GetAllUsableBeatmapSets().First().Beatmaps.First()); + game.Beatmap.Value = manager.GetWorkingBeatmap(manager.GetAllUsableBeatmapSets().First().Beatmaps.First(b => b.RulesetID == 0)); + songSelect.SetRuleset(rulesets.AvailableRulesets.First(r => r.ID == 1)); }); - loadNewSongSelect(); - AddWaitStep(3); - AddAssert(@"Ruleset matches beatmap", () => songSelect.RulesetID == game.Beatmap.Value.BeatmapInfo.RulesetID); + AddAssert(@"taiko beatmap set", () => songSelect.RulesetID == 1); + + // Test that song select changes the active ruleset when externally set beatmapset has no playable beatmaps + AddStep(@"Set fruits only beatmapset", () => + { + songSelect.SetRuleset(rulesets.AvailableRulesets.First()); + game.Beatmap.Value = manager.GetWorkingBeatmap(manager.QueryBeatmapSet(b => b.OnlineBeatmapSetID == 1993).Beatmaps.First()); + }); + AddAssert(@"Ruleset changed to fruits", () => songSelect.RulesetID == game.Beatmap.Value.BeatmapInfo.RulesetID); } private BeatmapSetInfo createTestBeatmapSet(int i) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 8033f8da8b..d4fd64dcd9 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -221,14 +221,7 @@ namespace osu.Game.Screens.Select Beatmap.DisabledChanged += disabled => Carousel.AllowSelection = !disabled; Beatmap.TriggerChange(); - Beatmap.ValueChanged += b => - { - if (IsCurrentScreen) - { - Carousel.SelectBeatmap(b?.BeatmapInfo); - ensurePlayableRuleset(); - } - }; + Beatmap.ValueChanged += WorkingBeatmapChanged; } public void Edit(BeatmapInfo beatmap) @@ -271,6 +264,15 @@ namespace osu.Game.Screens.Select // We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds. private BeatmapInfo beatmapNoDebounce; + protected void WorkingBeatmapChanged(WorkingBeatmap beatmap) + { + if (IsCurrentScreen) + { + Carousel.SelectBeatmap(beatmap?.BeatmapInfo); + ensurePlayableRuleset(); + } + } + /// /// selection has been changed as the result of interaction with the carousel. /// From c6c55c40edf51afef87b5b1531e7da02ebccdfaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Mar 2018 00:50:41 +0900 Subject: [PATCH 041/537] Rewrite BreakOverlay No longer relies on Schedule calls (could not be rewound). Also no longer sucks. --- osu.Game.Tests/Visual/TestCaseBreakOverlay.cs | 2 +- .../{BreaksOverlay => Break}/BlurredIcon.cs | 10 +- .../ArrowsOverlay.cs => Break/BreakArrows.cs} | 35 ++--- .../InfoContainer.cs => Break/BreakInfo.cs} | 26 ++-- .../InfoLine.cs => Break/BreakInfoLine.cs} | 10 +- .../Play/{BreaksOverlay => Break}/GlowIcon.cs | 12 +- .../LetterboxOverlay.cs | 13 +- .../RemainingTimeCounter.cs | 14 +- .../Play/{BreaksOverlay => }/BreakOverlay.cs | 142 +++++++++--------- osu.Game/Screens/Play/Player.cs | 1 - osu.Game/osu.Game.csproj | 16 +- 11 files changed, 125 insertions(+), 156 deletions(-) rename osu.Game/Screens/Play/{BreaksOverlay => Break}/BlurredIcon.cs (92%) rename osu.Game/Screens/Play/{BreaksOverlay/ArrowsOverlay.cs => Break/BreakArrows.cs} (78%) rename osu.Game/Screens/Play/{BreaksOverlay/InfoContainer.cs => Break/BreakInfo.cs} (62%) rename osu.Game/Screens/Play/{BreaksOverlay/InfoLine.cs => Break/BreakInfoLine.cs} (84%) rename osu.Game/Screens/Play/{BreaksOverlay => Break}/GlowIcon.cs (93%) rename osu.Game/Screens/Play/{BreaksOverlay => Break}/LetterboxOverlay.cs (77%) rename osu.Game/Screens/Play/{BreaksOverlay => Break}/RemainingTimeCounter.cs (70%) rename osu.Game/Screens/Play/{BreaksOverlay => }/BreakOverlay.cs (51%) diff --git a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs index ae24d86325..51b8c61963 100644 --- a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs @@ -3,9 +3,9 @@ using osu.Framework.Timing; using osu.Game.Beatmaps.Timing; -using osu.Game.Screens.Play.BreaksOverlay; using System.Collections.Generic; using NUnit.Framework; +using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual { diff --git a/osu.Game/Screens/Play/BreaksOverlay/BlurredIcon.cs b/osu.Game/Screens/Play/Break/BlurredIcon.cs similarity index 92% rename from osu.Game/Screens/Play/BreaksOverlay/BlurredIcon.cs rename to osu.Game/Screens/Play/Break/BlurredIcon.cs index 5395d7688e..6f47c97f89 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BlurredIcon.cs +++ b/osu.Game/Screens/Play/Break/BlurredIcon.cs @@ -1,13 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Game.Graphics; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using OpenTK; -namespace osu.Game.Screens.Play.BreaksOverlay +namespace osu.Game.Screens.Play.Break { public class BlurredIcon : BufferedContainer { diff --git a/osu.Game/Screens/Play/BreaksOverlay/ArrowsOverlay.cs b/osu.Game/Screens/Play/Break/BreakArrows.cs similarity index 78% rename from osu.Game/Screens/Play/BreaksOverlay/ArrowsOverlay.cs rename to osu.Game/Screens/Play/Break/BreakArrows.cs index 9fdf90bd28..f2a60cdddf 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/ArrowsOverlay.cs +++ b/osu.Game/Screens/Play/Break/BreakArrows.cs @@ -1,18 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; -using OpenTK; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; -using osu.Game.Beatmaps.Timing; +using OpenTK; -namespace osu.Game.Screens.Play.BreaksOverlay +namespace osu.Game.Screens.Play.Break { - public class ArrowsOverlay : VisibilityContainer + public class BreakArrows : CompositeDrawable { - private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; - private const int glow_icon_size = 60; private const int glow_icon_blur_sigma = 10; private const float glow_icon_final_offset = 0.22f; @@ -29,10 +26,10 @@ namespace osu.Game.Screens.Play.BreaksOverlay private readonly BlurredIcon leftBlurredIcon; private readonly BlurredIcon rightBlurredIcon; - public ArrowsOverlay() + public BreakArrows() { RelativeSizeAxes = Axes.Both; - Children = new Drawable[] + InternalChildren = new Drawable[] { leftGlowIcon = new GlowIcon { @@ -82,22 +79,22 @@ namespace osu.Game.Screens.Play.BreaksOverlay }; } - protected override void PopIn() + public void Show(double duration) { - leftGlowIcon.MoveToX(-glow_icon_final_offset, fade_duration, Easing.OutQuint); - rightGlowIcon.MoveToX(glow_icon_final_offset, fade_duration, Easing.OutQuint); + leftGlowIcon.MoveToX(-glow_icon_final_offset, duration, Easing.OutQuint); + rightGlowIcon.MoveToX(glow_icon_final_offset, duration, Easing.OutQuint); - leftBlurredIcon.MoveToX(-blurred_icon_final_offset, fade_duration, Easing.OutQuint); - rightBlurredIcon.MoveToX(blurred_icon_final_offset, fade_duration, Easing.OutQuint); + leftBlurredIcon.MoveToX(-blurred_icon_final_offset, duration, Easing.OutQuint); + rightBlurredIcon.MoveToX(blurred_icon_final_offset, duration, Easing.OutQuint); } - protected override void PopOut() + public void Hide(double duration) { - leftGlowIcon.MoveToX(-glow_icon_offscreen_offset, fade_duration, Easing.OutQuint); - rightGlowIcon.MoveToX(glow_icon_offscreen_offset, fade_duration, Easing.OutQuint); + leftGlowIcon.MoveToX(-glow_icon_offscreen_offset, duration, Easing.OutQuint); + rightGlowIcon.MoveToX(glow_icon_offscreen_offset, duration, Easing.OutQuint); - leftBlurredIcon.MoveToX(-blurred_icon_offscreen_offset, fade_duration, Easing.OutQuint); - rightBlurredIcon.MoveToX(blurred_icon_offscreen_offset, fade_duration, Easing.OutQuint); + leftBlurredIcon.MoveToX(-blurred_icon_offscreen_offset, duration, Easing.OutQuint); + rightBlurredIcon.MoveToX(blurred_icon_offscreen_offset, duration, Easing.OutQuint); } } } diff --git a/osu.Game/Screens/Play/BreaksOverlay/InfoContainer.cs b/osu.Game/Screens/Play/Break/BreakInfo.cs similarity index 62% rename from osu.Game/Screens/Play/BreaksOverlay/InfoContainer.cs rename to osu.Game/Screens/Play/Break/BreakInfo.cs index d7ab4ff2e5..5e011903fe 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/InfoContainer.cs +++ b/osu.Game/Screens/Play/Break/BreakInfo.cs @@ -1,24 +1,21 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Scoring; -using osu.Game.Beatmaps.Timing; +using OpenTK; -namespace osu.Game.Screens.Play.BreaksOverlay +namespace osu.Game.Screens.Play.Break { - public class InfoContainer : VisibilityContainer + public class BreakInfo : Container { - private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; + public PercentageBreakInfoLine AccuracyDisplay; + public BreakInfoLine RankDisplay; + public BreakInfoLine GradeDisplay; - public PercentageInfoLine AccuracyDisplay; - public InfoLine RankDisplay; - public InfoLine GradeDisplay; - - public InfoContainer() + public BreakInfo() { AutoSizeAxes = Axes.Both; Child = new FillFlowContainer @@ -43,16 +40,13 @@ namespace osu.Game.Screens.Play.BreaksOverlay Direction = FillDirection.Vertical, Children = new Drawable[] { - AccuracyDisplay = new PercentageInfoLine("Accuracy"), - RankDisplay = new InfoLine("Rank"), - GradeDisplay = new InfoLine("Grade"), + AccuracyDisplay = new PercentageBreakInfoLine("Accuracy"), + RankDisplay = new BreakInfoLine("Rank"), + GradeDisplay = new BreakInfoLine("Grade"), }, } }, }; } - - protected override void PopIn() => this.FadeIn(fade_duration); - protected override void PopOut() => this.FadeOut(fade_duration); } } diff --git a/osu.Game/Screens/Play/BreaksOverlay/InfoLine.cs b/osu.Game/Screens/Play/Break/BreakInfoLine.cs similarity index 84% rename from osu.Game/Screens/Play/BreaksOverlay/InfoLine.cs rename to osu.Game/Screens/Play/Break/BreakInfoLine.cs index b39eaf1c22..3d96bca1fa 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/InfoLine.cs +++ b/osu.Game/Screens/Play/Break/BreakInfoLine.cs @@ -8,9 +8,9 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -namespace osu.Game.Screens.Play.BreaksOverlay +namespace osu.Game.Screens.Play.Break { - public class InfoLine : Container + public class BreakInfoLine : Container where T : struct { private const int margin = 2; @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay private readonly string prefix; - public InfoLine(string name, string prefix = @"") + public BreakInfoLine(string name, string prefix = @"") { this.prefix = prefix; @@ -71,9 +71,9 @@ namespace osu.Game.Screens.Play.BreaksOverlay } } - public class PercentageInfoLine : InfoLine + public class PercentageBreakInfoLine : BreakInfoLine { - public PercentageInfoLine(string name, string prefix = "") : base(name, prefix) + public PercentageBreakInfoLine(string name, string prefix = "") : base(name, prefix) { } diff --git a/osu.Game/Screens/Play/BreaksOverlay/GlowIcon.cs b/osu.Game/Screens/Play/Break/GlowIcon.cs similarity index 93% rename from osu.Game/Screens/Play/BreaksOverlay/GlowIcon.cs rename to osu.Game/Screens/Play/Break/GlowIcon.cs index bad9df2093..79b39a873a 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/GlowIcon.cs +++ b/osu.Game/Screens/Play/Break/GlowIcon.cs @@ -1,13 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Containers; +using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using OpenTK; -using osu.Framework.Allocation; -namespace osu.Game.Screens.Play.BreaksOverlay +namespace osu.Game.Screens.Play.Break { public class GlowIcon : Container { @@ -16,24 +16,24 @@ namespace osu.Game.Screens.Play.BreaksOverlay public override Vector2 Size { + get { return base.Size; } set { blurredIcon.Size = spriteIcon.Size = value; blurredIcon.ForceRedraw(); } - get { return base.Size; } } public Vector2 BlurSigma { - set { blurredIcon.BlurSigma = value; } get { return blurredIcon.BlurSigma; } + set { blurredIcon.BlurSigma = value; } } public FontAwesome Icon { - set { spriteIcon.Icon = blurredIcon.Icon = value; } get { return spriteIcon.Icon; } + set { spriteIcon.Icon = blurredIcon.Icon = value; } } public GlowIcon() diff --git a/osu.Game/Screens/Play/BreaksOverlay/LetterboxOverlay.cs b/osu.Game/Screens/Play/Break/LetterboxOverlay.cs similarity index 77% rename from osu.Game/Screens/Play/BreaksOverlay/LetterboxOverlay.cs rename to osu.Game/Screens/Play/Break/LetterboxOverlay.cs index f4c9362fff..21eb5ebea0 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/LetterboxOverlay.cs +++ b/osu.Game/Screens/Play/Break/LetterboxOverlay.cs @@ -1,18 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps.Timing; +using OpenTK.Graphics; -namespace osu.Game.Screens.Play.BreaksOverlay +namespace osu.Game.Screens.Play.Break { - public class LetterboxOverlay : VisibilityContainer + public class LetterboxOverlay : CompositeDrawable { - private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; private const int height = 350; private static readonly Color4 transparent_black = new Color4(0, 0, 0, 0); @@ -20,7 +18,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay public LetterboxOverlay() { RelativeSizeAxes = Axes.Both; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Container { @@ -48,8 +46,5 @@ namespace osu.Game.Screens.Play.BreaksOverlay } }; } - - protected override void PopIn() => this.FadeIn(fade_duration); - protected override void PopOut() => this.FadeOut(fade_duration); } } diff --git a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs b/osu.Game/Screens/Play/Break/RemainingTimeCounter.cs similarity index 70% rename from osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs rename to osu.Game/Screens/Play/Break/RemainingTimeCounter.cs index 015fefb423..f6e683f519 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs +++ b/osu.Game/Screens/Play/Break/RemainingTimeCounter.cs @@ -1,18 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics.Sprites; -using osu.Framework.Graphics; using System; -using osu.Game.Beatmaps.Timing; +using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; -namespace osu.Game.Screens.Play.BreaksOverlay +namespace osu.Game.Screens.Play.Break { public class RemainingTimeCounter : Counter { - private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; - private readonly OsuSpriteText counter; public RemainingTimeCounter() @@ -25,13 +22,8 @@ namespace osu.Game.Screens.Play.BreaksOverlay TextSize = 33, Font = "Venera", }; - - Alpha = 0; } protected override void OnCountChanged(double count) => counter.Text = ((int)Math.Ceiling(count / 1000)).ToString(); - - public override void Show() => this.FadeIn(fade_duration); - public override void Hide() => this.FadeOut(fade_duration); } } diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs similarity index 51% rename from osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs rename to osu.Game/Screens/Play/BreakOverlay.cs index af7c1ef5aa..6c7ee596a1 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -1,15 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Scoring; -using System.Collections.Generic; -using osu.Framework.Graphics.UserInterface; +using osu.Game.Screens.Play.Break; -namespace osu.Game.Screens.Play.BreaksOverlay +namespace osu.Game.Screens.Play { public class BreakOverlay : Container { @@ -18,28 +19,26 @@ namespace osu.Game.Screens.Play.BreaksOverlay private const int vertical_margin = 25; private List breaks; + + private readonly Container fadeContainer; + public List Breaks { + get => breaks; set { breaks = value; initializeBreaks(); } - get - { - return breaks; - } } public override bool RemoveCompletedTransforms => false; - private readonly bool letterboxing; - private readonly LetterboxOverlay letterboxOverlay; private readonly Container remainingTimeAdjustmentBox; private readonly Container remainingTimeBox; private readonly RemainingTimeCounter remainingTimeCounter; - private readonly InfoContainer info; - private readonly ArrowsOverlay arrowsOverlay; + private readonly BreakInfo info; + private readonly BreakArrows breakArrows; public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor) : this(letterboxing) @@ -49,61 +48,72 @@ namespace osu.Game.Screens.Play.BreaksOverlay public BreakOverlay(bool letterboxing) { - this.letterboxing = letterboxing; - RelativeSizeAxes = Axes.Both; - Children = new Drawable[] + Child = fadeContainer = new Container { - letterboxOverlay = new LetterboxOverlay + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - remainingTimeAdjustmentBox = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Width = 0, - Child = remainingTimeBox = new Container + new LetterboxOverlay + { + Alpha = letterboxing ? 1 : 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + remainingTimeAdjustmentBox = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Height = 8, - CornerRadius = 4, - Masking = true, - Child = new Box { RelativeSizeAxes = Axes.Both } + Width = 0, + Child = remainingTimeBox = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Height = 8, + CornerRadius = 4, + Masking = true, + Child = new Box { RelativeSizeAxes = Axes.Both } + } + }, + remainingTimeCounter = new RemainingTimeCounter + { + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + Margin = new MarginPadding { Bottom = vertical_margin }, + }, + info = new BreakInfo + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = vertical_margin }, + }, + breakArrows = new BreakArrows + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, } - }, - remainingTimeCounter = new RemainingTimeCounter - { - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - Margin = new MarginPadding { Bottom = vertical_margin }, - }, - info = new InfoContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = vertical_margin }, - }, - arrowsOverlay = new ArrowsOverlay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, } }; } + protected override void LoadComplete() + { + base.LoadComplete(); + initializeBreaks(); + } + private void initializeBreaks() { + if (!IsLoaded) return; // we need a clock. + FinishTransforms(true); Scheduler.CancelDelayedTasks(); - if (breaks == null) - return; + if (breaks == null) return; //we need breaks. foreach (var b in breaks) { @@ -112,6 +122,9 @@ namespace osu.Game.Screens.Play.BreaksOverlay using (BeginAbsoluteSequence(b.StartTime, true)) { + fadeContainer.FadeIn(fade_duration); + breakArrows.Show(fade_duration); + remainingTimeAdjustmentBox .ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint) .Delay(b.Duration - fade_duration) @@ -123,37 +136,16 @@ namespace osu.Game.Screens.Play.BreaksOverlay .ResizeWidthTo(1); remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration); - } - using (BeginAbsoluteSequence(b.StartTime)) - { - Schedule(showBreak); - using (BeginDelayedSequence(b.Duration - fade_duration)) - Schedule(hideBreak); + using (BeginDelayedSequence(b.Duration - fade_duration, true)) + { + fadeContainer.FadeOut(fade_duration); + breakArrows.Hide(fade_duration); + } } } } - private void showBreak() - { - if (letterboxing) - letterboxOverlay.Show(); - - remainingTimeCounter.Show(); - info.Show(); - arrowsOverlay.Show(); - } - - private void hideBreak() - { - if (letterboxing) - letterboxOverlay.Hide(); - - remainingTimeCounter.Hide(); - info.Hide(); - arrowsOverlay.Hide(); - } - private void bindProcessor(ScoreProcessor processor) { info.AccuracyDisplay.Current.BindTo(processor.Accuracy); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7a0c723ab5..84f6cd606a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -26,7 +26,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Backgrounds; -using osu.Game.Screens.Play.BreaksOverlay; using osu.Game.Screens.Ranking; using osu.Game.Storyboards.Drawables; using OpenTK; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ff365ad93e..429e7c7b27 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -395,14 +395,14 @@ - - - - - - - - + + + + + + + + From 7d11e55d0610c1999072f05c837dca74cb68b910 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Mar 2018 19:43:33 +0900 Subject: [PATCH 042/537] Only block mouse input when the shaded portion of the SkipButton is hovered --- osu.Game/Screens/Play/SkipButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index 08bb26c72b..c77061e5d9 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -36,6 +36,8 @@ namespace osu.Game.Screens.Play public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + protected override bool BlockPassThroughMouse => fadeContainer.IsHovered; + public SkipButton(double startTime) { this.startTime = startTime; From 53edfedfc8e86a9ebd3fb2723ec2cbab5fd60a40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Mar 2018 19:43:57 +0900 Subject: [PATCH 043/537] Remove unneeded HighFrequencyMousePosition from GameplayMenuOverlay --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 615c124ea7..29b68abc21 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -18,7 +18,7 @@ using System.Collections.Generic; namespace osu.Game.Screens.Play { - public abstract class GameplayMenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition + public abstract class GameplayMenuOverlay : OverlayContainer { private const int transition_duration = 200; private const int button_height = 70; From 60f851df3e092f74e596753b9e6d8eb385dbf4e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Mar 2018 19:45:49 +0900 Subject: [PATCH 044/537] Add support for DrawInfo alpha when drawing CursorTrail --- .../UI/Cursor/CursorTrail.cs | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 37ca0c021b..c56cd46131 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -3,9 +3,9 @@ using System; using System.Diagnostics; +using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Timing; using OpenTK; +using OpenTK.Graphics; using OpenTK.Graphics.ES30; namespace osu.Game.Rulesets.Osu.UI.Cursor @@ -115,14 +116,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor protected override bool OnMouseMove(InputState state) { + Vector2 pos = state.Mouse.NativeState.Position; + if (lastPosition == null) { - lastPosition = state.Mouse.NativeState.Position; + lastPosition = pos; resampler.AddPosition(lastPosition.Value); return base.OnMouseMove(state); } - foreach (Vector2 pos2 in resampler.AddPosition(state.Mouse.NativeState.Position)) + foreach (Vector2 pos2 in resampler.AddPosition(pos)) { Trace.Assert(lastPosition.HasValue); @@ -162,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private class TrailDrawNodeSharedData { - public VertexBuffer VertexBuffer; + public VertexBuffer VertexBuffer; } private class TrailDrawNode : DrawNode @@ -188,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public override void Draw(Action vertexAction) { if (Shared.VertexBuffer == null) - Shared.VertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw); + Shared.VertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw); Shader.GetUniform("g_FadeClock").Value = Time; @@ -205,17 +208,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor int end = start; Vector2 pos = Parts[i].Position; - ColourInfo colour = DrawInfo.Colour; - colour.TopLeft.Linear.A = Parts[i].Time + colour.TopLeft.Linear.A; - colour.TopRight.Linear.A = Parts[i].Time + colour.TopRight.Linear.A; - colour.BottomLeft.Linear.A = Parts[i].Time + colour.BottomLeft.Linear.A; - colour.BottomRight.Linear.A = Parts[i].Time + colour.BottomRight.Linear.A; + float time = Parts[i].Time; Texture.DrawQuad( new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y), - colour, + DrawInfo.Colour, null, - v => Shared.VertexBuffer.Vertices[end++] = v); + v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex + { + Position = v.Position, + TexturePosition = v.TexturePosition, + Time = time + 1, + Colour = v.Colour, + }); Parts[i].WasUpdated = false; } @@ -240,5 +245,26 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Shader.Unbind(); } } + + [StructLayout(LayoutKind.Sequential)] + public struct TexturedTrailVertex : IEquatable, IVertex + { + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 Position; + [VertexMember(4, VertexAttribPointerType.Float)] + public Color4 Colour; + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 TexturePosition; + [VertexMember(1, VertexAttribPointerType.Float)] + public float Time; + + public bool Equals(TexturedTrailVertex other) + { + return Position.Equals(other.Position) + && TexturePosition.Equals(other.TexturePosition) + && Colour.Equals(other.Colour) + && Time.Equals(other.Time); + } + } } } From 4da30c6940a279b45ea4dd86ecf781793fb54618 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Mar 2018 19:05:30 +0900 Subject: [PATCH 045/537] Fix missing conversion case --- .../Patterns/Legacy/DistanceObjectPatternGenerator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index a102781e70..29b8aaa51b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -396,13 +396,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy // Create the hold note addToPattern(pattern, holdColumn, startTime, endTime); - int noteCount = 1; + int noteCount; if (ConversionDifficulty > 6.5) noteCount = GetRandomNoteCount(0.63, 0); else if (ConversionDifficulty > 4) noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0); else if (ConversionDifficulty > 2.5) noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0); + else + noteCount = 0; noteCount = Math.Min(TotalColumns - 1, noteCount); bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); From 69c0e95d9d987de074b3bb95e45b53fe422c56ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Mar 2018 20:06:08 +0900 Subject: [PATCH 046/537] Use a better fade effect for the gameplay cursor --- .../UI/Cursor/GameplayCursor.cs | 95 +++++++++++-------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 0aeb14514d..34940a084a 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -20,13 +20,66 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { protected override Drawable CreateCursor() => new OsuCursor(); + protected override Container Content => fadeContainer; + + private readonly Container fadeContainer; + public GameplayCursor() { - Add(new CursorTrail { Depth = 1 }); + InternalChild = fadeContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new CursorTrail { Depth = 1 } + } + }; } private int downCount; + public bool OnPressed(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + downCount++; + ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); + break; + } + + return false; + } + + public bool OnReleased(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + if (--downCount == 0) + ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); + break; + } + + return false; + } + + public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. + + protected override void PopIn() + { + fadeContainer.FadeTo(1, 300, Easing.OutQuint); + ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); + } + + protected override void PopOut() + { + fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); + ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint); + } + public class OsuCursor : Container { private Container cursorContainer; @@ -131,45 +184,5 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor cursorContainer.Scale = new Vector2(scale); } } - - public bool OnPressed(OsuAction action) - { - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - downCount++; - ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); - break; - } - - return false; - } - - public bool OnReleased(OsuAction action) - { - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - if (--downCount == 0) - ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); - break; - } - - return false; - } - - protected override void PopIn() - { - ActiveCursor.FadeTo(1, 250, Easing.OutQuint); - ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); - } - - protected override void PopOut() - { - ActiveCursor.FadeTo(0, 250, Easing.OutQuint); - ActiveCursor.ScaleTo(0.6f, 250, Easing.In); - } } } From f4965ee7d6d4c72a4f2a6ee24fb7fb6916b1990e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Mar 2018 20:06:25 +0900 Subject: [PATCH 047/537] Use high precision updating on CursorTrail Avoids bloackage --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index c56cd46131..d9a6b81f6b 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -19,7 +19,7 @@ using OpenTK.Graphics.ES30; namespace osu.Game.Rulesets.Osu.UI.Cursor { - internal class CursorTrail : Drawable + internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition { private int currentIndex; From 2bd58e54213e89088fba3754feb8b0b9c2814c58 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Mar 2018 19:06:43 +0900 Subject: [PATCH 048/537] Re-order RNG call to match osu-stable --- .../Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 29b8aaa51b..ce2c679bf3 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -396,6 +396,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy // Create the hold note addToPattern(pattern, holdColumn, startTime, endTime); + int nextColumn = Random.Next(RandomStart, TotalColumns); int noteCount; if (ConversionDifficulty > 6.5) noteCount = GetRandomNoteCount(0.63, 0); @@ -408,7 +409,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy noteCount = Math.Min(TotalColumns - 1, noteCount); bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); - int nextColumn = Random.Next(RandomStart, TotalColumns); var rowPattern = new Pattern(); for (int i = 0; i <= spanCount; i++) From 3ddaf1879d56a208834b36a1b9dd9ca6994760ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Mar 2018 20:11:27 +0900 Subject: [PATCH 049/537] CursorTrail always present, just to be safe --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index d9a6b81f6b..fed2105f21 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -32,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private float time; + public override bool IsPresent => true; + private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); private const int max_sprites = 2048; From dfeee79a249a2b4c9df4abefd562d92314ac96e3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Mar 2018 19:35:35 +0900 Subject: [PATCH 050/537] Fix incorrect probability --- .../Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index c4ef23a982..98d79d3f5c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -356,7 +356,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy break; case 3: centreProbability = Math.Max(centreProbability, 0.03); - p2 = Math.Max(p2, 0.1); + p2 = 0; p3 = 0; break; case 4: From 675c7d0dfd301bcb2b8500541cb3f21c0d5777d0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Mar 2018 19:35:57 +0900 Subject: [PATCH 051/537] Invert Max/Mins --- .../Legacy/DistanceObjectPatternGenerator.cs | 12 +++++------ .../Legacy/HitObjectPatternGenerator.cs | 20 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index ce2c679bf3..c7d7fc9a07 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -305,19 +305,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy p4 = 0; break; case 3: - p2 = Math.Max(p2, 0.1); + p2 = Math.Min(p2, 0.1); p3 = 0; p4 = 0; break; case 4: - p2 = Math.Max(p2, 0.3); - p3 = Math.Max(p3, 0.04); + p2 = Math.Min(p2, 0.3); + p3 = Math.Min(p3, 0.04); p4 = 0; break; case 5: - p2 = Math.Max(p2, 0.34); - p3 = Math.Max(p3, 0.1); - p4 = Math.Max(p4, 0.03); + p2 = Math.Min(p2, 0.34); + p3 = Math.Min(p3, 0.1); + p4 = Math.Min(p4, 0.03); break; } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 98d79d3f5c..c2ec4d9645 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -308,20 +308,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy p5 = 0; break; case 3: - p2 = Math.Max(p2, 0.1); + p2 = Math.Min(p2, 0.1); p3 = 0; p4 = 0; p5 = 0; break; case 4: - p2 = Math.Max(p2, 0.23); - p3 = Math.Max(p3, 0.04); + p2 = Math.Min(p2, 0.23); + p3 = Math.Min(p3, 0.04); p4 = 0; p5 = 0; break; case 5: - p3 = Math.Max(p3, 0.15); - p4 = Math.Max(p4, 0.03); + p3 = Math.Min(p3, 0.15); + p4 = Math.Min(p4, 0.03); p5 = 0; break; } @@ -355,23 +355,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy p3 = 0; break; case 3: - centreProbability = Math.Max(centreProbability, 0.03); + centreProbability = Math.Min(centreProbability, 0.03); p2 = 0; p3 = 0; break; case 4: centreProbability = 0; - p2 = Math.Max(p2 * 2, 0.2); + p2 = Math.Min(p2 * 2, 0.2); p3 = 0; break; case 5: - centreProbability = Math.Max(centreProbability, 0.03); + centreProbability = Math.Min(centreProbability, 0.03); p3 = 0; break; case 6: centreProbability = 0; - p2 = Math.Max(p2 * 2, 0.5); - p3 = Math.Max(p3 * 2, 0.15); + p2 = Math.Min(p2 * 2, 0.5); + p3 = Math.Min(p3 * 2, 0.15); break; } From 356d353cead3f2568bba01f68a2fcd3ba72d11ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Mar 2018 20:14:06 +0900 Subject: [PATCH 052/537] Fix ConversionDifficulty never actually being calculated --- .../Beatmaps/ManiaBeatmapConverter.cs | 15 +++++------ .../Legacy/DistanceObjectPatternGenerator.cs | 5 ++-- .../Legacy/EndTimeObjectPatternGenerator.cs | 5 ++-- .../Legacy/HitObjectPatternGenerator.cs | 5 ++-- .../Patterns/Legacy/PatternGenerator.cs | 27 ++++++++++++------- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 9922d4c8ad..595027f3ca 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps yield break; } - var objects = IsForCurrentRuleset ? generateSpecific(original) : generateConverted(original); + var objects = IsForCurrentRuleset ? generateSpecific(original) : generateConverted(original, beatmap); if (objects == null) yield break; @@ -125,26 +125,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// Method that generates hit objects for non-osu!mania beatmaps. /// /// The original hit object. + /// The original beatmap. This is used /// The hit objects generated. - private IEnumerable generateConverted(HitObject original) + private IEnumerable generateConverted(HitObject original, Beatmap originalBeatmap) { var endTimeData = original as IHasEndTime; var distanceData = original as IHasDistance; var positionData = original as IHasPosition; - // Following lines currently commented out to appease resharper - Patterns.PatternGenerator conversion = null; if (distanceData != null) - conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern); + conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap); else if (endTimeData != null) - conversion = new EndTimeObjectPatternGenerator(random, original, beatmap); + conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap); else if (positionData != null) { computeDensity(original.StartTime); - conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair); + conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap); recordNote(original.StartTime, positionData.Position); } @@ -167,7 +166,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator { public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) - : base(random, hitObject, beatmap, previousPattern) + : base(random, hitObject, beatmap, previousPattern, null) { } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index c7d7fc9a07..a3d784b28a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -29,8 +30,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private PatternType convertType; - public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) - : base(random, hitObject, beatmap, previousPattern) + public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) + : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { convertType = PatternType.None; if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 278a4c4aab..ffbabba75a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using System.Linq; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Objects; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy @@ -15,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { private readonly double endTime; - public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap) - : base(random, hitObject, beatmap, new Pattern()) + public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap) + : base(random, hitObject, beatmap, new Pattern(), originalBeatmap) { var endtimeData = HitObject as IHasEndTime; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index c2ec4d9645..e126534c54 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using OpenTK; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; @@ -19,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly PatternType convertType; - public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair) - : base(random, hitObject, beatmap, previousPattern) + public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap) + : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime)); if (density < 0) throw new ArgumentOutOfRangeException(nameof(density)); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 5f98749f0c..501950cdcd 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -25,14 +25,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// protected readonly FastRandom Random; - protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) + /// + /// The beatmap which is being converted from. + /// + protected readonly Beatmap OriginalBeatmap; + + protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) : base(hitObject, beatmap, previousPattern) { if (random == null) throw new ArgumentNullException(nameof(random)); - if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); - if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern)); + if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap)); Random = random; + OriginalBeatmap = originalBeatmap; + RandomStart = TotalColumns == 8 ? 1 : 0; } @@ -94,17 +100,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (conversionDifficulty != null) return conversionDifficulty.Value; - HitObject lastObject = Beatmap.HitObjects.LastOrDefault(); - HitObject firstObject = Beatmap.HitObjects.FirstOrDefault(); + HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault(); + HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault(); double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0); - drainTime -= Beatmap.TotalBreakTime; + drainTime -= OriginalBeatmap.TotalBreakTime; if (drainTime == 0) - drainTime = 10000; + drainTime = 10000000; - BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.BaseDifficulty; - conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; + // We need this in seconds + drainTime /= 1000; + + BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; + conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); return conversionDifficulty.Value; From 4be478d38eccc878596b46771ba78ca90fd8671c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Mar 2018 20:14:34 +0900 Subject: [PATCH 053/537] Fix LowProbability conversions happening during kiai time --- .../Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index a3d784b28a..28cf119833 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { convertType = PatternType.None; - if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) + if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) convertType = PatternType.LowProbability; var distanceData = hitObject as IHasDistance; From 27a510aad8bf7715e59a29cb2df4c351787f4cfb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Mar 2018 20:16:11 +0900 Subject: [PATCH 054/537] The endtime-object pattern is never checked against --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 595027f3ca..2dd3468df0 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -152,10 +152,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return null; Pattern newPattern = conversion.Generate(); - lastPattern = newPattern; - var stairPatternGenerator = conversion as HitObjectPatternGenerator; - lastStair = stairPatternGenerator?.StairType ?? lastStair; + lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern; + lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair; return newPattern.HitObjects; } From 02265ad686625583982e19142bcd4f82ae31955b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Mar 2018 20:16:54 +0900 Subject: [PATCH 055/537] Enable mania's basic conversion testcase --- osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs index 2095addc72..9d55ab643d 100644 --- a/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Tests private bool isForCurrentRuleset; [NonParallelizable] - [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2150")] + [TestCase("basic", false)] public void Test(string name, bool isForCurrentRuleset) { this.isForCurrentRuleset = isForCurrentRuleset; From 792a3ac469f758f37df40177a90599082ad859b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Mar 2018 00:10:53 +0900 Subject: [PATCH 056/537] SkipButton -> SkipOverlay --- osu.Game.Tests/Visual/TestCaseSkipButton.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/{SkipButton.cs => SkipOverlay.cs} | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game/Screens/Play/{SkipButton.cs => SkipOverlay.cs} (95%) diff --git a/osu.Game.Tests/Visual/TestCaseSkipButton.cs b/osu.Game.Tests/Visual/TestCaseSkipButton.cs index a4d2019cd7..df94d5147f 100644 --- a/osu.Game.Tests/Visual/TestCaseSkipButton.cs +++ b/osu.Game.Tests/Visual/TestCaseSkipButton.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual { base.LoadComplete(); - Add(new SkipButton(Clock.CurrentTime + 5000)); + Add(new SkipOverlay(Clock.CurrentTime + 5000)); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ad803ebc44..7f8881f463 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -183,7 +183,7 @@ namespace osu.Game.Screens.Play Alpha = 0, }, RulesetContainer, - new SkipButton(firstObjectTime) + new SkipOverlay(firstObjectTime) { Clock = Clock, // skip button doesn't want to use the audio clock directly ProcessCustomClock = false, diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipOverlay.cs similarity index 95% rename from osu.Game/Screens/Play/SkipButton.cs rename to osu.Game/Screens/Play/SkipOverlay.cs index c77061e5d9..5418a6c416 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -21,7 +21,7 @@ using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play { - public class SkipButton : OverlayContainer, IKeyBindingHandler + public class SkipOverlay : OverlayContainer, IKeyBindingHandler { private readonly double startTime; @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play protected override bool BlockPassThroughMouse => fadeContainer.IsHovered; - public SkipButton(double startTime) + public SkipOverlay(double startTime) { this.startTime = startTime; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 37e304d62d..406e251899 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -801,7 +801,7 @@ - + From d115c56742927401a82802bb0a21e5549e86f059 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Mar 2018 00:27:55 +0900 Subject: [PATCH 057/537] Avoid using input blocking --- osu.Game/Screens/Play/SkipOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 5418a6c416..923084c9e7 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -35,8 +35,7 @@ namespace osu.Game.Screens.Play private double displayTime; public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - - protected override bool BlockPassThroughMouse => fadeContainer.IsHovered; + protected override bool BlockPassThroughMouse => false; public SkipOverlay(double startTime) { @@ -276,7 +275,7 @@ namespace osu.Game.Screens.Play flow.TransformSpacingTo(new Vector2(5), 500, Easing.OutQuint); box.FadeColour(colourHover, 500, Easing.OutQuint); background.FadeTo(0.4f, 500, Easing.OutQuint); - return base.OnHover(state); + return true; } protected override void OnHoverLost(InputState state) From 4aafc2228ecacf89b477f90bcbc7d60e0c192f3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Mar 2018 00:53:40 +0900 Subject: [PATCH 058/537] Improve skip button behaviour when mouse buttons are down --- osu.Game/Screens/Play/SkipOverlay.cs | 37 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 923084c9e7..19ee0cb989 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -52,12 +52,6 @@ namespace osu.Game.Screens.Play Origin = Anchor.Centre; } - protected override bool OnMouseMove(InputState state) - { - fadeContainer.State = Visibility.Visible; - return base.OnMouseMove(state); - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -122,15 +116,9 @@ namespace osu.Game.Screens.Play Expire(); } - protected override void PopIn() - { - this.FadeIn(); - } + protected override void PopIn() => this.FadeIn(); - protected override void PopOut() - { - this.FadeOut(); - } + protected override void PopOut() => this.FadeOut(); protected override void Update() { @@ -138,6 +126,13 @@ namespace osu.Game.Screens.Play remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint); } + protected override bool OnMouseMove(InputState state) + { + if (!state.Mouse.HasAnyButtonPressed) + fadeContainer.State = Visibility.Visible; + return base.OnMouseMove(state); + } + public bool OnPressed(GlobalAction action) { switch (action) @@ -177,7 +172,7 @@ namespace osu.Game.Screens.Play if (stateChanged) this.FadeIn(500, Easing.OutExpo); - if (!IsHovered) + if (!IsHovered && !IsDragged) using (BeginDelayedSequence(1000)) scheduledHide = Schedule(() => State = Visibility.Hidden); break; @@ -195,6 +190,18 @@ namespace osu.Game.Screens.Play base.LoadComplete(); State = Visibility.Visible; } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + scheduledHide?.Cancel(); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + State = Visibility.Visible; + return base.OnMouseUp(state, args); + } } private class Button : OsuClickableContainer From e187c6453d841aa393dda610938e858b786951bf Mon Sep 17 00:00:00 2001 From: Poyo Date: Mon, 5 Mar 2018 18:19:06 -0800 Subject: [PATCH 059/537] Added mania-difficulty mod support --- .../ManiaDifficultyCalculator.cs | 15 ++++++++------- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../Objects/ManiaHitObjectDifficulty.cs | 6 +++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index e1d3b6212f..02560c8141 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -4,6 +4,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; @@ -31,8 +32,11 @@ namespace osu.Game.Rulesets.Mania private readonly List difficultyHitObjects = new List(); public ManiaDifficultyCalculator(Beatmap beatmap) - : base(beatmap) - { + : base(beatmap) { + } + + public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods) + : base(beatmap, mods) { } public override double Calculate(Dictionary categoryDifficulty = null) @@ -53,11 +57,8 @@ namespace osu.Game.Rulesets.Mania double starRating = calculateDifficulty() * star_scaling_factor; - if (categoryDifficulty != null) - { - categoryDifficulty.Add("Strain", starRating); - // categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate); - } + categoryDifficulty?.Add("Strain", starRating); + // categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate); return starRating; } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 3bfb4d3e44..ac815e0e2f 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Mania public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods); public override int LegacyID => 3; diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs index 0b5e7d7e4c..2b59279972 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs @@ -22,13 +22,13 @@ namespace osu.Game.Rulesets.Mania.Objects private readonly int beatmapColumnCount; - private double endTime; - private double[] heldUntil; + private readonly double endTime; + private readonly double[] heldUntil; /// /// Measures jacks or more generally: repeated presses of the same button /// - private double[] individualStrains; + private readonly double[] individualStrains; internal double IndividualStrain { From bd952ce370453f7e63ae18153dc56f9098db5c7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Mar 2018 18:20:20 +0900 Subject: [PATCH 060/537] Allow skinnable drawables to be of non-restricted size --- osu.Game/Skinning/LegacySkin.cs | 10 +++------ osu.Game/Skinning/SkinnableDrawable.cs | 29 ++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 17fe6369a7..f03d1ce632 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -32,12 +32,7 @@ namespace osu.Game.Skinning var texture = textures.Get(componentName); if (texture == null) return null; - return new Sprite - { - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Texture = texture, - }; + return new Sprite { Texture = texture }; } public override SampleChannel GetSample(string sampleName) => samples.Get(sampleName); @@ -48,7 +43,8 @@ namespace osu.Game.Skinning private readonly IResourceStore underlyingStore; private string getPathForFile(string filename) => - skin.Files.FirstOrDefault(f => string.Equals(Path.GetFileNameWithoutExtension(f.Filename), filename.Split('/').Last(), StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath; + skin.Files.FirstOrDefault(f => string.Equals(Path.GetFileNameWithoutExtension(f.Filename), filename.Split('/').Last(), StringComparison.InvariantCultureIgnoreCase))?.FileInfo + .StoragePath; public LegacySkinResourceStore(SkinInfo skin, IResourceStore underlyingStore) { diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index cd669778a6..a5f22f60a2 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -3,13 +3,14 @@ using System; using osu.Framework.Graphics; +using OpenTK; namespace osu.Game.Skinning { public class SkinnableDrawable : SkinnableDrawable { - public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true) - : base(name, defaultImplementation, fallback) + public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true, bool restrictSize = true) + : base(name, defaultImplementation, fallback, restrictSize) { } } @@ -21,10 +22,16 @@ namespace osu.Game.Skinning private readonly string componentName; - public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true) : base(fallback) + /// + /// Whether a user-skin drawable should be limited to the size of our parent. + /// + public readonly bool RestrictSize; + + public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true, bool restrictSize = true) : base(fallback) { componentName = name; createDefault = defaultImplementation; + RestrictSize = restrictSize; RelativeSizeAxes = Axes.Both; } @@ -32,11 +39,25 @@ namespace osu.Game.Skinning protected override void SkinChanged(Skin skin, bool allowFallback) { var drawable = skin.GetDrawableComponent(componentName); - if (drawable == null && allowFallback) + if (drawable != null) + { + if (RestrictSize) + { + drawable.RelativeSizeAxes = Axes.Both; + drawable.Size = Vector2.One; + drawable.FillMode = FillMode.Fit; + } + } + else if (allowFallback) drawable = createDefault(componentName); if (drawable != null) + { + drawable.Origin = Anchor.Centre; + drawable.Anchor = Anchor.Centre; + InternalChild = drawable; + } else ClearInternal(); } From 05eb6786548780dd2231f24c466ba84411de1fa2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Mar 2018 18:20:35 +0900 Subject: [PATCH 061/537] Add skin support for judgements --- .../Objects/Drawables/DrawableOsuJudgement.cs | 2 +- .../Rulesets/Judgements/DrawableJudgement.cs | 42 ++++++++++++------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 716f4b629b..6d6cae9ca5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void LoadComplete() { if (Judgement.Result != HitResult.Miss) - JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); + JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); base.LoadComplete(); } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index c1bf55b214..4664517312 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -10,6 +10,8 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Judgements { @@ -18,9 +20,11 @@ namespace osu.Game.Rulesets.Judgements /// public class DrawableJudgement : Container { + private const float judgement_size = 80; + protected readonly Judgement Judgement; - protected readonly SpriteText JudgementText; + protected SpriteText JudgementText; /// /// Creates a drawable which visualises a . @@ -30,31 +34,37 @@ namespace osu.Game.Rulesets.Judgements { Judgement = judgement; - AutoSizeAxes = Axes.Both; - - Children = new[] - { - JudgementText = new OsuSpriteText - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Text = judgement.Result.GetDescription().ToUpper(), - Font = @"Venera", - Scale = new Vector2(0.85f, 1), - TextSize = 12 - } - }; + Size = new Vector2(judgement_size); } [BackgroundDependencyLoader] private void load(OsuColour colours) { + string legacyName = string.Empty; switch (Judgement.Result) { case HitResult.Miss: - Colour = colours.Red; + legacyName = "hit0"; + break; + case HitResult.Meh: + legacyName = "hit50"; + break; + case HitResult.Good: + legacyName = "hit100"; + break; + case HitResult.Great: + legacyName = "hit300"; break; } + + Child = new SkinnableDrawable($"Play/osu/{legacyName}", _ => JudgementText = new OsuSpriteText + { + Text = Judgement.Result.GetDescription().ToUpper(), + Font = @"Venera", + Colour = Judgement.Result == HitResult.Miss ? colours.Red : Color4.White, + Scale = new Vector2(0.85f, 1), + TextSize = 12 + }, restrictSize: false); } protected override void LoadComplete() From f2d7621df3a05a662819a4c83c7c5a598f6cff0f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Mar 2018 18:20:52 +0900 Subject: [PATCH 062/537] Add skin support for explode/flash layers Basically to hide them for legacy skins, though. --- .../Objects/Drawables/Pieces/ExplodePiece.cs | 14 ++++++-------- .../Objects/Drawables/Pieces/FlashPiece.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs index 9be951e29c..76ed89be67 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Skinning; using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces @@ -19,15 +20,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Blending = BlendingMode.Additive; Alpha = 0; - Children = new Drawable[] + Child = new SkinnableDrawable("Play/osu/hitcircle-explode", _ => new TrianglesPiece { - new TrianglesPiece - { - Blending = BlendingMode.Additive, - RelativeSizeAxes = Axes.Both, - Alpha = 0.2f, - } - }; + Blending = BlendingMode.Additive, + RelativeSizeAxes = Axes.Both, + Alpha = 0.2f, + }, false); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs index 56faa335b1..921d24f69d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; using osu.Framework.Graphics.Shapes; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -14,22 +15,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Size = new Vector2(128); - Masking = true; - CornerRadius = Size.X / 2; - Anchor = Anchor.Centre; Origin = Anchor.Centre; Blending = BlendingMode.Additive; Alpha = 0; - Children = new Drawable[] + Child = new SkinnableDrawable("Play/osu/hitcircle-flash", name => new CircularContainer { - new Box + Masking = true, + RelativeSizeAxes = Axes.Both, + Child = new Box { RelativeSizeAxes = Axes.Both } - }; + }, false); } } } From b62ed004f274b3bf25704d2d8dfb6ebe909a715a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Mar 2018 19:14:42 +0900 Subject: [PATCH 063/537] Remove judgements when deciding a new judgement for a HitObject Generally happens when rewinding. --- .../UI/DrawableManiaJudgement.cs | 5 ++-- osu.Game.Rulesets.Mania/UI/ManiaStage.cs | 7 +++--- .../Objects/Drawables/DrawableOsuJudgement.cs | 6 ++--- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 11 ++++----- .../UI/DrawableTaikoJudgement.cs | 7 ++---- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 +++--- .../Rulesets/Judgements/DrawableJudgement.cs | 6 ++++- osu.Game/Rulesets/UI/JudgementContainer.cs | 24 +++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 9 files changed, 50 insertions(+), 24 deletions(-) create mode 100644 osu.Game/Rulesets/UI/JudgementContainer.cs diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index 8a03f5a785..b8ae09c4a0 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -3,13 +3,14 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.UI { internal class DrawableManiaJudgement : DrawableJudgement { - public DrawableManiaJudgement(Judgement judgement) - : base(judgement) + public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject) + : base(judgement, judgedObject) { JudgementText.TextSize = 25; } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs index 2b8039f5df..d4ca704829 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using OpenTK; using OpenTK.Graphics; @@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Container content; public Container Judgements => judgements; - private readonly Container judgements; + private readonly JudgementContainer judgements; private readonly Container topLevelContainer; @@ -114,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.UI Padding = new MarginPadding { Top = HIT_TARGET_POSITION } } }, - judgements = new Container + judgements = new JudgementContainer { Anchor = Anchor.TopCentre, Origin = Anchor.Centre, @@ -171,7 +172,7 @@ namespace osu.Game.Rulesets.Mania.UI internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { judgements.Clear(); - judgements.Add(new DrawableManiaJudgement(judgement) + judgements.Add(new DrawableManiaJudgement(judgement, judgedObject) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 716f4b629b..0b1df4bdf5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -2,17 +2,17 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Game.Rulesets.Osu.Judgements; using OpenTK; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableOsuJudgement : DrawableJudgement { - public DrawableOsuJudgement(OsuJudgement judgement) - : base(judgement) + public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject) + : base(judgement, judgedObject) { } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 7f8cbce78e..98a8096678 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.UI public class OsuPlayfield : Playfield { private readonly Container approachCircles; - private readonly Container judgementLayer; + private readonly JudgementContainer judgementLayer; private readonly ConnectionRenderer connectionLayer; // Todo: This should not be a thing, but is currently required for the editor @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.UI RelativeSizeAxes = Axes.Both, Depth = 2, }, - judgementLayer = new Container + judgementLayer = new JudgementContainer { RelativeSizeAxes = Axes.Both, Depth = 1, @@ -75,16 +75,13 @@ namespace osu.Game.Rulesets.Osu.UI private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) { - var osuJudgement = (OsuJudgement)judgement; - var osuObject = (OsuHitObject)judgedObject.HitObject; - if (!judgedObject.DisplayJudgement) return; - DrawableOsuJudgement explosion = new DrawableOsuJudgement(osuJudgement) + DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject) { Origin = Anchor.Centre, - Position = osuObject.StackedEndPosition + osuJudgement.PositionOffset + Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition + ((OsuJudgement)judgement).PositionOffset }; judgementLayer.Add(explosion); diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index c0e8bd1b5a..6274232ffd 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -15,17 +15,14 @@ namespace osu.Game.Rulesets.Taiko.UI /// public class DrawableTaikoJudgement : DrawableJudgement { - public readonly DrawableHitObject JudgedObject; - /// /// Creates a new judgement text. /// /// The object which is being judged. /// The judgement to visualise. - public DrawableTaikoJudgement(DrawableHitObject judgedObject, Judgement judgement) - : base(judgement) + public DrawableTaikoJudgement(Judgement judgement, DrawableHitObject judgedObject) + : base(judgement, judgedObject) { - JudgedObject = judgedObject; } [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 49c87f7480..75aaceaecb 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -16,6 +16,7 @@ using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Taiko.UI @@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; - private readonly Container judgementContainer; + private readonly JudgementContainer judgementContainer; protected override Container Content => content; private readonly Container content; @@ -131,7 +132,7 @@ namespace osu.Game.Rulesets.Taiko.UI Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, Blending = BlendingMode.Additive }, - judgementContainer = new Container + judgementContainer = new JudgementContainer { Name = "Judgements", RelativeSizeAxes = Axes.Y, @@ -227,7 +228,7 @@ namespace osu.Game.Rulesets.Taiko.UI { if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null) { - judgementContainer.Add(new DrawableTaikoJudgement(judgedObject, judgement) + judgementContainer.Add(new DrawableTaikoJudgement(judgement, judgedObject) { Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft, Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre, diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index c1bf55b214..a3811654f2 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Judgements @@ -20,15 +21,18 @@ namespace osu.Game.Rulesets.Judgements { protected readonly Judgement Judgement; + public readonly DrawableHitObject JudgedObject; + protected readonly SpriteText JudgementText; /// /// Creates a drawable which visualises a . /// /// The judgement to visualise. - public DrawableJudgement(Judgement judgement) + public DrawableJudgement(Judgement judgement, DrawableHitObject judgedObject) { Judgement = judgement; + JudgedObject = judgedObject; AutoSizeAxes = Axes.Both; diff --git a/osu.Game/Rulesets/UI/JudgementContainer.cs b/osu.Game/Rulesets/UI/JudgementContainer.cs new file mode 100644 index 0000000000..1291b9fc98 --- /dev/null +++ b/osu.Game/Rulesets/UI/JudgementContainer.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; + +namespace osu.Game.Rulesets.UI +{ + public class JudgementContainer : Container + where T : DrawableJudgement + { + public override void Add(T judgement) + { + if (judgement == null) throw new ArgumentNullException(nameof(judgement)); + + // remove any existing judgements for the judged object. + // this can be the case when rewinding. + RemoveAll(c => c.JudgedObject == judgement.JudgedObject); + + base.Add(judgement); + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b8ada7c017..2a72023e74 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -374,6 +374,7 @@ + From fe3ab94afbfc84665e28fa8df731a507d6a3656c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Mar 2018 19:19:56 +0900 Subject: [PATCH 064/537] Fix mania judgement regression --- osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index 8a03f5a785..ab3c5bcbb8 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; @@ -11,7 +12,13 @@ namespace osu.Game.Rulesets.Mania.UI public DrawableManiaJudgement(Judgement judgement) : base(judgement) { - JudgementText.TextSize = 25; + } + + [BackgroundDependencyLoader] + private void load() + { + if (JudgementText != null) + JudgementText.TextSize = 25; } protected override void LoadComplete() From ee96e974a89b81d22d12ce86560d4bf02097819d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Mar 2018 09:43:31 +0900 Subject: [PATCH 065/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 6372fb22c1..865b0df18b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 6372fb22c1c85f600921a139849b8dedf71026d5 +Subproject commit 865b0df18bb240190cdf7a7f60d44c0b28c84c5f From 3b5699911808e7796de3dc40d9d556e097d4e665 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Mar 2018 12:57:12 +0900 Subject: [PATCH 066/537] Add drawable to display (and update) relative dates --- osu.Game/Graphics/DrawableDate.cs | 64 +++++++++++++++++++ osu.Game/Overlays/Profile/ProfileHeader.cs | 59 ++++++++--------- .../Sections/Ranks/DrawableProfileScore.cs | 7 +- .../Sections/Recent/DrawableRecentActivity.cs | 9 +-- osu.Game/osu.Game.csproj | 1 + 5 files changed, 95 insertions(+), 45 deletions(-) create mode 100644 osu.Game/Graphics/DrawableDate.cs diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs new file mode 100644 index 0000000000..452443f9d0 --- /dev/null +++ b/osu.Game/Graphics/DrawableDate.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using Humanizer; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Threading; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics +{ + public class DrawableDate : OsuSpriteText, IHasTooltip + { + private readonly DateTimeOffset date; + private ScheduledDelegate updateTask; + + public DrawableDate(DateTimeOffset date) + { + AutoSizeAxes = Axes.Both; + Font = "Exo2.0-RegularItalic"; + + this.date = date.ToLocalTime(); + } + + [BackgroundDependencyLoader] + private void load() + { + updateTime(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Scheduler.Add(updateTimeWithReschedule); + } + + private void updateTimeWithReschedule() + { + updateTime(); + + var diffToNow = DateTimeOffset.Now.Subtract(date); + + double timeUntilNextUpdate = 1000; + if (diffToNow.TotalSeconds > 60) + { + timeUntilNextUpdate *= 60; + if (diffToNow.TotalMinutes > 60) + { + timeUntilNextUpdate *= 60; + + if (diffToNow.TotalHours > 24) + timeUntilNextUpdate *= 24; + } + } + + Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate); + } + + private void updateTime() => Text = date.Humanize(); + public string TooltipText => date.ToString(); + } +} diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index d085800f41..f4b363cd91 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -130,11 +130,7 @@ namespace osu.Game.Overlays.Profile } } }, - infoTextLeft = new OsuTextFlowContainer(t => - { - t.TextSize = 14; - t.Alpha = 0.8f; - }) + infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14) { X = UserProfileOverlay.CONTENT_X_MARGIN, Y = cover_height + 20, @@ -318,11 +314,23 @@ namespace osu.Game.Overlays.Profile colourBar.Show(); } - void boldItalic(SpriteText t) + void boldItalic(SpriteText t) => t.Font = @"Exo2.0-BoldItalic"; + void lightText(SpriteText t) => t.Alpha = 0.8f; + + OsuSpriteText createScoreText(string text) => new OsuSpriteText { - t.Font = @"Exo2.0-BoldItalic"; - t.Alpha = 1; - } + TextSize = 14, + Text = text + }; + + OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-Bold", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = text + }; if (user.Age != null) { @@ -331,7 +339,7 @@ namespace osu.Game.Overlays.Profile if (user.Country != null) { - infoTextLeft.AddText("from "); + infoTextLeft.AddText("from ", lightText); infoTextLeft.AddText(user.Country.FullName, boldItalic); countryFlag.Country = user.Country; } @@ -344,18 +352,18 @@ namespace osu.Game.Overlays.Profile } else { - infoTextLeft.AddText("Joined "); - infoTextLeft.AddText(user.JoinDate.LocalDateTime.ToShortDateString(), boldItalic); + infoTextLeft.AddText("Joined ", lightText); + infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic); } infoTextLeft.NewLine(); - infoTextLeft.AddText("Last seen "); - infoTextLeft.AddText(user.LastVisit.LocalDateTime.ToShortDateString(), boldItalic); + infoTextLeft.AddText("Last seen ", lightText); + infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic); infoTextLeft.NewParagraph(); if (user.PlayStyle?.Length > 0) { - infoTextLeft.AddText("Plays with "); + infoTextLeft.AddText("Plays with ", lightText); infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); } @@ -411,23 +419,6 @@ namespace osu.Game.Overlays.Profile } } - // These could be local functions when C# 7 enabled - - private OsuSpriteText createScoreText(string text) => new OsuSpriteText - { - TextSize = 14, - Text = text - }; - - private OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText - { - TextSize = 14, - Font = @"Exo2.0-Bold", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Text = text - }; - private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null) { if (string.IsNullOrEmpty(str)) return; @@ -436,10 +427,12 @@ namespace osu.Game.Overlays.Profile if (url != null) { infoTextRight.AddLink(" " + str, url); - } else + } + else { infoTextRight.AddText(" " + str); } + infoTextRight.NewLine(); } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index bb1a409f2e..509356ae04 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -54,12 +54,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks RightFlowContainer.SetLayoutPosition(text, 1); LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap)); - LeftFlowContainer.Add(new OsuSpriteText - { - Text = Score.Date.LocalDateTime.ToShortDateString(), - TextSize = 11, - Colour = OsuColour.Gray(0xAA), - }); + LeftFlowContainer.Add(new DrawableDate(Score.Date)); foreach (Mod mod in Score.Mods) modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) }); diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index 2dde8a3d54..e8be8d1e44 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; @@ -40,14 +39,12 @@ namespace osu.Game.Overlays.Profile.Sections.Recent RelativeSizeAxes = Axes.X, }); - RightFlowContainer.Add(new OsuSpriteText + RightFlowContainer.Add(new DrawableDate(activity.CreatedAt) { - Text = activity.CreatedAt.LocalDateTime.ToShortDateString(), + TextSize = 13, + Colour = OsuColour.Gray(0xAA), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Font = "Exo2.0-RegularItalic", - TextSize = 12, - Colour = OsuColour.Gray(0xAA), }); var formatted = createMessage(); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6f7c92ab5a..01074318cd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -287,6 +287,7 @@ + From d3e91024a7cb9dd52185b5f067e304ffa8df3bd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Mar 2018 18:16:23 +0900 Subject: [PATCH 067/537] Block player enter when a drag initiates from an overlaying container --- osu.Game/Screens/Play/PlayerLoader.cs | 19 ++++++++++++++++--- .../PlayerSettings/PlayerSettingsGroup.cs | 4 ++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 2950990779..e082e3f8de 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -22,7 +23,6 @@ namespace osu.Game.Screens.Play private Player player; private BeatmapMetadataDisplay info; - private VisualSettings visualSettings; private bool showOverlays = true; public override bool ShowOverlaysOnEnter => showOverlays; @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, }); - Add(visualSettings = new VisualSettings + Add(new VisualSettings { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -116,9 +116,22 @@ namespace osu.Game.Screens.Play logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo); } + private bool weHandledMouseDown; + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + weHandledMouseDown = true; + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + weHandledMouseDown = false; + return base.OnMouseUp(state, args); + } + private void pushWhenLoaded() { - if (player.LoadState != LoadState.Ready || visualSettings.IsHovered) + if (player.LoadState != LoadState.Ready || !IsHovered || GetContainingInputManager().CurrentState.Mouse.HasAnyButtonPressed && !weHandledMouseDown) { Schedule(pushWhenLoaded); return; diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs index 95b464154a..e0de89535e 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -133,5 +134,8 @@ namespace osu.Game.Screens.Play.PlayerSettings } protected override Container Content => content; + + protected override bool OnHover(InputState state) => true; + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; } } From 94ed4ab01b3748a7df6c58925b5f68bb35278943 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Mar 2018 20:28:55 +0900 Subject: [PATCH 068/537] Add debouncing to player loading Allows the mouse to temporarily exit and re-enter overlay elements without triggering a load --- osu.Game/Screens/Play/PlayerLoader.cs | 42 +++++++++++++++++---------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index e082e3f8de..b91272de75 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Screens.Backgrounds; using OpenTK; using osu.Framework.Localisation; +using osu.Framework.Threading; using osu.Game.Screens.Menu; using osu.Game.Screens.Play.PlayerSettings; @@ -51,6 +52,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, }); + Add(new VisualSettings { Anchor = Anchor.TopRight, @@ -100,7 +102,7 @@ namespace osu.Game.Screens.Play contentIn(); info.Delay(750).FadeIn(500); - this.Delay(2150).Schedule(pushWhenLoaded); + this.Delay(1800).Schedule(pushWhenLoaded); } protected override void LogoArriving(OsuLogo logo, bool resuming) @@ -129,29 +131,39 @@ namespace osu.Game.Screens.Play return base.OnMouseUp(state, args); } + private ScheduledDelegate pushDebounce; + + private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && (!GetContainingInputManager().CurrentState.Mouse.HasAnyButtonPressed || weHandledMouseDown); + private void pushWhenLoaded() { - if (player.LoadState != LoadState.Ready || !IsHovered || GetContainingInputManager().CurrentState.Mouse.HasAnyButtonPressed && !weHandledMouseDown) + Schedule(pushWhenLoaded); + + if (!readyForPush) { - Schedule(pushWhenLoaded); + pushDebounce?.Cancel(); + pushDebounce = null; return; } - contentOut(); - - this.Delay(250).Schedule(() => + if (pushDebounce == null) pushDebounce = Scheduler.AddDelayed(() => { - if (!IsCurrentScreen) return; + contentOut(); - if (!Push(player)) - Exit(); - else + this.Delay(250).Schedule(() => { - //By default, we want to load the player and never be returned to. - //Note that this may change if the player we load requested a re-run. - ValidForResume = false; - } - }); + if (!IsCurrentScreen) return; + + if (!Push(player)) + Exit(); + else + { + //By default, we want to load the player and never be returned to. + //Note that this may change if the player we load requested a re-run. + ValidForResume = false; + } + }); + }, 500); } protected override bool OnExiting(Screen next) From 1f48cfb79af0a821832b5c92750e13470a71d4ec Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Thu, 8 Mar 2018 17:35:20 +0100 Subject: [PATCH 069/537] Added Mirror Mod for Mania --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 1 + .../Mods/ManiaMirrorMod.cs | 28 +++++++++++++++++++ .../osu.Game.Rulesets.Mania.csproj | 1 + 3 files changed, 30 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 16b6888f2b..7c257bf719 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -91,6 +91,7 @@ namespace osu.Game.Rulesets.Mania }, new ManiaModRandom(), new ManiaModDualStages(), + new ManiaMirrorMod(), new MultiMod { Mods = new Mod[] diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs new file mode 100644 index 0000000000..cbcbd9f329 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using System.Linq; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaMirrorMod : Mod, IApplicableToRulesetContainer + { + public override string Name => "Mirror"; + public override string ShortenedName => "MR"; + public override ModType Type => ModType.Special; + public override double ScoreMultiplier => 1; + public override bool Ranked => true; + + public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; + + rulesetContainer.Objects.OfType().ForEach(h => h.Column = -(h.Column) + (availableColumns)-1); + } + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index a09b3e93a7..3c80e21ff2 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -81,6 +81,7 @@ + From 6ad962fc8b7ec6e7390be778012d17988cb81777 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Mar 2018 14:34:05 +0900 Subject: [PATCH 070/537] Interpolate ParallaxContainer's scale Things were a bit jumpy when a screen was adjusting `ParallaxAmount`. This smoothes the applied scale changes to look great again. Most noticeable when hitting the retry hotkey (`~`) from gameplay. --- osu.Game/Graphics/Containers/ParallaxContainer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index febe52d775..97d6225534 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -66,8 +66,10 @@ namespace osu.Game.Graphics.Containers { Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount; - content.Position = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), content.Position, offset, 0, 1000, Easing.OutQuint); - content.Scale = new Vector2(1 + ParallaxAmount); + double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000); + + content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint); + content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint); } firstUpdate = false; From 25fb527cc7bd7f1d1c3a855db9ca2281ee3175a3 Mon Sep 17 00:00:00 2001 From: naoey Date: Fri, 9 Mar 2018 15:51:00 +0530 Subject: [PATCH 071/537] Remove previous fix and move filtered logic to carousel. - Add an optional bool parameter to SelectBeatmap to skip selecting filtered maps --- osu.Game/Screens/Select/BeatmapCarousel.cs | 31 +++++++++++++-------- osu.Game/Screens/Select/SongSelect.cs | 32 ---------------------- 2 files changed, 20 insertions(+), 43 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 9793440348..02bad82ca9 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -169,20 +169,29 @@ namespace osu.Game.Screens.Select }); } - public void SelectBeatmap(BeatmapInfo beatmap) + /// + /// Selects a given beatmap on the carousel. + /// + /// The beatmap to select. + /// Whether to skip selecting filtered beatmaps. + /// True if a selection was made, false if it was skipped. + public bool SelectBeatmap(BeatmapInfo beatmap, bool skipFiltered = false) { if (beatmap?.Hidden != false) - return; + return false; - foreach (CarouselBeatmapSet group in beatmapSets) - { - var item = group.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); - if (item != null) - { - select(item); - return; - } - } + var group = beatmapSets.FirstOrDefault(s => s.BeatmapSet.OnlineBeatmapSetID == beatmap.BeatmapSet.OnlineBeatmapSetID); + + if (group == null || !skipFiltered && group.Filtered) + return false; + + var item = group.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); + + if (item == null || !skipFiltered && item.Filtered) + return false; + + select(item); + return true; } /// diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index d4fd64dcd9..2c8dcae3cf 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -267,10 +267,7 @@ namespace osu.Game.Screens.Select protected void WorkingBeatmapChanged(WorkingBeatmap beatmap) { if (IsCurrentScreen) - { Carousel.SelectBeatmap(beatmap?.BeatmapInfo); - ensurePlayableRuleset(); - } } /// @@ -328,7 +325,6 @@ namespace osu.Game.Screens.Select { base.OnEntering(last); - ensurePlayableRuleset(); Content.FadeInFromZero(250); FilterControl.Activate(); } @@ -456,34 +452,6 @@ namespace osu.Game.Screens.Select } } - private void ensurePlayableRuleset() - { - if (Beatmap.IsDefault) - // DummyBeatmap won't be playable anyway - return; - - bool conversionAllowed = rulesetConversionAllowed.Value; - int? currentRuleset = Ruleset.Value.ID; - int beatmapRuleset = Beatmap.Value.BeatmapInfo.RulesetID; - - if (currentRuleset == beatmapRuleset || conversionAllowed && beatmapRuleset == 0) - // Current beatmap is playable, nothing more to do - return; - - // Otherwise, first check if the current beatmapset has any playable beatmaps - BeatmapInfo beatmap = Beatmap.Value.BeatmapSetInfo.Beatmaps?.FirstOrDefault(b => b.RulesetID == currentRuleset || conversionAllowed && b.RulesetID == 0); - - // If it does then update the WorkingBeatmap - if (beatmap != null) - { - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); - return; - } - - // If it doesn't, then update the current ruleset so that the current beatmap is playable - Ruleset.Value = Beatmap.Value.BeatmapInfo.Ruleset; - } - private void onBeatmapSetAdded(BeatmapSetInfo s) => Carousel.UpdateBeatmapSet(s); private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s); private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); From d04f47718fa73622a6610897357dd3c9de8acdfb Mon Sep 17 00:00:00 2001 From: naoey Date: Fri, 9 Mar 2018 16:22:59 +0530 Subject: [PATCH 072/537] Make song select choose random when initial selection fails. - Revert TestCasePlaySongSelect to master --- .../Visual/TestCasePlaySongSelect.cs | 100 ++---------------- osu.Game/Screens/Select/BeatmapCarousel.cs | 32 ++++-- osu.Game/Screens/Select/SongSelect.cs | 25 ++--- 3 files changed, 37 insertions(+), 120 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index 8532962389..cede0160bc 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -11,7 +11,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.MathUtils; using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Screens.Select; @@ -56,22 +55,10 @@ namespace osu.Game.Tests.Visual public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; - - public void SetRuleset(RulesetInfo ruleset) => Ruleset.Value = ruleset; - - public int? RulesetID => Ruleset.Value.ID; - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - // Necessary while running tests because gc is moody and uncollected object interferes with OnEntering test - Beatmap.ValueChanged -= WorkingBeatmapChanged; - } } [BackgroundDependencyLoader] - private void load(OsuGameBase game, OsuConfigManager config) + private void load(OsuGameBase game) { TestSongSelect songSelect = null; @@ -90,7 +77,6 @@ namespace osu.Game.Tests.Visual { if (deleteMaps) { - // TODO: check why this alone doesn't allow import test to run twice in the same session, probably because the delete op is not saved? manager.Delete(manager.GetAllUsableBeatmapSets()); game.Beatmap.SetDefault(); } @@ -102,8 +88,6 @@ namespace osu.Game.Tests.Visual } Add(songSelect = new TestSongSelect()); - - songSelect?.SetRuleset(rulesets.AvailableRulesets.First()); }); loadNewSongSelect(true); @@ -118,36 +102,6 @@ namespace osu.Game.Tests.Visual { for (int i = 0; i < 100; i += 10) manager.Import(createTestBeatmapSet(i)); - - // also import a set which has a single non - osu ruleset beatmap - manager.Import(new BeatmapSetInfo - { - OnlineBeatmapSetID = 1993, - Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(), - Metadata = new BeatmapMetadata - { - OnlineBeatmapSetID = 1993, - // Create random metadata, then we can check if sorting works based on these - Artist = "MONACA " + RNG.Next(0, 9), - Title = "Black Song " + RNG.Next(0, 9), - AuthorString = "Some Guy " + RNG.Next(0, 9), - }, - Beatmaps = new List - { - new BeatmapInfo - { - OnlineBeatmapID = 1994, - Ruleset = rulesets.AvailableRulesets.ElementAt(3), - RulesetID = 3, - Path = "normal.fruits", - Version = "Normal", - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 3.5f, - } - }, - } - }); }); AddWaitStep(3); @@ -161,45 +115,6 @@ namespace osu.Game.Tests.Visual AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); - - // Test that song select sets a playable beatmap while entering - AddStep(@"Remove song select", () => - { - Remove(songSelect); - songSelect.Dispose(); - songSelect = null; - }); - AddStep(@"Set non-osu beatmap", () => game.Beatmap.Value = manager.GetWorkingBeatmap(manager.GetAllUsableBeatmapSets().First().Beatmaps.First(b => b.RulesetID != 0))); - AddAssert(@"Non-osu beatmap set", () => game.Beatmap.Value.BeatmapInfo.RulesetID != 0); - loadNewSongSelect(); - AddWaitStep(3); - AddAssert(@"osu beatmap set", () => game.Beatmap.Value.BeatmapInfo.RulesetID == 0); - - // Test that song select changes WorkingBeatmap to be playable in current ruleset when updated externally - AddStep(@"Try set non-osu beatmap", () => - { - var testMap = manager.GetAllUsableBeatmapSets().First().Beatmaps.First(b => b.RulesetID != 0); - songSelect.SetRuleset(rulesets.AvailableRulesets.First()); - game.Beatmap.Value = manager.GetWorkingBeatmap(testMap); - }); - AddAssert(@"Beatmap changed to osu", () => songSelect.RulesetID == 0 && game.Beatmap.Value.BeatmapInfo.RulesetID == 0); - - // Test that song select updates WorkingBeatmap when ruleset conversion is disabled - AddStep(@"Disable beatmap conversion", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false)); - AddStep(@"Set osu beatmap taiko rs", () => - { - game.Beatmap.Value = manager.GetWorkingBeatmap(manager.GetAllUsableBeatmapSets().First().Beatmaps.First(b => b.RulesetID == 0)); - songSelect.SetRuleset(rulesets.AvailableRulesets.First(r => r.ID == 1)); - }); - AddAssert(@"taiko beatmap set", () => songSelect.RulesetID == 1); - - // Test that song select changes the active ruleset when externally set beatmapset has no playable beatmaps - AddStep(@"Set fruits only beatmapset", () => - { - songSelect.SetRuleset(rulesets.AvailableRulesets.First()); - game.Beatmap.Value = manager.GetWorkingBeatmap(manager.QueryBeatmapSet(b => b.OnlineBeatmapSetID == 1993).Beatmaps.First()); - }); - AddAssert(@"Ruleset changed to fruits", () => songSelect.RulesetID == game.Beatmap.Value.BeatmapInfo.RulesetID); } private BeatmapSetInfo createTestBeatmapSet(int i) @@ -221,8 +136,7 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1234 + i, - Ruleset = rulesets.AvailableRulesets.ElementAt(0), - RulesetID = 0, + Ruleset = rulesets.AvailableRulesets.First(), Path = "normal.osu", Version = "Normal", BaseDifficulty = new BeatmapDifficulty @@ -233,9 +147,8 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1235 + i, - Ruleset = rulesets.AvailableRulesets.First(r => r.ID != 0), - RulesetID = 1, - Path = "hard.taiko", + Ruleset = rulesets.AvailableRulesets.First(), + Path = "hard.osu", Version = "Hard", BaseDifficulty = new BeatmapDifficulty { @@ -245,9 +158,8 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1236 + i, - Ruleset = rulesets.AvailableRulesets.ElementAt(2), - RulesetID = 2, - Path = "insane.fruits", + Ruleset = rulesets.AvailableRulesets.First(), + Path = "insane.osu", Version = "Insane", BaseDifficulty = new BeatmapDifficulty { diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 02bad82ca9..287584bf2f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -171,27 +171,41 @@ namespace osu.Game.Screens.Select /// /// Selects a given beatmap on the carousel. + /// + /// If skipFiltered is true, we will try to select another unfiltered beatmap in the same set. If the + /// entire set is filtered, no selection is made. /// /// The beatmap to select. /// Whether to skip selecting filtered beatmaps. - /// True if a selection was made, false if it was skipped. + /// True if a selection was made, False if it wasn't. public bool SelectBeatmap(BeatmapInfo beatmap, bool skipFiltered = false) { if (beatmap?.Hidden != false) return false; - var group = beatmapSets.FirstOrDefault(s => s.BeatmapSet.OnlineBeatmapSetID == beatmap.BeatmapSet.OnlineBeatmapSetID); + foreach (CarouselBeatmapSet set in beatmapSets) + { + if (skipFiltered && set.Filtered) + continue; - if (group == null || !skipFiltered && group.Filtered) - return false; + var item = set.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); - var item = group.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); + if (item == null) + // The beatmap that needs to be selected doesn't exist in this set + continue; - if (item == null || !skipFiltered && item.Filtered) - return false; + if (skipFiltered && item.Filtered) + // The beatmap exists in this set but is filtered, so look for the first unfiltered map in the set + item = set.Beatmaps.FirstOrDefault(b => !b.Filtered); - select(item); - return true; + if (item != null) + { + select(item); + return true; + } + } + + return false; } /// diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 2c8dcae3cf..461b17338d 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Linq; using System.Threading; using OpenTK; using OpenTK.Input; @@ -10,14 +9,12 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; @@ -66,8 +63,6 @@ namespace osu.Game.Screens.Select private SampleChannel sampleChangeDifficulty; private SampleChannel sampleChangeBeatmap; - private Bindable rulesetConversionAllowed; - private CancellationTokenSource initialAddSetsTask; private DependencyContainer dependencies; @@ -184,7 +179,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours, OsuConfigManager config) + private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours) { dependencies.CacheAs(this); @@ -199,8 +194,6 @@ namespace osu.Game.Screens.Select if (this.beatmaps == null) this.beatmaps = beatmaps; - rulesetConversionAllowed = config.GetBindable(OsuSetting.ShowConvertedBeatmaps); - if (osu != null) Ruleset.BindTo(osu.Ruleset); @@ -459,16 +452,14 @@ namespace osu.Game.Screens.Select private void carouselBeatmapsLoaded() { - if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false) + if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false && Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, true)) + return; + + if (Carousel.SelectedBeatmapSet == null && !Carousel.SelectNextRandom()) { - Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo); - } - else if (Carousel.SelectedBeatmapSet == null) - { - if (!Carousel.SelectNextRandom()) - // in the case random selection failed, we want to trigger selectionChanged - // to show the dummy beatmap (we have nothing else to display). - carouselSelectionChanged(null); + // in the case random selection failed, we want to trigger selectionChanged + // to show the dummy beatmap (we have nothing else to display). + carouselSelectionChanged(null); } } From 217dd2ecdc273af42124b89fa2c902828d545c95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Mar 2018 21:23:03 +0900 Subject: [PATCH 073/537] Initial push for better decoders --- .../Formats/LegacyBeatmapDecoderTest.cs | 16 +- .../Formats/LegacyStoryboardDecoderTest.cs | 4 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 4 +- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- osu.Game/Beatmaps/Beatmap.cs | 7 +- osu.Game/Beatmaps/BeatmapManager.cs | 6 +- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 20 +- osu.Game/Beatmaps/Formats/Decoder.cs | 81 +++-- .../Beatmaps/Formats/JsonBeatmapDecoder.cs | 14 +- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 40 +-- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 59 +--- .../Formats/LegacyStoryboardDecoder.cs | 296 +++++++++--------- .../Objects/Legacy/ConvertHitObjectParser.cs | 4 +- .../Tests/Beatmaps/BeatmapConversionTest.cs | 4 +- osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- 15 files changed, 250 insertions(+), 309 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index ab10da2cd1..b74be134c1 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var beatmap = decoder.DecodeBeatmap(stream); + var beatmap = decoder.Decode(stream); var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmap.Metadata; @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var beatmapInfo = decoder.DecodeBeatmap(stream).BeatmapInfo; + var beatmapInfo = decoder.Decode(stream).BeatmapInfo; int[] expectedBookmarks = { @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var beatmap = decoder.DecodeBeatmap(stream); + var beatmap = decoder.Decode(stream); var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmap.Metadata; @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var difficulty = decoder.DecodeBeatmap(stream).BeatmapInfo.BaseDifficulty; + var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var beatmap = decoder.DecodeBeatmap(stream); + var beatmap = decoder.Decode(stream); var metadata = beatmap.Metadata; var breakPoint = beatmap.Breaks[0]; @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var beatmap = decoder.DecodeBeatmap(stream); + var beatmap = decoder.Decode(stream); var controlPoints = beatmap.ControlPointInfo; Assert.AreEqual(4, controlPoints.TimingPoints.Count); @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var comboColors = decoder.DecodeBeatmap(stream).ComboColors; + var comboColors = decoder.Decode(stream).ComboColors; Color4[] expectedColors = { @@ -191,7 +191,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var hitObjects = decoder.DecodeBeatmap(stream).HitObjects; + var hitObjects = decoder.Decode(stream).HitObjects; var curveData = hitObjects[0] as IHasCurve; var positionData = hitObjects[0] as IHasPosition; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index dce6c0f55b..1c0801c634 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -18,11 +18,11 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeStoryboardEvents() { - var decoder = new LegacyBeatmapDecoder(); + var decoder = new LegacyStoryboardDecoder(); using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) using (var stream = new StreamReader(resStream)) { - var storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(stream); + var storyboard = decoder.Decode(stream); Assert.IsTrue(storyboard.HasDrawable); Assert.AreEqual(4, storyboard.Layers.Count()); diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 89d96c774e..80dea9d01d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -159,7 +159,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var sr = new StreamReader(stream)) { - var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.DecodeBeatmap(sr); + var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); using (var ms = new MemoryStream()) using (var sw = new StreamWriter(ms)) using (var sr2 = new StreamReader(ms)) @@ -168,7 +168,7 @@ namespace osu.Game.Tests.Beatmaps.Formats sw.Flush(); ms.Position = 0; - return (legacyDecoded, new JsonBeatmapDecoder().DecodeBeatmap(sr2)); + return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2)); } } } diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 1f7246a119..29d25accbb 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO BeatmapMetadata meta; using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - meta = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; + meta = Decoder.GetDecoder(stream).Decode(stream).Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 4fd54e4364..9b00993b6e 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -22,6 +22,7 @@ namespace osu.Game.Beatmaps public BeatmapInfo BeatmapInfo = new BeatmapInfo(); public ControlPointInfo ControlPointInfo = new ControlPointInfo(); public List Breaks = new List(); + public List ComboColors = new List { new Color4(17, 136, 170, 255), @@ -85,9 +86,13 @@ namespace osu.Game.Beatmaps /// Constructs a new beatmap. /// /// The original beatmap to use the parameters of. - public Beatmap(Beatmap original = null) + public Beatmap(Beatmap original) : base(original) { } + + public Beatmap() + { + } } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 1d6d8b6726..817a3388e2 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -301,7 +301,7 @@ namespace osu.Game.Beatmaps BeatmapMetadata metadata; using (var stream = new StreamReader(reader.GetStream(mapName))) - metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; + metadata = Decoder.GetDecoder(stream).Decode(stream).Metadata; return new BeatmapSetInfo { @@ -328,8 +328,8 @@ namespace osu.Game.Beatmaps raw.CopyTo(ms); ms.Position = 0; - var decoder = Decoder.GetDecoder(sr); - Beatmap beatmap = decoder.DecodeBeatmap(sr); + var decoder = Decoder.GetDecoder(sr); + Beatmap beatmap = decoder.Decode(sr); beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index a72c1adfcd..fb11684309 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Logging; using osu.Game.Beatmaps.Formats; using osu.Game.Graphics.Textures; using osu.Game.Storyboards; @@ -30,10 +31,7 @@ namespace osu.Game.Beatmaps try { using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) - { - Decoder decoder = Decoder.GetDecoder(stream); - return decoder.DecodeBeatmap(stream); - } + return Decoder.GetDecoder(stream).Decode(stream); } catch { @@ -78,23 +76,23 @@ namespace osu.Game.Beatmaps Storyboard storyboard; try { - using (var beatmap = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { - Decoder decoder = Decoder.GetDecoder(beatmap); + var decoder = Decoder.GetDecoder(stream); // todo: support loading from both set-wide storyboard *and* beatmap specific. - if (BeatmapSetInfo?.StoryboardFile == null) - storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap); + storyboard = decoder.Decode(stream); else { - using (var reader = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap, reader); + using (var secondaryStream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + storyboard = decoder.Decode(stream, secondaryStream); } } } - catch + catch (Exception e) { + Logger.Error(e, "Storyboard failed to load"); storyboard = new Storyboard(); } diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 1aae52208a..9f10485c5f 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -4,38 +4,64 @@ using System; using System.Collections.Generic; using System.IO; -using osu.Game.Storyboards; +using System.Linq; namespace osu.Game.Beatmaps.Formats { + public abstract class Decoder : Decoder + where TOutput : new() + { + protected virtual TOutput CreateTemplateObject() => new TOutput(); + + public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams) + { + var output = CreateTemplateObject(); + foreach (StreamReader stream in new[] { primaryStream }.Concat(otherStreams)) + ParseStreamInto(stream, output); + return output; + } + + protected abstract void ParseStreamInto(StreamReader stream, TOutput beatmap); + } + public abstract class Decoder { - private static readonly Dictionary> decoders = new Dictionary>(); + private static readonly Dictionary>> decoders = new Dictionary>>(); static Decoder() { - LegacyDecoder.Register(); + LegacyBeatmapDecoder.Register(); JsonBeatmapDecoder.Register(); + LegacyStoryboardDecoder.Register(); } /// /// Retrieves a to parse a . /// /// A stream pointing to the . - public static Decoder GetDecoder(StreamReader stream) + public static Decoder GetDecoder(StreamReader stream) + where T : new() { if (stream == null) throw new ArgumentNullException(nameof(stream)); + if (!decoders.TryGetValue(typeof(T), out var typedDecoders)) + throw new IOException(@"Unknown decoder type"); + string line; do - { line = stream.ReadLine()?.Trim(); } - while (line != null && line.Length == 0); + { + line = stream.ReadLine()?.Trim(); + } while (line != null && line.Length == 0); - if (line == null || !decoders.ContainsKey(line)) + if (line == null) throw new IOException(@"Unknown file format"); - return decoders[line](line); + var decoder = typedDecoders.Select(d => line.StartsWith(d.Key) ? d.Value : null).FirstOrDefault(); + if (decoder == null) + throw new IOException(@"Unknown file format"); + + return (Decoder)decoder.Invoke(line); } /// @@ -43,41 +69,12 @@ namespace osu.Game.Beatmaps.Formats /// /// A string in the file which triggers this decoder to be used. /// A function which constructs the given . - protected static void AddDecoder(string magic, Func constructor) + protected static void AddDecoder(string magic, Func constructor) { - decoders[magic] = constructor; + if (!decoders.TryGetValue(typeof(T), out var typedDecoders)) + decoders.Add(typeof(T), typedDecoders = new Dictionary>()); + + typedDecoders[magic] = constructor; } - - /// - /// Retrieves a to parse a - /// - public abstract Decoder GetStoryboardDecoder(); - - public virtual Beatmap DecodeBeatmap(StreamReader stream) - { - var beatmap = new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata(), - BaseDifficulty = new BeatmapDifficulty(), - }, - }; - - ParseBeatmap(stream, beatmap); - return beatmap; - } - - protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap); - - public virtual Storyboard DecodeStoryboard(params StreamReader[] streams) - { - var storyboard = new Storyboard(); - foreach (StreamReader stream in streams) - ParseStoryboard(stream, storyboard); - return storyboard; - } - - protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard); } } diff --git a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs index b0798e5a87..add0f39280 100644 --- a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs @@ -3,20 +3,17 @@ using System.IO; using osu.Game.IO.Serialization; -using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { - public class JsonBeatmapDecoder : Decoder + public class JsonBeatmapDecoder : Decoder { public static void Register() { - AddDecoder("{", m => new JsonBeatmapDecoder()); + AddDecoder("{", m => new JsonBeatmapDecoder()); } - public override Decoder GetStoryboardDecoder() => this; - - protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) + protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) { stream.BaseStream.Position = 0; stream.DiscardBufferedData(); @@ -26,10 +23,5 @@ namespace osu.Game.Beatmaps.Formats foreach (var hitObject in beatmap.HitObjects) hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); } - - protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) - { - // throw new System.NotImplementedException(); - } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 7d4f8b5bf5..c54d81aa2b 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; using System.IO; +using System.Linq; using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; @@ -12,8 +13,10 @@ using osu.Framework; namespace osu.Game.Beatmaps.Formats { - public class LegacyBeatmapDecoder : LegacyDecoder + public class LegacyBeatmapDecoder : LegacyDecoder { + public const int LATEST_VERSION = 14; + private Beatmap beatmap; private bool hasCustomColours; @@ -22,6 +25,11 @@ namespace osu.Game.Beatmaps.Formats private LegacySampleBank defaultSampleBank; private int defaultSampleVolume = 100; + public static void Register() + { + AddDecoder(@"osu file format v", m => new LegacyBeatmapDecoder(int.Parse(m.Split('v').Last()))); + } + /// /// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited. /// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck. @@ -35,29 +43,16 @@ namespace osu.Game.Beatmaps.Formats private readonly int offset = UniversalOffset; - public LegacyBeatmapDecoder() + public LegacyBeatmapDecoder(int version = LATEST_VERSION) : base(version) { - } - - public LegacyBeatmapDecoder(string header) - { - BeatmapVersion = int.Parse(header.Substring(17)); - // BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off) - offset += BeatmapVersion < 5 ? 24 : 0; + offset += FormatVersion < 5 ? 24 : 0; } - protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) + protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - this.beatmap = beatmap; - this.beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion; - - ParseContent(stream); + base.ParseStreamInto(stream, beatmap); // objects may be out of order *only* if a user has manually edited an .osu file. // unfortunately there are ranked maps in this state (example: https://osu.ppy.sh/s/594828). @@ -67,14 +62,9 @@ namespace osu.Game.Beatmaps.Formats hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); } - protected override bool ShouldSkipLine(string line) - { - if (base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_")) - return true; - return false; - } + protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_"); - protected override void ProcessSection(Section section, string line) + protected override void ParseLine(Beatmap beatmap, Section section, string line) { switch (section) { diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index e0fc439924..6a3fb82586 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -4,47 +4,20 @@ using System; using System.Collections.Generic; using System.IO; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { - public abstract class LegacyDecoder : Decoder + public abstract class LegacyDecoder : Decoder + where T : new() { - public static void Register() + protected readonly int FormatVersion; + + protected LegacyDecoder(int version) { - AddDecoder(@"osu file format v14", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v13", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v12", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v11", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v10", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v9", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v8", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v7", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v6", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v5", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v4", m => new LegacyBeatmapDecoder(m)); - AddDecoder(@"osu file format v3", m => new LegacyBeatmapDecoder(m)); - // TODO: differences between versions + FormatVersion = version; } - protected int BeatmapVersion; - - public override Decoder GetStoryboardDecoder() => new LegacyStoryboardDecoder(BeatmapVersion); - - public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); - - protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) - { - throw new NotImplementedException(); - } - - protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) - { - throw new NotImplementedException(); - } - - protected void ParseContent(StreamReader stream) + protected override void ParseStreamInto(StreamReader stream, T beatmap) { Section section = Section.None; @@ -54,13 +27,6 @@ namespace osu.Game.Beatmaps.Formats if (ShouldSkipLine(line)) continue; - // It's already set in ParseBeatmap... why do it again? - //if (line.StartsWith(@"osu file format v")) - //{ - // Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); - // continue; - //} - if (line.StartsWith(@"[") && line.EndsWith(@"]")) { if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) @@ -68,18 +34,13 @@ namespace osu.Game.Beatmaps.Formats continue; } - ProcessSection(section, line); + ParseLine(beatmap, section, line); } } - protected virtual bool ShouldSkipLine(string line) - { - if (string.IsNullOrWhiteSpace(line) || line.StartsWith("//")) - return true; - return false; - } + protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//"); - protected abstract void ProcessSection(Section section, string line); + protected abstract void ParseLine(T output, Section section, string line); protected KeyValuePair SplitKeyVal(string line, char separator) { diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index a4ff060c83..e35276ae1a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.IO; using OpenTK; @@ -13,37 +13,34 @@ using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { - public class LegacyStoryboardDecoder : LegacyDecoder + public class LegacyStoryboardDecoder : LegacyDecoder { - private Storyboard storyboard; - private StoryboardSprite storyboardSprite; private CommandTimelineGroup timelineGroup; + private Storyboard storyboard; + private readonly Dictionary variables = new Dictionary(); public LegacyStoryboardDecoder() + : base(0) { } - public LegacyStoryboardDecoder(int beatmapVersion) + public static void Register() { - BeatmapVersion = beatmapVersion; + // note that this isn't completely correct + AddDecoder(@"osu file format v", m => new LegacyStoryboardDecoder()); + AddDecoder(@"[Events]", m => new LegacyStoryboardDecoder()); } - protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) + protected override void ParseStreamInto(StreamReader stream, Storyboard storyboard) { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - if (storyboard == null) - throw new ArgumentNullException(nameof(storyboard)); - this.storyboard = storyboard; - - ParseContent(stream); + base.ParseStreamInto(stream, storyboard); } - protected override void ProcessSection(Section section, string line) + protected override void ParseLine(Storyboard storyboard, Section section, string line) { switch (section) { @@ -80,38 +77,38 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case EventType.Sprite: - { - var layer = parseLayer(split[1]); - var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); - var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); - var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); - storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - storyboard.GetLayer(layer).Add(storyboardSprite); - } + { + var layer = parseLayer(split[1]); + var origin = parseOrigin(split[2]); + var path = cleanFilename(split[3]); + var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); + var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); + storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); + storyboard.GetLayer(layer).Add(storyboardSprite); + } break; case EventType.Animation: - { - var layer = parseLayer(split[1]); - var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); - var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); - var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); - var frameCount = int.Parse(split[6]); - var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); - var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; - storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - storyboard.GetLayer(layer).Add(storyboardSprite); - } + { + var layer = parseLayer(split[1]); + var origin = parseOrigin(split[2]); + var path = cleanFilename(split[3]); + var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); + var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); + var frameCount = int.Parse(split[6]); + var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); + var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; + storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); + storyboard.GetLayer(layer).Add(storyboardSprite); + } break; case EventType.Sample: - { - var time = double.Parse(split[1], CultureInfo.InvariantCulture); - var layer = parseLayer(split[2]); - var path = cleanFilename(split[3]); - var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); - } + { + var time = double.Parse(split[1], CultureInfo.InvariantCulture); + var layer = parseLayer(split[2]); + var path = cleanFilename(split[3]); + var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; + storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + } break; } } @@ -124,120 +121,120 @@ namespace osu.Game.Beatmaps.Formats switch (commandType) { case "T": - { - var triggerName = split[1]; - var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue; - var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; - var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; - timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); - } + { + var triggerName = split[1]; + var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue; + var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; + var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; + timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); + } break; case "L": - { - var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); - var loopCount = int.Parse(split[2]); - timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); - } + { + var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); + var loopCount = int.Parse(split[2]); + timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); + } break; default: + { + if (string.IsNullOrEmpty(split[3])) + split[3] = split[2]; + + var easing = (Easing)int.Parse(split[1]); + var startTime = double.Parse(split[2], CultureInfo.InvariantCulture); + var endTime = double.Parse(split[3], CultureInfo.InvariantCulture); + + switch (commandType) { - if (string.IsNullOrEmpty(split[3])) - split[3] = split[2]; - - var easing = (Easing)int.Parse(split[1]); - var startTime = double.Parse(split[2], CultureInfo.InvariantCulture); - var endTime = double.Parse(split[3], CultureInfo.InvariantCulture); - - switch (commandType) + case "F": { - case "F": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "S": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); - } - break; - case "V": - { - var startX = float.Parse(split[4], CultureInfo.InvariantCulture); - var startY = float.Parse(split[5], CultureInfo.InvariantCulture); - var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; - var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); - } - break; - case "R": - { - 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)); - } - break; - case "M": - { - var startX = float.Parse(split[4], CultureInfo.InvariantCulture); - var startY = float.Parse(split[5], CultureInfo.InvariantCulture); - var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; - var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); - timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); - } - break; - case "MX": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "MY": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "C": - { - var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); - var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture); - var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture); - var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed; - var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen; - var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue; - timelineGroup?.Colour.Add(easing, startTime, endTime, - new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), - new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); - } - break; - case "P": - { - var type = split[4]; - switch (type) - { - case "A": - timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); - break; - case "H": - timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); - break; - case "V": - timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); - break; - } - } - break; - default: - throw new InvalidDataException($@"Unknown command type: {commandType}"); + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); } + break; + case "S": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); + } + break; + case "V": + { + var startX = float.Parse(split[4], CultureInfo.InvariantCulture); + var startY = float.Parse(split[5], CultureInfo.InvariantCulture); + var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; + var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); + } + break; + case "R": + { + 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)); + } + break; + case "M": + { + var startX = float.Parse(split[4], CultureInfo.InvariantCulture); + var startY = float.Parse(split[5], CultureInfo.InvariantCulture); + var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; + var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; + timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); + timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); + } + break; + case "MX": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "MY": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "C": + { + var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); + var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture); + var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture); + var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed; + var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen; + var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue; + timelineGroup?.Colour.Add(easing, startTime, endTime, + new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), + new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); + } + break; + case "P": + { + var type = split[4]; + switch (type) + { + case "A": + timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); + break; + case "H": + timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); + break; + case "V": + timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); + break; + } + } + break; + default: + throw new InvalidDataException($@"Unknown command type: {commandType}"); } + } break; } } @@ -269,6 +266,7 @@ namespace osu.Game.Beatmaps.Formats case LegacyOrigins.BottomRight: return Anchor.BottomRight; } + throw new InvalidDataException($@"Unknown origin: {value}"); } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index ce292ef223..5084b28cf2 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -188,8 +188,8 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] split = str.Split(':'); - var bank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]); - var addbank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]); + var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]); + var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]); // Let's not implement this for now, because this doesn't fit nicely into the bank structure //string sampleFile = split2.Length > 4 ? split2[4] : string.Empty; diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 219d805bc1..8505498e4f 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -112,9 +112,9 @@ namespace osu.Game.Tests.Beatmaps using (var resStream = openResource($"{resource_namespace}.{name}.osu")) using (var stream = new StreamReader(resStream)) { - var decoder = Decoder.GetDecoder(stream); + var decoder = Decoder.GetDecoder(stream); ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; - return decoder.DecodeBeatmap(stream); + return decoder.Decode(stream); } } diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 181ed5e0e6..d835adb54f 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder(reader).DecodeBeatmap(reader); + beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder(reader).Decode(reader); return beatmap; } From 2c0488b1f17ac169632cc6e575318ddfd9d360e6 Mon Sep 17 00:00:00 2001 From: naoey Date: Fri, 9 Mar 2018 19:39:28 +0530 Subject: [PATCH 074/537] Invert bool, add test, and handle ruleset change. --- .../Visual/TestCaseBeatmapCarousel.cs | 42 ++++++++++++++++++- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 ++--- osu.Game/Screens/Select/SongSelect.cs | 11 +++-- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index fe26366362..c68e548f44 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -12,6 +12,7 @@ using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Rulesets; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Filter; @@ -22,6 +23,7 @@ namespace osu.Game.Tests.Visual public class TestCaseBeatmapCarousel : OsuTestCase { private TestBeatmapCarousel carousel; + private RulesetStore rulesets; public override IReadOnlyList RequiredTypes => new[] { @@ -46,8 +48,10 @@ namespace osu.Game.Tests.Visual private const int set_count = 5; [BackgroundDependencyLoader] - private void load() + private void load(RulesetStore rulesets) { + this.rulesets = rulesets; + Add(carousel = new TestBeatmapCarousel { RelativeSizeAxes = Axes.Both, @@ -75,6 +79,7 @@ namespace osu.Game.Tests.Visual testRemoveAll(); testEmptyTraversal(); testHiding(); + testSelectingFilteredRuleset(); } private void ensureRandomFetchSuccess() => @@ -363,6 +368,41 @@ namespace osu.Game.Tests.Visual } } + private void testSelectingFilteredRuleset() + { + var testMixed = createTestBeatmapSet(set_count + 1); + AddStep("add mixed ruleset beatmapset", () => + { + for (int i = 0; i <= 2; i++) + { + testMixed.Beatmaps[i].Ruleset = rulesets.AvailableRulesets.ElementAt(i); + testMixed.Beatmaps[i].RulesetID = i; + } + + carousel.UpdateBeatmapSet(testMixed); + }); + AddStep("filter to ruleset 0", () => + carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false)); + AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false)); + AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0])); + + AddStep("remove mixed set", () => + { + carousel.RemoveBeatmapSet(testMixed); + testMixed = null; + }); + var testSingle = createTestBeatmapSet(set_count + 2); + testSingle.Beatmaps.ForEach(b => + { + b.Ruleset = rulesets.AvailableRulesets.ElementAt(1); + b.RulesetID = b.Ruleset.ID ?? 1; + }); + AddStep("add single ruleset beatmapset", () => carousel.UpdateBeatmapSet(testSingle)); + AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testSingle.Beatmaps[0], false)); + checkNoSelection(); + AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle)); + } + private BeatmapSetInfo createTestBeatmapSet(int id) { return new BeatmapSetInfo diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 287584bf2f..c2bb155753 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -172,20 +172,20 @@ namespace osu.Game.Screens.Select /// /// Selects a given beatmap on the carousel. /// - /// If skipFiltered is true, we will try to select another unfiltered beatmap in the same set. If the + /// If bypassFilters is false, we will try to select another unfiltered beatmap in the same set. If the /// entire set is filtered, no selection is made. /// /// The beatmap to select. - /// Whether to skip selecting filtered beatmaps. + /// Whether to select the beatmap even if it is filtered (i.e., not visible on carousel). /// True if a selection was made, False if it wasn't. - public bool SelectBeatmap(BeatmapInfo beatmap, bool skipFiltered = false) + public bool SelectBeatmap(BeatmapInfo beatmap, bool bypassFilters = true) { if (beatmap?.Hidden != false) return false; foreach (CarouselBeatmapSet set in beatmapSets) { - if (skipFiltered && set.Filtered) + if (!bypassFilters && set.Filtered) continue; var item = set.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); @@ -194,7 +194,7 @@ namespace osu.Game.Screens.Select // The beatmap that needs to be selected doesn't exist in this set continue; - if (skipFiltered && item.Filtered) + if (!bypassFilters && item.Filtered) // The beatmap exists in this set but is filtered, so look for the first unfiltered map in the set item = set.Beatmaps.FirstOrDefault(b => !b.Filtered); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 461b17338d..b12ab69edd 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -259,8 +259,13 @@ namespace osu.Game.Screens.Select protected void WorkingBeatmapChanged(WorkingBeatmap beatmap) { - if (IsCurrentScreen) - Carousel.SelectBeatmap(beatmap?.BeatmapInfo); + if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false)) + // If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch + if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value) + { + Ruleset.Value = beatmap.BeatmapInfo.Ruleset; + Carousel.SelectBeatmap(beatmap.BeatmapInfo); + } } /// @@ -452,7 +457,7 @@ namespace osu.Game.Screens.Select private void carouselBeatmapsLoaded() { - if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false && Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, true)) + if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false && Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false)) return; if (Carousel.SelectedBeatmapSet == null && !Carousel.SelectNextRandom()) From 3b766b8ec869c7503ea79c130c09a4608d58785d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Mar 2018 23:11:48 +0900 Subject: [PATCH 075/537] Make CaptureBox account for changes in hitobject states --- osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs index 269dd79bf7..6702678448 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs @@ -42,6 +42,13 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection private void load(OsuColour colours) { BorderColour = colours.Yellow; + } + + protected override void Update() + { + base.Update(); + + // Todo: We might need to optimise this // Move the rectangle to cover the hitobjects var topLeft = new Vector2(float.MaxValue, float.MaxValue); From 4a48136e4f558ca4cba581534de4c256be5e434b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Mar 2018 23:12:34 +0900 Subject: [PATCH 076/537] Make hitobject positions adjustable --- .../Selection/Overlays/HitCircleOverlay.cs | 2 ++ .../Selection/Overlays/SliderOverlay.cs | 11 +++++---- .../Objects/Drawables/DrawableHitCircle.cs | 2 ++ .../Objects/Drawables/DrawableSlider.cs | 6 +++-- .../Objects/Drawables/DrawableSliderHead.cs | 14 +++++++++++ .../Objects/Drawables/DrawableSliderTail.cs | 4 +++- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 24 +++++++++++++++++-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 4 ++-- osu.Game.Rulesets.Osu/Objects/SliderCircle.cs | 19 +++++++++++++++ .../osu.Game.Rulesets.Osu.csproj | 2 ++ .../Edit/Layers/Selection/HitObjectOverlay.cs | 18 ++++++++++++++ .../Edit/Types/IHasEditablePosition.cs | 12 ++++++++++ osu.Game/osu.Game.csproj | 1 + 13 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs create mode 100644 osu.Game.Rulesets.Osu/Objects/SliderCircle.cs create mode 100644 osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs index 4e64783840..ea5104af18 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays Scale = hitCircle.Scale; AddInternal(new RingPiece()); + + hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position; } [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs index a035a683e9..f63d8f0c62 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs @@ -22,18 +22,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { this.slider = slider; - var obj = (Slider)slider.HitObject; + Position = slider.Position; + + var sliderObject = (Slider)slider.HitObject; InternalChildren = new Drawable[] { - body = new SliderBody(obj) + body = new SliderBody(sliderObject) { AccentColour = Color4.Transparent, - PathWidth = obj.Scale * 64 + PathWidth = sliderObject.Scale * 64 }, new SliderCircleOverlay(slider.HeadCircle, slider), new SliderCircleOverlay(slider.TailCircle, slider), }; + + sliderObject.PositionChanged += _ => Position = slider.Position; } [BackgroundDependencyLoader] @@ -46,7 +50,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { base.Update(); - Position = slider.Position; Size = slider.Size; OriginPosition = slider.OriginPosition; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 959c87bbba..d70b26e181 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -66,6 +66,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables //may not be so correct Size = circle.DrawSize; + + HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; } protected override void CheckForJudgements(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index f715ed075c..3fa047a780 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -55,8 +55,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.TailCircle.Position - s.Position }, - TailCircle = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.Position - s.Position } + HeadCircle = new DrawableSliderHead(s, s.HeadCircle), + TailCircle = new DrawableSliderTail(s, s.TailCircle) }; components.Add(Body); @@ -84,6 +84,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables components.Add(drawableRepeatPoint); AddNested(drawableRepeatPoint); } + + HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; } [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs new file mode 100644 index 0000000000..dd31790ee0 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableSliderHead : DrawableHitCircle + { + public DrawableSliderHead(Slider slider, HitCircle h) + : base(h) + { + Position = HitObject.Position - slider.Position; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index b907aea8c3..b277e7df7a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool Tracking { get; set; } - public DrawableSliderTail(HitCircle hitCircle) + public DrawableSliderTail(Slider slider, HitCircle hitCircle) : base(hitCircle) { Origin = Anchor.Centre; @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables FillMode = FillMode.Fit; AlwaysPresent = true; + + Position = HitObject.Position - slider.Position; } protected override void CheckForJudgements(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 9b9d88f0f6..93eaf70589 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -1,23 +1,41 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using OpenTK; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit.Types; namespace osu.Game.Rulesets.Osu.Objects { - public abstract class OsuHitObject : HitObject, IHasCombo, IHasPosition + public abstract class OsuHitObject : HitObject, IHasCombo, IHasPosition, IHasEditablePosition { public const double OBJECT_RADIUS = 64; + public event Action PositionChanged; + public double TimePreempt = 600; public double TimeFadein = 400; - public Vector2 Position { get; set; } + private Vector2 position; + + public Vector2 Position + { + get => position; + set + { + if (position == value) + return; + position = value; + + PositionChanged?.Invoke(value); + } + } + public float X => Position.X; public float Y => Position.Y; @@ -48,5 +66,7 @@ namespace osu.Game.Rulesets.Osu.Objects Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } + + public virtual void SetPosition(Vector2 offset) => Position += offset; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 76439ca530..a633e3957e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Objects private void createSliderEnds() { - HeadCircle = new HitCircle + HeadCircle = new SliderCircle(this) { StartTime = StartTime, Position = Position, @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Objects SampleControlPoint = SampleControlPoint }; - TailCircle = new HitCircle + TailCircle = new SliderCircle(this) { StartTime = EndTime, Position = EndPosition, diff --git a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs new file mode 100644 index 0000000000..31ea67bbe0 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Objects +{ + public class SliderCircle : HitCircle + { + private readonly Slider slider; + + public SliderCircle(Slider slider) + { + this.slider = slider; + } + + public override void SetPosition(Vector2 offset) => slider.SetPosition(offset); + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 8e8a01b009..d6fe87660f 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -87,6 +87,7 @@ + @@ -117,6 +118,7 @@ + diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs index 543dd2cc54..803e86ae77 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Rulesets.Edit.Types; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Edit.Layers.Selection @@ -19,6 +21,22 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection State = Visibility.Visible; } + protected override bool OnDragStart(InputState state) => hitObject.HitObject is IHasEditablePosition; + + protected override bool OnDrag(InputState state) + { + switch (hitObject.HitObject) + { + case IHasEditablePosition editablePosition: + editablePosition.SetPosition(state.Mouse.Delta); + break; + } + + return true; + } + + protected override bool OnDragEnd(InputState state) => true; + protected override void PopIn() => Alpha = 1; protected override void PopOut() => Alpha = 0; } diff --git a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs new file mode 100644 index 0000000000..79694e37a7 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Rulesets.Edit.Types +{ + public interface IHasEditablePosition + { + void SetPosition(Vector2 offset); + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d10f0085cc..aee38d85bc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -365,6 +365,7 @@ + From ad72d3816b7e90f648ff1509b1ebf1446e44b3d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Mar 2018 23:43:50 +0900 Subject: [PATCH 077/537] Allow dragging anywhere in a capture box to move objects --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 1 + .../Edit/Layers/Selection/CaptureBox.cs | 19 ++++++++++++ .../Edit/Layers/Selection/HitObjectOverlay.cs | 31 ++----------------- .../Layers/Selection/HitObjectOverlayLayer.cs | 29 ++++++++++++++--- .../Edit/Layers/Selection/SelectionLayer.cs | 6 ++++ 5 files changed, 53 insertions(+), 33 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index e6a51cc39b..383adca136 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -108,6 +108,7 @@ namespace osu.Game.Rulesets.Edit selectionLayer.ObjectSelected += hitObjectOverlayLayer.AddOverlay; selectionLayer.ObjectDeselected += hitObjectOverlayLayer.RemoveOverlay; + selectionLayer.SelectionMovementRequested += hitObjectOverlayLayer.MoveObjects; toolboxCollection.Items = new[] { new RadioButton("Select", () => setCompositionTool(null)) } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs index 6702678448..1454c78014 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs @@ -1,11 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; @@ -17,6 +19,11 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// public class CaptureBox : VisibilityContainer { + /// + /// Invoked when the captured s should be moved. + /// + public event Action MovementRequested; + private readonly IDrawable captureArea; private readonly IReadOnlyList capturedObjects; @@ -67,6 +74,18 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection Position = topLeft; } + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + + protected override bool OnDragStart(InputState state) => true; + + protected override bool OnDrag(InputState state) + { + MovementRequested?.Invoke(state.Mouse.Delta); + return true; + } + + protected override bool OnDragEnd(InputState state) => true; + public override bool DisposeOnDeathRemoval => true; protected override void PopIn() => this.FadeIn(); diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs index 803e86ae77..8c58275943 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs @@ -2,42 +2,17 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Rulesets.Edit.Types; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Edit.Layers.Selection { - public class HitObjectOverlay : OverlayContainer + public class HitObjectOverlay : Container { - // ReSharper disable once NotAccessedField.Local - // This will be used later to handle drag movement, etc - private readonly DrawableHitObject hitObject; + public readonly DrawableHitObject HitObject; public HitObjectOverlay(DrawableHitObject hitObject) { - this.hitObject = hitObject; - - State = Visibility.Visible; + HitObject = hitObject; } - - protected override bool OnDragStart(InputState state) => hitObject.HitObject is IHasEditablePosition; - - protected override bool OnDrag(InputState state) - { - switch (hitObject.HitObject) - { - case IHasEditablePosition editablePosition: - editablePosition.SetPosition(state.Mouse.Delta); - break; - } - - return true; - } - - protected override bool OnDragEnd(InputState state) => true; - - protected override void PopIn() => Alpha = 1; - protected override void PopOut() => Alpha = 0; } } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs index 0b6e63d1fe..bb8496e9c6 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs @@ -1,20 +1,25 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Rulesets.Edit.Types; using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; namespace osu.Game.Rulesets.Edit.Layers.Selection { public class HitObjectOverlayLayer : CompositeDrawable { - private readonly Dictionary existingOverlays = new Dictionary(); + private readonly Container overlayContainer; public HitObjectOverlayLayer() { RelativeSizeAxes = Axes.Both; + + InternalChild = overlayContainer = new Container { RelativeSizeAxes = Axes.Both }; } /// @@ -27,8 +32,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection if (overlay == null) return; - existingOverlays[hitObject] = overlay; - AddInternal(overlay); + overlayContainer.Add(overlay); } /// @@ -37,13 +41,28 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// The to remove the overlay for. public void RemoveOverlay(DrawableHitObject hitObject) { - if (!existingOverlays.TryGetValue(hitObject, out var existing)) + var existing = overlayContainer.FirstOrDefault(h => h.HitObject == hitObject); + if (existing == null) return; existing.Hide(); existing.Expire(); } + public void MoveObjects(Vector2 offset) + { + // Todo: Various forms of snapping + foreach (var hitObject in overlayContainer.Select(o => o.HitObject.HitObject)) + { + switch (hitObject) + { + case IHasEditablePosition editablePosition: + editablePosition.SetPosition(offset); + break; + } + } + } + /// /// Creates a for a specific . /// diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index 3895d34d7f..0383b933ab 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -27,6 +27,11 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// public event Action ObjectDeselected; + /// + /// Invoked when the selected s should be moved. + /// + public event Action SelectionMovementRequested; + private readonly Playfield playfield; public SelectionLayer(Playfield playfield) @@ -192,6 +197,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection return; AddInternal(captureBox = new CaptureBox(this, selectedHitObjects.ToList())); + captureBox.MovementRequested += v => SelectionMovementRequested?.Invoke(v); } } } From 376f6eec58afb79c510ad46ed0e7e8d0c9bd9955 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 10 Mar 2018 00:02:13 +0900 Subject: [PATCH 078/537] SetPosition -> OffsetPosition --- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/SliderCircle.cs | 2 +- .../Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs | 2 +- osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 93eaf70589..339ad61b17 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -67,6 +67,6 @@ namespace osu.Game.Rulesets.Osu.Objects Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } - public virtual void SetPosition(Vector2 offset) => Position += offset; + public virtual void OffsetPosition(Vector2 offset) => Position += offset; } } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs index 31ea67bbe0..1e83d02735 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs @@ -14,6 +14,6 @@ namespace osu.Game.Rulesets.Osu.Objects this.slider = slider; } - public override void SetPosition(Vector2 offset) => slider.SetPosition(offset); + public override void OffsetPosition(Vector2 offset) => slider.OffsetPosition(offset); } } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs index bb8496e9c6..d391855f7a 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection switch (hitObject) { case IHasEditablePosition editablePosition: - editablePosition.SetPosition(offset); + editablePosition.OffsetPosition(state.Mouse.Delta); break; } } diff --git a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs index 79694e37a7..3530dba8f4 100644 --- a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs +++ b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Edit.Types { public interface IHasEditablePosition { - void SetPosition(Vector2 offset); + void OffsetPosition(Vector2 offset); } } From 0e8fbc47b7610a8fc2f69e2a3aa47372c63d9b1f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 10 Mar 2018 00:02:51 +0900 Subject: [PATCH 079/537] Give HitObjectOverlayLayer full input state information --- osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs | 4 ++-- .../Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs | 3 +-- osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs index 1454c78014..970481032b 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// /// Invoked when the captured s should be moved. /// - public event Action MovementRequested; + public event Action MovementRequested; private readonly IDrawable captureArea; private readonly IReadOnlyList capturedObjects; @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection protected override bool OnDrag(InputState state) { - MovementRequested?.Invoke(state.Mouse.Delta); + MovementRequested?.Invoke(state); return true; } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs index d391855f7a..438ba5e76d 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Game.Rulesets.Edit.Types; using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; namespace osu.Game.Rulesets.Edit.Layers.Selection { @@ -49,7 +48,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection existing.Expire(); } - public void MoveObjects(Vector2 offset) + public void MoveObjects(InputState state) { // Todo: Various forms of snapping foreach (var hitObject in overlayContainer.Select(o => o.HitObject.HitObject)) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index 0383b933ab..ca6c1c122a 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// /// Invoked when the selected s should be moved. /// - public event Action SelectionMovementRequested; + public event Action SelectionMovementRequested; private readonly Playfield playfield; From 4103c66cff25829c6acc0f9feb5ce5cff4060daa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 10 Mar 2018 00:48:57 +0900 Subject: [PATCH 080/537] Move selection overlay to HitObjectOverlayLayer for extensibility --- .../Visual/TestCaseEditorSelectionLayer.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 +- .../Layers/Selection/HitObjectOverlayLayer.cs | 28 ++++++++------- .../Edit/Layers/Selection/SelectionLayer.cs | 26 +++++++------- .../{CaptureBox.cs => SelectionOverlay.cs} | 35 ++++++++++--------- osu.Game/osu.Game.csproj | 2 +- 6 files changed, 50 insertions(+), 46 deletions(-) rename osu.Game/Rulesets/Edit/Layers/Selection/{CaptureBox.cs => SelectionOverlay.cs} (68%) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 8d12dfc517..dc8a13d044 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual { typeof(SelectionBox), typeof(SelectionLayer), - typeof(CaptureBox), + typeof(SelectionOverlay), typeof(HitObjectComposer), typeof(OsuHitObjectComposer), typeof(HitObjectOverlayLayer), diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 383adca136..914640622b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -108,7 +108,8 @@ namespace osu.Game.Rulesets.Edit selectionLayer.ObjectSelected += hitObjectOverlayLayer.AddOverlay; selectionLayer.ObjectDeselected += hitObjectOverlayLayer.RemoveOverlay; - selectionLayer.SelectionMovementRequested += hitObjectOverlayLayer.MoveObjects; + selectionLayer.SelectionCleared += hitObjectOverlayLayer.RemoveSelectionOverlay; + selectionLayer.SelectionFinished += hitObjectOverlayLayer.AddSelectionOverlay; toolboxCollection.Items = new[] { new RadioButton("Select", () => setCompositionTool(null)) } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs index 438ba5e76d..24d594f59a 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs @@ -1,11 +1,10 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Rulesets.Edit.Types; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Edit.Layers.Selection @@ -48,18 +47,14 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection existing.Expire(); } - public void MoveObjects(InputState state) + private SelectionOverlay currentSelectionOverlay; + + public void AddSelectionOverlay() => AddInternal(currentSelectionOverlay = CreateSelectionOverlay(overlayContainer)); + + public void RemoveSelectionOverlay() { - // Todo: Various forms of snapping - foreach (var hitObject in overlayContainer.Select(o => o.HitObject.HitObject)) - { - switch (hitObject) - { - case IHasEditablePosition editablePosition: - editablePosition.OffsetPosition(state.Mouse.Delta); - break; - } - } + currentSelectionOverlay?.Hide(); + currentSelectionOverlay?.Expire(); } /// @@ -67,5 +62,12 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// /// The to create the overlay for. protected virtual HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) => null; + + /// + /// Creates a which outlines s + /// and handles all hitobject movement/pattern adjustments. + /// + /// The overlays. + protected virtual SelectionOverlay CreateSelectionOverlay(IReadOnlyList overlays) => new SelectionOverlay(overlays); } } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index ca6c1c122a..2f8b9165c4 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -28,9 +28,14 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection public event Action ObjectDeselected; /// - /// Invoked when the selected s should be moved. + /// Invoked when the selection has been cleared. /// - public event Action SelectionMovementRequested; + public event Action SelectionCleared; + + /// + /// Invoked when the user has finished selecting all s. + /// + public event Action SelectionFinished; private readonly Playfield playfield; @@ -42,7 +47,6 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection } private SelectionBox selectionBox; - private CaptureBox captureBox; private readonly HashSet selectedHitObjects = new HashSet(); @@ -100,7 +104,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection if (!select(hitObject)) return; - clearCapture(); + clearSelection(); finishSelection(); } @@ -127,7 +131,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection if (!deselect(hitObject)) return; - clearCapture(); + clearSelection(); finishSelection(); } @@ -153,7 +157,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection selectedHitObjects.ForEach(h => ObjectDeselected?.Invoke(h)); selectedHitObjects.Clear(); - clearCapture(); + clearSelection(); } /// @@ -185,19 +189,13 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection select(target); } - private void clearCapture() - { - captureBox?.Hide(); - captureBox?.Expire(); - } + private void clearSelection() => SelectionCleared?.Invoke(); private void finishSelection() { if (selectedHitObjects.Count == 0) return; - - AddInternal(captureBox = new CaptureBox(this, selectedHitObjects.ToList())); - captureBox.MovementRequested += v => SelectionMovementRequested?.Invoke(v); + SelectionFinished?.Invoke(); } } } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs similarity index 68% rename from osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs rename to osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs index 970481032b..c20769a912 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/CaptureBox.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs @@ -1,14 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics; +using osu.Game.Rulesets.Edit.Types; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; @@ -17,20 +18,13 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// /// A box which encloses s. /// - public class CaptureBox : VisibilityContainer + public class SelectionOverlay : VisibilityContainer { - /// - /// Invoked when the captured s should be moved. - /// - public event Action MovementRequested; + private readonly IReadOnlyList overlays; - private readonly IDrawable captureArea; - private readonly IReadOnlyList capturedObjects; - - public CaptureBox(IDrawable captureArea, IReadOnlyList capturedObjects) + public SelectionOverlay(IReadOnlyList overlays) { - this.captureArea = captureArea; - this.capturedObjects = capturedObjects; + this.overlays = overlays; Masking = true; BorderThickness = SelectionBox.BORDER_RADIUS; @@ -61,10 +55,10 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection var topLeft = new Vector2(float.MaxValue, float.MaxValue); var bottomRight = new Vector2(float.MinValue, float.MinValue); - foreach (var obj in capturedObjects) + foreach (var obj in overlays) { - topLeft = Vector2.ComponentMin(topLeft, captureArea.ToLocalSpace(obj.SelectionQuad.TopLeft)); - bottomRight = Vector2.ComponentMax(bottomRight, captureArea.ToLocalSpace(obj.SelectionQuad.BottomRight)); + topLeft = Vector2.ComponentMin(topLeft, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.TopLeft)); + bottomRight = Vector2.ComponentMax(bottomRight, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.BottomRight)); } topLeft -= new Vector2(5); @@ -80,7 +74,16 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection protected override bool OnDrag(InputState state) { - MovementRequested?.Invoke(state); + // Todo: Various forms of snapping + foreach (var hitObject in overlays.Select(o => o.HitObject.HitObject)) + { + switch (hitObject) + { + case IHasEditablePosition editablePosition: + editablePosition.OffsetPosition(state.Mouse.Delta); + break; + } + } return true; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index aee38d85bc..fa99ae616a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -362,7 +362,7 @@ - + From 8ed5fce43a46b3c0f97fb939e6c24c371b2740dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 10 Mar 2018 01:11:56 +0900 Subject: [PATCH 081/537] Split out the slider head into a separate drawable hitobject --- .../Objects/Drawables/DrawableSlider.cs | 9 ++---- .../Objects/Drawables/DrawableSliderHead.cs | 32 +++++++++++++++++++ .../Objects/Drawables/DrawableSliderTail.cs | 4 ++- .../osu.Game.Rulesets.Osu.csproj | 1 + 4 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index f715ed075c..5b9ed4d259 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; using osu.Game.Configuration; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -55,8 +54,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.TailCircle.Position - s.Position }, - TailCircle = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.Position - s.Position } + HeadCircle = new DrawableSliderHead(s, s.HeadCircle), + TailCircle = new DrawableSliderTail(s, s.TailCircle) }; components.Add(Body); @@ -103,10 +102,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); - //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. - if (!HeadCircle.IsHit) - HeadCircle.Position = slider.CurvePositionAt(completionProgress); - foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs new file mode 100644 index 0000000000..cf36d5fc14 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableSliderHead : DrawableHitCircle + { + private readonly Slider slider; + + public DrawableSliderHead(Slider slider, HitCircle h) + : base(h) + { + this.slider = slider; + + Position = HitObject.Position - slider.Position; + } + + protected override void Update() + { + base.Update(); + + double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); + + //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. + if (!IsHit) + Position = slider.CurvePositionAt(completionProgress); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index b907aea8c3..b277e7df7a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool Tracking { get; set; } - public DrawableSliderTail(HitCircle hitCircle) + public DrawableSliderTail(Slider slider, HitCircle hitCircle) : base(hitCircle) { Origin = Anchor.Centre; @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables FillMode = FillMode.Fit; AlwaysPresent = true; + + Position = HitObject.Position - slider.Position; } protected override void CheckForJudgements(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 8e8a01b009..92cac71ad3 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -87,6 +87,7 @@ + From 92b302971f3f04ef3a852aab3098dca1a49563ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 10 Mar 2018 01:23:53 +0900 Subject: [PATCH 082/537] Trim whitespace --- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 339ad61b17..574063d4a5 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects if (position == value) return; position = value; - + PositionChanged?.Invoke(value); } } From ed20e31bbe1e95ee642d6357fb08e278ede68bad Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Sat, 10 Mar 2018 08:39:11 +0100 Subject: [PATCH 083/537] Removed redundant parentheses --- osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs index cbcbd9f329..e7f0e27733 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Mods { var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; - rulesetContainer.Objects.OfType().ForEach(h => h.Column = -(h.Column) + (availableColumns)-1); + rulesetContainer.Objects.OfType().ForEach(h => h.Column = -h.Column + availableColumns - 1); } } } From a4dfeff2d775e018407c45eded431b33a9ec1a62 Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Sat, 10 Mar 2018 08:44:46 +0100 Subject: [PATCH 084/537] Renamed ManiaMirrorMod to ManiaModMirror --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../Mods/{ManiaMirrorMod.cs => ManiaModMirror.cs} | 2 +- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename osu.Game.Rulesets.Mania/Mods/{ManiaMirrorMod.cs => ManiaModMirror.cs} (91%) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 7c257bf719..268bc23640 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Mania }, new ManiaModRandom(), new ManiaModDualStages(), - new ManiaMirrorMod(), + new ManiaModMirror(), new MultiMod { Mods = new Mod[] diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs similarity index 91% rename from osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs rename to osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index e7f0e27733..be3a0e02db 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaMirrorMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -10,7 +10,7 @@ using System.Linq; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaMirrorMod : Mod, IApplicableToRulesetContainer + public class ManiaModMirror : Mod, IApplicableToRulesetContainer { public override string Name => "Mirror"; public override string ShortenedName => "MR"; diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 3c80e21ff2..52d8f66717 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -81,7 +81,7 @@ - + From 709fcf955239e89aa178614bfd72766dce32c76a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Mar 2018 20:01:36 +0900 Subject: [PATCH 085/537] Update ISSUE_TEMPLATE --- ISSUE_TEMPLATE.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index ff930b07a3..2bff304fba 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,9 +1,11 @@ -osu!lazer is currently in early stages of development and is not yet ready for end users. Please avoid creating issues or bugs if you do not personally intend to fix them. Some acceptable topics include: +osu!lazer is currently still under heavy development! +Please ensure that you are making an issue for one of the following: + +- A bug with currently implemented features (not features that don't exist) +- A feature you are considering adding, so we can collaborate on feedback and design. - Discussions about technical design decisions -- Bugs that you have found and are personally willing and able to fix -- TODO lists of smaller tasks around larger features - -Basically, issues are not a place for you to get help. They are a place for developers to collaborate on the game. If your issue qualifies, replace this text with a detailed description of your issue with as much relevant information as you can provide. + +Screenshots and log files are highly welcomed. \ No newline at end of file From 9233266fe1311be39a71b826b652515f5398c743 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Mar 2018 00:44:00 +0900 Subject: [PATCH 086/537] Fix login failure for users with no country rank Closes #2148. --- osu.Game.Tests/Visual/TestCaseUserProfile.cs | 6 ++++++ osu.Game/Overlays/Profile/RankGraph.cs | 4 ++-- osu.Game/Overlays/UserProfileOverlay.cs | 15 +++++++++------ osu.Game/Users/UserStatistics.cs | 4 ++-- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index 3caef777e7..1fc6c6f224 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -58,6 +58,12 @@ namespace osu.Game.Tests.Visual checkSupporterTag(false); + AddStep("Show null dummy", () => profile.ShowUser(new User + { + Username = @"Null", + Id = 1, + }, false)); + AddStep("Show ppy", () => profile.ShowUser(new User { Username = @"peppy", diff --git a/osu.Game/Overlays/Profile/RankGraph.cs b/osu.Game/Overlays/Profile/RankGraph.cs index 429049c7bc..369bdee65f 100644 --- a/osu.Game/Overlays/Profile/RankGraph.cs +++ b/osu.Game/Overlays/Profile/RankGraph.cs @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Profile { placeholder.FadeIn(fade_duration, Easing.Out); - if (user == null) + if (user?.Statistics?.Ranks.Global == null) { rankText.Text = string.Empty; performanceText.Text = string.Empty; @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.Profile return; } - int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global }; + int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global.Value }; ranks = userRanks.Select((x, index) => new KeyValuePair(index, x)).Where(x => x.Value != 0).ToArray(); if (ranks.Length > 1) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index f3fd7aeac5..aed0a6d7c6 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -169,15 +169,18 @@ namespace osu.Game.Overlays { Header.User = user; - foreach (string id in user.ProfileOrder) + if (user.ProfileOrder != null) { - var sec = sections.FirstOrDefault(s => s.Identifier == id); - if (sec != null) + foreach (string id in user.ProfileOrder) { - sec.User.Value = user; + var sec = sections.FirstOrDefault(s => s.Identifier == id); + if (sec != null) + { + sec.User.Value = user; - sectionsContainer.Add(sec); - tabs.AddItem(sec); + sectionsContainer.Add(sec); + tabs.AddItem(sec); + } } } } diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index c29bc91d17..2504c9c62c 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -73,10 +73,10 @@ namespace osu.Game.Users public struct UserRanks { [JsonProperty(@"global")] - public int Global; + public int? Global; [JsonProperty(@"country")] - public int Country; + public int? Country; } } From db2a663234aea7180f577a807fb879c00f4d4908 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Mar 2018 01:26:03 +0900 Subject: [PATCH 087/537] Use private instead of protected --- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b12ab69edd..ca8a1cae41 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -214,7 +214,7 @@ namespace osu.Game.Screens.Select Beatmap.DisabledChanged += disabled => Carousel.AllowSelection = !disabled; Beatmap.TriggerChange(); - Beatmap.ValueChanged += WorkingBeatmapChanged; + Beatmap.ValueChanged += workingBeatmapChanged; } public void Edit(BeatmapInfo beatmap) @@ -257,7 +257,7 @@ namespace osu.Game.Screens.Select // We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds. private BeatmapInfo beatmapNoDebounce; - protected void WorkingBeatmapChanged(WorkingBeatmap beatmap) + private void workingBeatmapChanged(WorkingBeatmap beatmap) { if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false)) // If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch From df2815f19e658326d8197f642e6fde4a257679ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Mar 2018 02:11:25 +0900 Subject: [PATCH 088/537] Add TestCase for GameplayCursor --- .../Tests/TestCaseGameplayCursor.cs | 33 +++++++++++++++++++ .../osu.Game.Rulesets.Osu.csproj | 1 + 2 files changed, 34 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs new file mode 100644 index 0000000000..0504c47123 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.Cursor; +using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseGameplayCursor : OsuTestCase, IProvideCursor + { + private GameplayCursor cursor; + + public override IReadOnlyList RequiredTypes => new [] { typeof(CursorTrail) }; + + public CursorContainer Cursor => cursor; + + public bool ProvidingUserCursor => true; + + [BackgroundDependencyLoader] + private void load() + { + Add(cursor = new GameplayCursor() { RelativeSizeAxes = Axes.Both }); + } + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 92cac71ad3..a7c060cd79 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -130,6 +130,7 @@ + From 5092fe5596c36e65c62be0cc453e7858d6a88a4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Mar 2018 02:11:56 +0900 Subject: [PATCH 089/537] Adjust cursor trail display length In line with shader change at https://github.com/ppy/osu-resources/pull/41/commits/ad5ddec7b2a2b989bd67eaa38919fb0619ed8a6f --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index fed2105f21..dedfa28b7b 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor const int fade_clock_reset_threshold = 1000000; - time = (float)(Time.Current - timeOffset) / 500f; + time = (float)(Time.Current - timeOffset) / 300f; if (time > fade_clock_reset_threshold) resetTime(); } From ccc3c573abd6e1ae018947db37f5f27d7475b483 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Mar 2018 02:20:00 +0900 Subject: [PATCH 090/537] Fix resharper not fixing --- osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs index 0504c47123..273422f2e9 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Tests [BackgroundDependencyLoader] private void load() { - Add(cursor = new GameplayCursor() { RelativeSizeAxes = Axes.Both }); + Add(cursor = new GameplayCursor { RelativeSizeAxes = Axes.Both }); } } } From e6c22e2287c842711f4c76005882544468243b7b Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Sat, 10 Mar 2018 21:59:20 +0300 Subject: [PATCH 091/537] Taking screenshot support initial commit --- osu.Game/Configuration/OsuConfigManager.cs | 5 ++- .../Input/Bindings/GlobalActionContainer.cs | 4 ++ osu.Game/OsuGame.cs | 42 +++++++++++++++++++ .../Sections/Graphics/DetailSettings.cs | 8 +++- osu.Game/osu.Game.csproj | 1 + 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 3d927ef67c..dd17f2c4aa 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -82,6 +82,8 @@ namespace osu.Game.Configuration Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); Set(OsuSetting.Version, string.Empty); + + Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Png); } public OsuConfigManager(Storage storage) : base(storage) @@ -125,6 +127,7 @@ namespace osu.Game.Configuration Version, ShowConvertedBeatmaps, SpeedChangeVisualisation, - Skin + Skin, + ScreenshotFormat } } diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 17ec2af4b9..f6263a05c2 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -26,6 +26,8 @@ namespace osu.Game.Input.Bindings { new KeyBinding(InputKey.F8, GlobalAction.ToggleChat), new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial), + new KeyBinding(InputKey.F12,GlobalAction.TakeScreenshot), + new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings), new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar), new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), @@ -66,6 +68,8 @@ namespace osu.Game.Input.Bindings DecreaseVolume, [Description("Toggle mute")] ToggleMute, + [Description("Take screenshot")] + TakeScreenshot, // In-Game Keybindings [Description("Skip Cutscene")] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e656c7256e..1078548bef 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Drawing.Imaging; +using System.IO; using osu.Framework.Configuration; using osu.Framework.Screens; using osu.Game.Configuration; @@ -83,6 +85,8 @@ namespace osu.Game private Bindable configSkin; + private Bindable screenshotFormat; + private readonly string[] args; private SettingsOverlay settings; @@ -134,6 +138,8 @@ namespace osu.Game // bind config int to database SkinInfo configSkin = LocalConfig.GetBindable(OsuSetting.Skin); + screenshotFormat = LocalConfig.GetBindable(OsuSetting.ScreenshotFormat); + SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID; configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default; configSkin.TriggerChange(); @@ -432,11 +438,47 @@ namespace osu.Game case GlobalAction.ToggleDirect: direct.ToggleVisibility(); return true; + case GlobalAction.TakeScreenshot: + if (Window.ScreenshotTakenAction == null) + Window.ScreenshotTakenAction = (screenshotBitmap) => + { + var fileName = getScreenshotFileName(screenshotFormat); + + switch (screenshotFormat.Value) + { + case ScreenshotFormat.Bmp: + screenshotBitmap.Save(fileName, ImageFormat.Bmp); + break; + case ScreenshotFormat.Png: + screenshotBitmap.Save(fileName, ImageFormat.Png); + break; + case ScreenshotFormat.Jpg: + screenshotBitmap.Save(fileName, ImageFormat.Jpeg); + break; + default: + throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); + } + }; + + RequestScreenshot(); + return true; } return false; } + private string getScreenshotFileName(ScreenshotFormat screenshotFormat) + { + // TODO Change screenshots location + var baseDirectory = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); + var screenshotsDirectory = baseDirectory.CreateSubdirectory("Screenshots"); + + var screenshotExtension = screenshotFormat.ToString().ToLower(); + var screenshots = screenshotsDirectory.GetFiles($"*.{screenshotExtension}"); + + return Path.Combine(screenshotsDirectory.FullName, $"screenshot{screenshots.Length + 1}.{screenshotExtension}"); + } + private readonly BindableDouble inactiveVolumeAdjust = new BindableDouble(); protected override void OnDeactivated() diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index b9d76c05f0..3f4fc96d31 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Graphics @@ -12,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - Children = new[] + Children = new Drawable[] { new SettingsCheckbox { @@ -24,6 +25,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = "Rotate cursor when dragging", Bindable = config.GetBindable(OsuSetting.CursorRotation) }, + new SettingsEnumDropdown() + { + LabelText = "Screenshot format", + Bindable = config.GetBindable(OsuSetting.ScreenshotFormat) + } }; } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d10f0085cc..fdda575a6c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -187,6 +187,7 @@ $(SolutionDir)\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll + $(SolutionDir)\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll From a321bcf3af990a07a97369f7ee1158f06c329a8f Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sun, 11 Mar 2018 20:19:03 +0100 Subject: [PATCH 092/537] Fix check against LegacyID for non-default rulesets --- osu.Game/Rulesets/RulesetStore.cs | 2 +- osu.Game/Tests/Visual/TestCasePerformancePoints.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 92fbf25f04..4891b46c9d 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets context.SaveChanges(); //add any other modes - foreach (var r in instances.Where(r => r.LegacyID < 0)) + foreach (var r in instances.Where(r => r.LegacyID == null || r.LegacyID < 0)) if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) context.RulesetInfo.Add(r.RulesetInfo); diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index 5b32433467..c35c0308f9 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual private void load(BeatmapManager beatmaps) { var sets = beatmaps.GetAllUsableBeatmapSets(); - var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID < 0 || b.RulesetID == ruleset.LegacyID); + var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID == null || ruleset.LegacyID < 0 || b.RulesetID == ruleset.LegacyID); allBeatmaps.ForEach(b => beatmapDisplays.Add(new BeatmapDisplay(b))); } From 9c75c392f2edb4452f48a2e7ab9bb4e2b40de47e Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sun, 11 Mar 2018 21:27:49 +0100 Subject: [PATCH 093/537] Only check by null instead of sign --- osu.Game/Rulesets/RulesetStore.cs | 4 ++-- osu.Game/Tests/Visual/TestCasePerformancePoints.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 4891b46c9d..e621c3cf2b 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, (RulesetInfo)null)).ToList(); //add all legacy modes in correct order - foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) + foreach (var r in instances.Where(r => r.LegacyID != null).OrderBy(r => r.LegacyID)) { if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == r.RulesetInfo.ID) == null) context.RulesetInfo.Add(r.RulesetInfo); @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets context.SaveChanges(); //add any other modes - foreach (var r in instances.Where(r => r.LegacyID == null || r.LegacyID < 0)) + foreach (var r in instances.Where(r => r.LegacyID == null)) if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) context.RulesetInfo.Add(r.RulesetInfo); diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index c35c0308f9..7ca69c14b8 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual private void load(BeatmapManager beatmaps) { var sets = beatmaps.GetAllUsableBeatmapSets(); - var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID == null || ruleset.LegacyID < 0 || b.RulesetID == ruleset.LegacyID); + var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID == null || b.RulesetID == ruleset.LegacyID); allBeatmaps.ForEach(b => beatmapDisplays.Add(new BeatmapDisplay(b))); } From 33c721bcbb51d50f9d60d656208eb31538f7b6b2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 09:51:32 +0900 Subject: [PATCH 094/537] Fix post-merge errors --- osu-framework | 2 +- osu.Game/Screens/Play/Player.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 865b0df18b..59004b46f2 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 865b0df18bb240190cdf7a7f60d44c0b28c84c5f +Subproject commit 59004b46f2c96ac02fec712e66f9f96fe252f2fa diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 6453a72fc3..c8ff261a93 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -25,7 +25,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Ranking; using osu.Game.Storyboards.Drawables; From 209d91fe2131a8a9c8fd62f5be117bd6b2a69331 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 10:35:37 +0900 Subject: [PATCH 095/537] Fix duplicate item in csproj --- osu.Game/osu.Game.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 562f0ff788..4943db6852 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -379,7 +379,6 @@ - From 86d93ffe3c59a3b1fa57d4a8855298ac3ecc39b9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 10:49:50 +0900 Subject: [PATCH 096/537] Fix tooltip not working due to not handling input --- osu.Game/Graphics/DrawableDate.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 452443f9d0..a912f989e0 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -58,6 +58,8 @@ namespace osu.Game.Graphics Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate); } + public override bool HandleMouseInput => true; + private void updateTime() => Text = date.Humanize(); public string TooltipText => date.ToString(); } From 1447ca55a3a128a3adb0fee0206178893640e256 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Mar 2018 11:02:02 +0900 Subject: [PATCH 097/537] Add xmldoc, make restrictSize private --- osu.Game/Skinning/SkinnableDrawable.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index a5f22f60a2..81abc9e80c 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -22,16 +22,20 @@ namespace osu.Game.Skinning private readonly string componentName; - /// - /// Whether a user-skin drawable should be limited to the size of our parent. - /// - public readonly bool RestrictSize; + private readonly bool restrictSize; + /// + /// + /// + /// The namespace-complete resource name for this skinnable element. + /// A function to create the default skin implementation of this element. + /// Whther to fallback to the default implementation when a custom skin is specified but not implementation is present. + /// Whether a user-skin drawable should be limited to the size of our parent. public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true, bool restrictSize = true) : base(fallback) { componentName = name; createDefault = defaultImplementation; - RestrictSize = restrictSize; + this.restrictSize = restrictSize; RelativeSizeAxes = Axes.Both; } @@ -41,7 +45,7 @@ namespace osu.Game.Skinning var drawable = skin.GetDrawableComponent(componentName); if (drawable != null) { - if (RestrictSize) + if (restrictSize) { drawable.RelativeSizeAxes = Axes.Both; drawable.Size = Vector2.One; From c70be29edacbdebb10dd874d15cd6dda145ef5ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Mar 2018 11:30:13 +0900 Subject: [PATCH 098/537] Move legacy conversion to LegacySkin --- .../Rulesets/Judgements/DrawableJudgement.cs | 19 +------------------ osu.Game/Skinning/LegacySkin.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 4664517312..8639812aff 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -40,24 +40,7 @@ namespace osu.Game.Rulesets.Judgements [BackgroundDependencyLoader] private void load(OsuColour colours) { - string legacyName = string.Empty; - switch (Judgement.Result) - { - case HitResult.Miss: - legacyName = "hit0"; - break; - case HitResult.Meh: - legacyName = "hit50"; - break; - case HitResult.Good: - legacyName = "hit100"; - break; - case HitResult.Great: - legacyName = "hit300"; - break; - } - - Child = new SkinnableDrawable($"Play/osu/{legacyName}", _ => JudgementText = new OsuSpriteText + Child = new SkinnableDrawable($"Play/{Judgement.Result}", _ => JudgementText = new OsuSpriteText { Text = Judgement.Result.GetDescription().ToUpper(), Font = @"Venera", diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index f03d1ce632..2caeed8480 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -29,6 +29,22 @@ namespace osu.Game.Skinning public override Drawable GetDrawableComponent(string componentName) { + switch (componentName) + { + case "Play/Miss": + componentName = "hit0"; + break; + case "Play/Meh": + componentName = "hit50"; + break; + case "Play/Good": + componentName = "hit100"; + break; + case "Play/Great": + componentName = "hit300"; + break; + } + var texture = textures.Get(componentName); if (texture == null) return null; From 1bcda4930ebc6736a239b4e6e4710525b03d3d4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Mar 2018 11:33:12 +0900 Subject: [PATCH 099/537] Add back beatmap version set --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index c54d81aa2b..915ea9b587 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -52,6 +52,8 @@ namespace osu.Game.Beatmaps.Formats protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) { this.beatmap = beatmap; + this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion; + base.ParseStreamInto(stream, beatmap); // objects may be out of order *only* if a user has manually edited an .osu file. From 46caab6310d74a0b3d7ba36a3edea53aca6b17b0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 11:56:49 +0900 Subject: [PATCH 100/537] Reorder arithmetic operation --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index be3a0e02db..cfa5ef88b8 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Mods { var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; - rulesetContainer.Objects.OfType().ForEach(h => h.Column = -h.Column + availableColumns - 1); + rulesetContainer.Objects.OfType().ForEach(h => h.Column = availableColumns - 1 - h.Column); } } } From fbb80edde1888523ff730d31d186a5d1466e3849 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 13:01:29 +0900 Subject: [PATCH 101/537] Minor cleanups --- osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index 02560c8141..f4e3d54a3d 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -32,11 +32,13 @@ namespace osu.Game.Rulesets.Mania private readonly List difficultyHitObjects = new List(); public ManiaDifficultyCalculator(Beatmap beatmap) - : base(beatmap) { + : base(beatmap) + { } public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods) - : base(beatmap, mods) { + : base(beatmap, mods) + { } public override double Calculate(Dictionary categoryDifficulty = null) @@ -58,7 +60,6 @@ namespace osu.Game.Rulesets.Mania double starRating = calculateDifficulty() * star_scaling_factor; categoryDifficulty?.Add("Strain", starRating); - // categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate); return starRating; } From 81186f8423198b76a97a2d9846281d4a612cbe56 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 13:06:42 +0900 Subject: [PATCH 102/537] Apply beatmap converter mods in DifficultyCalculator --- osu.Game/Beatmaps/DifficultyCalculator.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 798268d05f..d61c62a30b 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -24,9 +24,15 @@ namespace osu.Game.Beatmaps protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null) { - Beatmap = CreateBeatmapConverter(beatmap).Convert(beatmap); Mods = mods ?? new Mod[0]; + var converter = CreateBeatmapConverter(beatmap); + + foreach (var mod in Mods.OfType>()) + mod.ApplyToBeatmapConverter(converter); + + Beatmap = converter.Convert(beatmap); + ApplyMods(Mods); PreprocessHitObjects(); From 3cd203699b485067d79eed122e27fd822895e695 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 13:09:19 +0900 Subject: [PATCH 103/537] Apply beatmap converter mods in PerformanceCalculator --- osu.Game/Rulesets/Scoring/PerformanceCalculator.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs index ba16d78b37..c047a421fd 100644 --- a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs @@ -2,7 +2,9 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Scoring @@ -23,9 +25,15 @@ namespace osu.Game.Rulesets.Scoring protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) { - Beatmap = CreateBeatmapConverter().Convert(beatmap); Score = score; + var converter = CreateBeatmapConverter(); + + foreach (var mod in score.Mods.OfType>()) + mod.ApplyToBeatmapConverter(converter); + + Beatmap = converter.Convert(beatmap); + var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods); diffCalc.Calculate(attributes); } From 2d9fcdcbd02c3f92c608d5f63ab0a7ab8705ff94 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 17:18:50 +0900 Subject: [PATCH 104/537] Fix slider circle overlays moving with the endpoints --- .../Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs index 3c7f8a067b..50a325d6cc 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK; @@ -14,12 +15,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays public class SliderCircleOverlay : HitObjectOverlay { public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider) - : this(sliderHead, sliderHead.Position, slider) + : this(sliderHead, Vector2.Zero, slider) { } public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider) - : this(sliderTail, sliderTail.Position, slider) + : this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider) { } From 212142429f7ff0e6322c8b404a1ff90114458022 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 17:25:34 +0900 Subject: [PATCH 105/537] Derive from IHasPosition --- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 574063d4a5..d9aed23414 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.Edit.Types; namespace osu.Game.Rulesets.Osu.Objects { - public abstract class OsuHitObject : HitObject, IHasCombo, IHasPosition, IHasEditablePosition + public abstract class OsuHitObject : HitObject, IHasCombo, IHasEditablePosition { public const double OBJECT_RADIUS = 64; diff --git a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs index 3530dba8f4..fa101ed835 100644 --- a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs +++ b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs @@ -1,11 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Rulesets.Objects.Types; using OpenTK; namespace osu.Game.Rulesets.Edit.Types { - public interface IHasEditablePosition + public interface IHasEditablePosition : IHasPosition { void OffsetPosition(Vector2 offset); } From 8c4bcb4a04a8c45598c92c56f5ee65e6f7a2f761 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 17:33:10 +0900 Subject: [PATCH 106/537] Only accept drag movement on the overlays --- .../Edit/Layers/Selection/Overlays/SliderOverlay.cs | 3 +++ osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs index f63d8f0c62..d478130868 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Edit.Layers.Selection; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using OpenTK; using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays @@ -56,5 +57,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays // Need to cause one update body.UpdateProgress(0); } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos); } } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs index c20769a912..c3bb5911f8 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs @@ -68,6 +68,8 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection Position = topLeft; } + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => overlays.Any(o => o.ReceiveMouseInputAt(screenSpacePos)); + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; protected override bool OnDragStart(InputState state) => true; From 7406cb290f655bb4953549fb6510eb13241df94e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 19:41:09 +0900 Subject: [PATCH 107/537] Split out test beatmap from TestCasePlayer into instantiable class --- .../Tests/TestCaseBananaShower.cs | 8 +- .../Tests/TestCaseCatchStacker.cs | 9 +- .../Tests/TestCaseHyperdash.cs | 5 +- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 725 ++++++++++++++++++ osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 7 +- osu.Game/Tests/Visual/TestCasePlayer.cs | 711 +---------------- osu.Game/osu.Game.csproj | 1 + 7 files changed, 744 insertions(+), 722 deletions(-) create mode 100644 osu.Game/Tests/Beatmaps/TestBeatmap.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseBananaShower.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseBananaShower.cs index ecae154075..ec9dd15673 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseBananaShower.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseBananaShower.cs @@ -28,16 +28,14 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override Beatmap CreateBeatmap() + protected override Beatmap CreateBeatmap(Ruleset ruleset) { var beatmap = new Beatmap { BeatmapInfo = new BeatmapInfo { - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 6, - } + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, + Ruleset = ruleset.RulesetInfo } }; diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs index 518845208c..8e5843f40a 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs @@ -15,19 +15,18 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override Beatmap CreateBeatmap() + protected override Beatmap CreateBeatmap(Ruleset ruleset) { var beatmap = new Beatmap { BeatmapInfo = new BeatmapInfo { - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 6, - } + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, + Ruleset = ruleset.RulesetInfo } }; + for (int i = 0; i < 512; i++) beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 }); diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index c01791a923..7564adea8c 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -15,9 +15,10 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override Beatmap CreateBeatmap() + protected override Beatmap CreateBeatmap(Ruleset ruleset) { - var beatmap = new Beatmap(); + var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } }; + for (int i = 0; i < 512; i++) if (i % 5 < 3) diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs new file mode 100644 index 0000000000..a29ee03975 --- /dev/null +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -0,0 +1,725 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using System.Text; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using Decoder = osu.Game.Beatmaps.Formats.Decoder; + +namespace osu.Game.Tests.Beatmaps +{ + public class TestBeatmap : Beatmap + { + public TestBeatmap(RulesetInfo ruleset) + : base(createTestBeatmap()) + { + BeatmapInfo.Ruleset = ruleset; + } + + private static Beatmap testBeatmapCache; + private static Beatmap createTestBeatmap() + { + if (testBeatmapCache != null) + return testBeatmapCache; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) + using (var reader = new StreamReader(stream)) + return testBeatmapCache = Decoder.GetDecoder(reader).DecodeBeatmap(reader); + } + + private const string test_beatmap_data = +@"osu file format v14 + +[General] +AudioLeadIn: 500 +PreviewTime: 53498 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.7 +Mode: 0 +LetterboxInBreaks: 0 +WidescreenStoryboard: 1 + +[Editor] +DistanceSpacing: 1.2 +BeatDivisor: 4 +GridSize: 4 +TimelineZoom: 1 + +[Metadata] +Title:My Love +TitleUnicode:My Love +Artist:Kuba Oms +ArtistUnicode:Kuba Oms +Creator:W h i t e +Version:Hard +Source:ADHD +Tags:Monthly Beatmapping Contest Electronic folk pop w_h_i_t_e +BeatmapID:397534 +BeatmapSetID:163112 + +[Difficulty] +HPDrainRate:5 +CircleSize:4 +OverallDifficulty:6 +ApproachRate:7 +SliderMultiplier:1.44 +SliderTickRate:2 + +[Events] +//Break Periods +2,69870,83770 +2,152170,158770 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +2170,468.75,4,2,0,40,1,0 +4045,-100,4,2,0,30,0,0 +4162,-100,4,2,0,40,0,0 +5920,-100,4,2,0,30,0,0 +6037,-100,4,2,0,40,0,0 +7795,-100,4,2,0,30,0,0 +7912,-100,4,2,0,40,0,0 +9670,-100,4,2,0,40,0,0 +9787,-100,4,2,0,50,0,0 +11545,-100,4,2,0,40,0,0 +11662,-100,4,2,0,50,0,0 +13420,-100,4,2,0,40,0,0 +13537,-100,4,2,0,50,0,0 +15295,-100,4,2,0,40,0,0 +15412,-100,4,2,0,50,0,0 +17170,-100,4,2,0,40,0,0 +17287,-100,4,2,0,50,0,0 +19045,-100,4,2,0,40,0,0 +19162,-100,4,2,0,50,0,0 +20920,-100,4,2,0,40,0,0 +21037,-100,4,2,0,50,0,0 +22795,-100,4,2,0,40,0,0 +22912,-100,4,2,0,50,0,0 +24670,-100,4,2,0,70,0,0 +37560,-200,4,2,0,30,0,0 +38263,-200,4,2,0,5,0,0 +38966,-100,4,2,0,30,0,0 +39670,-100,4,2,0,70,0,0 +53732,-100,4,2,0,40,0,0 +54670,-100,4,2,0,80,0,1 +55138,-100,4,2,0,60,0,1 +55255,-100,4,2,0,80,0,1 +56076,-100,4,2,0,60,0,1 +56193,-100,4,2,0,80,0,1 +57013,-100,4,2,0,60,0,1 +57130,-100,4,2,0,80,0,1 +57951,-100,4,2,0,60,0,1 +58068,-100,4,2,0,80,0,1 +58888,-100,4,2,0,60,0,1 +59005,-100,4,2,0,80,0,1 +59826,-100,4,2,0,60,0,1 +59943,-100,4,2,0,80,0,1 +60763,-100,4,2,0,60,0,1 +60880,-100,4,2,0,80,0,1 +61701,-100,4,2,0,60,0,1 +61818,-100,4,2,0,80,0,1 +62638,-100,4,2,0,60,0,1 +62755,-100,4,2,0,80,0,1 +63576,-100,4,2,0,60,0,1 +63693,-100,4,2,0,80,0,1 +64513,-100,4,2,0,60,0,1 +64630,-100,4,2,0,80,0,1 +65451,-100,4,2,0,60,0,1 +65568,-100,4,2,0,80,0,1 +66388,-100,4,2,0,60,0,1 +66505,-100,4,2,0,80,0,1 +67326,-100,4,2,0,60,0,1 +67443,-100,4,2,0,80,0,1 +68263,-100,4,2,0,60,0,1 +68380,-100,4,2,0,80,0,1 +69201,-100,4,2,0,60,0,1 +69318,-100,4,2,0,80,0,1 +69670,-100,4,2,0,70,0,0 +84670,-100,4,2,0,70,0,0 +97560,-200,4,2,0,70,0,0 +97795,-200,4,2,0,30,0,0 +98966,-100,4,2,0,30,0,0 +99670,-100,4,2,0,70,0,0 +113732,-100,4,2,0,40,0,0 +114670,-100,4,2,0,80,0,1 +115138,-100,4,2,0,60,0,1 +115255,-100,4,2,0,80,0,1 +116076,-100,4,2,0,60,0,1 +116193,-100,4,2,0,80,0,1 +117013,-100,4,2,0,60,0,1 +117130,-100,4,2,0,80,0,1 +117951,-100,4,2,0,60,0,1 +118068,-100,4,2,0,80,0,1 +118888,-100,4,2,0,60,0,1 +119005,-100,4,2,0,80,0,1 +119826,-100,4,2,0,60,0,1 +119943,-100,4,2,0,80,0,1 +120763,-100,4,2,0,60,0,1 +120880,-100,4,2,0,80,0,1 +121701,-100,4,2,0,60,0,1 +121818,-100,4,2,0,80,0,1 +122638,-100,4,2,0,60,0,1 +122755,-100,4,2,0,80,0,1 +123576,-100,4,2,0,60,0,1 +123693,-100,4,2,0,80,0,1 +124513,-100,4,2,0,60,0,1 +124630,-100,4,2,0,80,0,1 +125451,-100,4,2,0,60,0,1 +125568,-100,4,2,0,80,0,1 +126388,-100,4,2,0,60,0,1 +126505,-100,4,2,0,80,0,1 +127326,-100,4,2,0,60,0,1 +127443,-100,4,2,0,80,0,1 +128263,-100,4,2,0,60,0,1 +128380,-100,4,2,0,80,0,1 +129201,-100,4,2,0,60,0,1 +129318,-100,4,2,0,80,0,1 +129670,-200,4,2,0,40,0,0 +144670,-133.333333333333,4,2,0,40,0,0 +159670,-133.333333333333,4,2,0,40,0,0 +163420,-133.333333333333,4,2,0,45,0,0 +163888,-125,4,2,0,50,0,0 +164357,-117.647058823529,4,2,0,55,0,0 +164826,-111.111111111111,4,2,0,60,0,0 +165295,-105.263157894737,4,2,0,65,0,0 +165763,-100,4,2,0,70,0,0 +166232,-100,4,2,0,40,0,0 +167170,-100,4,2,0,80,0,1 +167638,-100,4,2,0,60,0,1 +167755,-100,4,2,0,80,0,1 +168576,-100,4,2,0,60,0,1 +168693,-100,4,2,0,80,0,1 +169513,-100,4,2,0,60,0,1 +169630,-100,4,2,0,80,0,1 +170451,-100,4,2,0,60,0,1 +170568,-100,4,2,0,80,0,1 +171388,-100,4,2,0,60,0,1 +171505,-100,4,2,0,80,0,1 +172326,-100,4,2,0,60,0,1 +172443,-100,4,2,0,80,0,1 +173263,-100,4,2,0,60,0,1 +173380,-100,4,2,0,80,0,1 +174201,-100,4,2,0,60,0,1 +174318,-100,4,2,0,80,0,1 +175138,-100,4,2,0,60,0,1 +175255,-100,4,2,0,80,0,1 +176076,-100,4,2,0,60,0,1 +176193,-100,4,2,0,80,0,1 +177013,-100,4,2,0,60,0,1 +177130,-100,4,2,0,80,0,1 +177951,-100,4,2,0,60,0,1 +178068,-100,4,2,0,80,0,1 +178888,-100,4,2,0,60,0,1 +179005,-100,4,2,0,80,0,1 +179826,-100,4,2,0,60,0,1 +179943,-100,4,2,0,80,0,1 +180763,-100,4,2,0,60,0,1 +180880,-100,4,2,0,80,0,1 +180998,-100,4,2,0,80,0,0 +181466,-100,4,2,0,60,0,0 +181584,-100,4,2,0,80,0,0 +181935,-100,4,2,0,80,0,0 +182170,-100,4,2,0,80,0,1 +182638,-100,4,2,0,60,0,1 +182755,-100,4,2,0,80,0,1 +183576,-100,4,2,0,60,0,1 +183693,-100,4,2,0,80,0,1 +184513,-100,4,2,0,60,0,1 +184630,-100,4,2,0,80,0,1 +185451,-100,4,2,0,60,0,1 +185568,-100,4,2,0,80,0,1 +186388,-100,4,2,0,60,0,1 +186505,-100,4,2,0,80,0,1 +187326,-100,4,2,0,60,0,1 +187443,-100,4,2,0,80,0,1 +188263,-100,4,2,0,60,0,1 +188380,-100,4,2,0,80,0,1 +189201,-100,4,2,0,60,0,1 +189318,-100,4,2,0,80,0,1 +190138,-100,4,2,0,60,0,1 +190255,-100,4,2,0,80,0,1 +191076,-100,4,2,0,60,0,1 +191193,-100,4,2,0,80,0,1 +192013,-100,4,2,0,60,0,1 +192130,-100,4,2,0,80,0,1 +192951,-100,4,2,0,60,0,1 +193068,-100,4,2,0,80,0,1 +193888,-100,4,2,0,60,0,1 +194005,-100,4,2,0,80,0,1 +194826,-100,4,2,0,60,0,1 +194943,-100,4,2,0,80,0,1 +195295,-100,4,2,0,50,0,1 +195529,-100,4,2,0,52,0,1 +195646,-100,4,2,0,54,0,1 +195763,-100,4,2,0,56,0,1 +195880,-100,4,2,0,58,0,1 +195998,-100,4,2,0,60,0,1 +196115,-100,4,2,0,62,0,1 +196232,-100,4,2,0,64,0,1 +196349,-100,4,2,0,68,0,1 +196466,-100,4,2,0,70,0,1 +196584,-100,4,2,0,72,0,1 +196701,-100,4,2,0,74,0,1 +196818,-100,4,2,0,76,0,1 +196935,-100,4,2,0,78,0,1 +197052,-100,4,2,0,80,0,1 +197170,-100,4,2,0,80,0,0 +197873,-100,4,2,0,60,0,0 +197990,-100,4,2,0,80,0,0 +198341,-100,4,2,0,60,0,0 +199045,-100,4,2,0,80,0,0 +199279,-100,4,2,0,60,0,0 +199630,-100,4,2,0,80,0,0 +200216,-100,4,2,0,60,0,0 +200334,-100,4,2,0,80,0,0 +201623,-100,4,2,0,60,0,0 +201740,-100,4,2,0,80,0,0 +202326,-100,4,2,0,60,0,0 +202443,-100,4,2,0,80,0,0 +203029,-100,4,2,0,60,0,0 +203498,-100,4,2,0,80,0,0 +203966,-100,4,2,0,60,0,0 +204201,-100,4,2,0,80,0,0 +205373,-100,4,2,0,60,0,0 +205490,-100,4,2,0,80,0,0 +205841,-100,4,2,0,60,0,0 +206076,-100,4,2,0,60,0,0 +206545,-100,4,2,0,80,0,0 +206779,-100,4,2,0,60,0,0 +207130,-100,4,2,0,80,0,0 +207716,-100,4,2,0,60,0,0 +207951,-100,4,2,0,80,0,0 +209123,-100,4,2,0,60,0,0 +209240,-100,4,2,0,80,0,0 +209826,-100,4,2,0,60,0,0 +209943,-100,4,2,0,80,0,0 +210529,-100,4,2,0,60,0,0 +210880,-100,4,2,0,80,0,0 +211232,-100,4,2,0,60,0,0 +211701,-100,4,2,0,70,0,0 +212170,-100,4,2,0,80,0,0 +212873,-100,4,2,0,60,0,0 +212990,-100,4,2,0,80,0,0 +213341,-100,4,2,0,60,0,0 +213576,-100,4,2,0,60,0,0 +214045,-100,4,2,0,80,0,0 +214279,-100,4,2,0,60,0,0 +214630,-100,4,2,0,80,0,0 +215216,-100,4,2,0,60,0,0 +215451,-100,4,2,0,80,0,0 +216623,-100,4,2,0,60,0,0 +216740,-100,4,2,0,80,0,0 +217326,-100,4,2,0,60,0,0 +217443,-100,4,2,0,80,0,0 +218029,-100,4,2,0,60,0,0 +218498,-100,4,2,0,80,0,0 +218732,-100,4,2,0,50,0,0 +219670,-100,4,2,0,70,0,0 +220138,-100,4,2,0,65,0,0 +220373,-100,4,2,0,45,0,0 +220490,-100,4,2,0,65,0,0 +220607,-100,4,2,0,60,0,0 +220841,-100,4,2,0,35,0,0 +221076,-100,4,2,0,35,0,0 +221545,-100,4,2,0,50,0,0 +221779,-100,4,2,0,30,0,0 +222013,-111.111111111111,4,2,0,25,0,0 +222130,-111.111111111111,4,2,0,40,0,0 +222482,-125,4,2,0,40,0,0 +222716,-125,4,2,0,20,0,0 +222951,-100,4,2,0,15,0,0 +223420,-100,4,2,0,30,0,0 +224357,-100,4,2,0,25,0,0 +225295,-100,4,2,0,20,0,0 +226232,-100,4,2,0,15,0,0 +226701,-100,4,2,0,10,0,0 +227170,-100,4,2,0,5,0,0 + + +[Colours] + Combo1 : 17,254,176 +Combo2 : 173,255,95 +Combo3 : 255,88,100 +Combo4 : 255,94,55 + +[HitObjects] +320,256,2170,6,0,P|256:284|192:256,1,144,4|0,0:0|0:0,0:0:0:0: +144,184,2873,1,0,0:0:0:0: +108,260,3107,2,0,P|112:296|100:336,1,72 +28,288,3576,2,0,P|24:252|36:212,1,72,0|0,0:0|0:0,0:0:0:0: +76,140,4045,6,0,L|220:136,1,144,4|0,0:0|0:0,0:0:0:0: +292,88,4748,1,0,0:0:0:0: +292,88,4982,2,0,P|304:120|300:168,1,72 +388,168,5451,2,0,P|396:133|416:103,1,72,0|0,0:0|0:0,0:0:0:0: +472,172,5920,6,0,B|470:200|457:222|457:222|488:256|476:308,1,144,4|0,0:0|0:0,0:0:0:0: +396,280,6623,1,0,0:0:0:0: +324,328,6857,2,0,P|288:332|252:324,1,72 +180,280,7326,2,0,L|108:284,1,72,0|0,0:0|0:0,0:0:0:0: +256,192,7795,12,0,9670,0:0:0:0: +428,212,10138,1,0,0:0:0:0: +292,320,10607,1,0,0:0:0:0: +184,184,11076,2,0,L|112:180,1,72,0|0,0:0|0:0,0:0:0:0: +24,172,11545,5,6,0:0:0:0: +160,280,12013,1,0,0:0:0:0: +268,144,12482,1,0,0:0:0:0: +132,36,12951,2,0,L|204:32,1,72,0|0,0:0|0:0,0:0:0:0: +284,60,13420,6,0,P|340:100|344:180,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0: +268,144,14591,1,0,0:0:0:0: +284,228,14826,2,0,P|316:248|364:252,1,72,0|0,0:0|0:0,0:0:0:0: +436,248,15295,6,0,P|372:272|344:340,1,144,6|2,0:0|0:0,0:0:0:0: +168,338,16232,2,0,P|141:273|76:248,1,144,2|2,0:0|0:0,0:0:0:0: +4,296,16935,1,0,0:0:0:0: +80,336,17170,5,6,0:0:0:0: +44,168,17638,1,0,0:0:0:0: +212,128,18107,1,0,0:0:0:0: +248,296,18576,2,0,P|284:288|320:292,1,72,0|0,0:0|0:0,0:0:0:0: +400,324,19045,5,6,0:0:0:0: +280,200,19513,1,0,0:0:0:0: +368,52,19982,1,0,0:0:0:0: +488,176,20451,2,0,P|452:168|416:172,1,72,0|0,0:0|0:0,0:0:0:0: +336,200,20920,6,0,P|284:216|200:192,1,144,6|0,0:0|0:0,0:0:0:0: +200,192,21857,2,0,L|204:264,1,72,0|0,0:3|0:0,0:0:0:0: +117,244,22326,2,0,L|120:172,1,72,0|0,0:0|0:0,0:0:0:0: +40,152,22795,6,0,L|28:296,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0: +152,24,24201,1,0,0:0:0:0: +220,76,24435,1,0,3:0:0:0: +304,56,24670,6,0,P|288:120|296:196,1,144,4|2,0:3|0:3,0:0:0:0: +344,268,25373,1,0,0:0:0:0: +416,316,25607,2,0,P|452:312|508:316,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +244,344,26545,6,0,P|176:356|108:328,1,144,4|2,0:3|0:3,0:0:0:0: +60,256,27248,1,0,0:0:0:0: +36,172,27482,2,0,L|40:100,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +188,252,28420,6,0,P|192:184|196:100,1,144,4|2,0:3|0:3,0:0:0:0: +140,40,29123,1,0,0:0:0:0: +140,40,29357,2,0,B|172:16|220:24|220:24|288:36,1,144,0|2,0:0|0:3,0:0:0:0: +364,52,30060,1,0,0:0:0:0: +308,116,30295,6,0,B|300:168|300:168|328:256,1,144,4|2,0:3|0:3,0:0:0:0: +340,340,30998,1,0,0:0:0:0: +260,308,31232,2,0,L|188:304,1,72,0|2,0:0|0:3,0:0:0:0: +100,296,31701,1,2,0:3:0:0: +136,374,31935,1,0,0:0:0:0: +152,224,32170,6,0,P|160:152|132:88,1,144,4|2,0:3|0:3,0:0:0:0: +56,48,32873,1,0,0:0:0:0: +60,136,33107,2,0,L|56:208,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +224,76,34045,6,0,P|289:104|360:96,1,144,4|2,0:3|0:3,0:0:0:0: +432,48,34748,1,0,0:0:0:0: +440,132,34982,2,0,B|432:156|432:156|436:204,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +448,304,35920,6,0,B|412:315|380:292|380:292|348:269|312:280,1,144,4|2,0:3|0:3,0:0:0:0: +332,364,36623,1,0,0:0:0:0: +247,339,36857,2,0,P|230:308|225:273,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +312,280,37560,6,0,L|316:172,1,108 +134,35,38966,5,0,0:0:0:0: +72,96,39201,2,0,P|119:119|171:111,1,108,0|0,0:0|0:0,0:0:0:0: +192,100,39670,6,0,L|200:172,1,72,4|2,0:0|0:0,0:0:0:0: +147,240,40138,2,0,P|133:272|132:308,1,72,0|2,1:0|0:0,0:0:0:0: +216,292,40607,2,0,B|260:308|260:308|356:292,1,144,4|0,2:3|1:0,1:0:0:0: +356,292,41310,1,2,0:0:0:0: +436,327,41545,6,0,P|441:292|435:257,1,72,4|2,0:3|0:0,0:0:0:0: +364,204,42013,2,0,P|336:144|352:68,1,144,0|4,1:0|2:3,1:0:0:0: +404,0,42716,1,2,0:0:0:0: +440,80,42951,2,0,B|464:84|464:84|512:80,1,72,0|2,1:0|0:0,0:0:0:0: +351,71,43420,6,0,B|296:68|296:68|268:76|268:76|196:72,1,144,4|0,2:3|1:0,1:0:0:0: +120,68,44123,1,2,0:0:0:0: +160,144,44357,2,0,P|172:180|168:232,1,72,4|2,0:3|0:0,0:0:0:0: +76,264,44826,2,0,P|76:228|88:194,1,72,0|2,1:0|0:0,0:0:0:0: +160,144,45295,5,4,0:3:0:0: +244,164,45529,1,2,0:0:0:0: +268,248,45763,2,0,L|344:252,1,72,0|2,1:0|0:0,0:0:0:0: +408,156,46232,2,0,L|336:159,1,72,4|2,0:3|0:0,0:0:0:0: +212,72,46701,2,0,L|288:76,1,72,0|2,1:0|0:0,0:0:0:0: +400,72,47170,6,0,P|464:96|488:172,1,144,4|0,2:0|1:0,1:0:0:0: +476,248,47873,1,2,0:0:0:0: +436,324,48107,2,0,L|284:320,1,144,4|0,2:3|1:0,1:0:0:0: +204,316,48810,1,2,0:0:0:0: +127,355,49045,6,0,P|120:321|124:285,1,72,4|2,0:3|0:0,0:0:0:0: +192,232,49513,2,0,L|335:228,1,144,0|4,1:0|2:3,1:0:0:0: +412,188,50216,1,2,0:0:0:0: +444,108,50451,2,0,P|452:72|448:36,1,72,0|2,1:0|0:0,0:0:0:0: +368,68,50920,6,0,B|332:79|300:56|300:56|268:33|232:44,1,144,4|0,2:3|1:0,1:0:0:0: +152,76,51623,1,2,0:0:0:0: +76,116,51857,2,0,L|80:268,1,144,4|0,2:3|1:0,1:0:0:0: +80,260,52560,1,2,0:0:0:0: +8,308,52795,6,0,P|34:334|69:346,1,72,4|2,0:3|0:0,0:0:0:0: +148,312,53263,2,0,P|163:278|162:241,1,72,0|2,1:0|0:0,0:0:0:0: +156,156,53732,5,0,3:0:0:0: +156,156,53966,1,2,0:0:0:0: +236,196,54201,2,0,L|312:192,1,72,8|0,0:3|0:0,0:0:0:0: +368,256,54670,6,0,P|392:216|352:116,1,144,4|2,0:0|1:2,0:0:0:0: +288,92,55373,1,0,0:0:0:0: +360,40,55607,2,0,L|432:36,1,72,4|0,0:3|3:0,0:0:0:0: +288,92,56076,2,0,L|216:88,1,72,2|0,1:2|0:0,0:0:0:0: +132,72,56545,6,0,P|172:88|200:184,1,144,4|2,0:3|1:2,0:0:0:0: +143,241,57248,1,0,0:0:0:0: +65,202,57482,2,0,P|87:174|119:157,1,72,4|0,0:3|3:0,0:0:0:0: +132,324,57951,2,0,P|98:312|72:288,1,72,2|0,1:2|0:0,0:0:0:0: +143,241,58420,6,0,L|288:240,1,144,4|2,0:3|1:2,0:0:0:0: +372,240,59123,1,0,0:0:0:0: +330,314,59357,2,0,P|318:350|322:390,1,72,4|0,0:3|3:0,0:0:0:0: +452,264,59826,2,0,P|453:228|442:194,1,72,2|0,1:2|0:0,0:0:0:0: +384,128,60295,6,0,B|336:144|336:144|244:128,1,144,4|2,0:3|1:2,0:0:0:0: +164,160,60998,2,0,P|160:116|168:88,1,72,0|4,0:0|0:3,0:0:0:0: +244,128,61466,2,0,P|248:172|240:200,1,72,0|2,3:0|1:2,0:0:0:0: +168,248,61935,1,0,0:0:0:0: +120,320,62170,6,0,P|196:328|252:272,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: +80,244,63341,1,0,3:0:0:0: +100,160,63576,2,0,L|24:156,1,72,2|0,1:2|0:0,0:0:0:0: +180,128,64045,6,0,P|249:138|304:94,1,144,4|2,0:3|1:2,0:0:0:0: +226,57,64748,1,0,0:0:0:0: +304,94,64982,2,0,L|300:166,1,72,4|0,0:3|3:0,0:0:0:0: +377,203,65451,2,0,L|388:132,1,72,2|0,1:2|0:0,0:0:0:0: +468,180,65920,6,0,L|432:328,1,144,4|2,0:3|1:2,0:0:0:0: +276,252,66857,2,0,P|208:248|140:280,1,144,4|2,0:3|1:2,0:0:0:0: +84,344,67560,1,0,0:0:0:0: +56,260,67795,6,0,L|52:188,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +168,128,68732,2,0,L|172:56,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +244,168,69435,1,0,0:0:0:0: +332,164,69670,1,4,0:3:0:0: +208,328,84670,6,0,P|224:264|216:188,1,144,4|2,0:3|0:3,0:0:0:0: +168,116,85373,1,0,0:0:0:0: +96,68,85607,2,0,P|60:72|4:68,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +268,40,86545,6,0,P|336:28|404:56,1,144,4|2,0:3|0:3,0:0:0:0: +452,128,87248,1,0,0:0:0:0: +476,212,87482,2,0,L|472:284,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +324,132,88420,6,0,P|320:200|316:284,1,144,4|2,0:3|0:3,0:0:0:0: +372,344,89123,1,0,0:0:0:0: +372,344,89357,2,0,B|340:368|292:360|292:360|224:348,1,144,0|2,0:0|0:3,0:0:0:0: +148,332,90060,1,0,0:0:0:0: +204,268,90295,6,0,B|212:216|212:216|184:128,1,144,4|2,0:3|0:3,0:0:0:0: +172,44,90998,1,0,0:0:0:0: +252,76,91232,2,0,L|324:80,1,72,0|2,0:0|0:3,0:0:0:0: +412,88,91701,1,2,0:3:0:0: +377,9,91935,1,0,0:0:0:0: +360,160,92170,6,0,P|352:232|380:296,1,144,4|2,0:3|0:3,0:0:0:0: +456,336,92873,1,0,0:0:0:0: +452,248,93107,2,0,L|456:176,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +288,308,94045,6,0,P|223:280|152:288,1,144,4|2,0:3|0:3,0:0:0:0: +80,336,94748,1,0,0:0:0:0: +72,252,94982,2,0,B|80:228|80:228|76:180,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +64,80,95920,6,0,B|100:69|132:92|132:92|164:115|200:104,1,144,4|2,0:3|0:3,0:0:0:0: +180,20,96623,1,0,0:0:0:0: +265,45,96857,2,0,P|282:76|287:111,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: +200,104,97560,1,0,0:0:0:0: +200,104,97677,1,0,0:0:0:0: +200,104,97795,6,0,B|196:142|217:166|217:166|176:180|160:220,1,144,4|0,0:3|0:0,0:0:0:0: +240,248,98966,5,0,0:0:0:0: +202,325,99201,2,0,P|254:333|301:309,1,108,0|0,0:0|0:0,0:0:0:0: +315,292,99670,6,0,L|323:220,1,72,4|2,0:0|0:0,0:0:0:0: +365,144,100138,2,0,P|379:112|380:76,1,72,0|2,1:0|0:0,0:0:0:0: +296,92,100607,2,0,B|252:76|252:76|156:92,1,144,4|0,2:3|1:0,1:0:0:0: +156,92,101310,1,2,0:0:0:0: +76,57,101545,6,0,P|71:92|77:127,1,72,4|2,0:3|0:0,0:0:0:0: +148,180,102013,2,0,P|176:240|160:316,1,144,0|4,1:0|2:3,1:0:0:0: +108,384,102716,1,2,0:0:0:0: +72,304,102951,2,0,B|48:300|48:300|0:304,1,72,0|2,1:0|0:0,0:0:0:0: +161,313,103420,6,0,B|216:316|216:316|244:308|244:308|316:312,1,144,4|0,2:3|1:0,1:0:0:0: +392,316,104123,1,2,0:0:0:0: +352,240,104357,2,0,P|340:204|344:152,1,72,4|2,0:3|0:0,0:0:0:0: +436,120,104826,2,0,P|436:156|424:190,1,72,0|2,1:0|0:0,0:0:0:0: +352,240,105295,5,4,0:3:0:0: +268,220,105529,1,2,0:0:0:0: +244,136,105763,2,0,L|168:132,1,72,0|2,1:0|0:0,0:0:0:0: +104,228,106232,2,0,L|176:225,1,72,4|2,0:3|0:0,0:0:0:0: +300,312,106701,2,0,L|224:308,1,72,0|2,1:0|0:0,0:0:0:0: +112,312,107170,6,0,P|48:288|24:212,1,144,4|0,2:0|1:0,1:0:0:0: +36,136,107873,1,2,0:0:0:0: +76,60,108107,2,0,L|228:64,1,144,4|0,2:3|1:0,1:0:0:0: +308,68,108810,1,2,0:0:0:0: +385,29,109045,6,0,P|392:63|388:99,1,72,4|2,0:3|0:0,0:0:0:0: +320,152,109513,2,0,L|177:156,1,144,0|4,1:0|2:3,1:0:0:0: +100,196,110216,1,2,0:0:0:0: +68,276,110451,2,0,P|60:312|64:348,1,72,0|2,1:0|0:0,0:0:0:0: +144,316,110920,6,0,B|180:305|212:328|212:328|244:351|280:340,1,144,4|0,2:3|1:0,1:0:0:0: +360,308,111623,1,2,0:0:0:0: +436,268,111857,2,0,L|432:116,1,144,4|0,2:3|1:0,1:0:0:0: +432,124,112560,1,2,0:0:0:0: +504,76,112795,6,0,P|478:50|443:38,1,72,4|2,0:3|0:0,0:0:0:0: +364,72,113263,2,0,P|349:106|350:143,1,72,0|2,1:0|0:0,0:0:0:0: +356,228,113732,5,0,3:0:0:0: +356,228,113966,1,2,0:0:0:0: +276,188,114201,2,0,L|200:192,1,72,8|0,0:3|0:0,0:0:0:0: +144,128,114670,6,0,P|120:168|160:268,1,144,4|2,0:0|1:2,0:0:0:0: +224,292,115373,1,0,0:0:0:0: +152,344,115607,2,0,L|80:348,1,72,4|0,0:3|3:0,0:0:0:0: +224,292,116076,2,0,L|296:296,1,72,2|0,1:2|0:0,0:0:0:0: +380,312,116545,6,0,P|340:296|312:200,1,144,4|2,0:3|1:2,0:0:0:0: +369,143,117248,1,0,0:0:0:0: +447,182,117482,2,0,P|425:210|393:227,1,72,4|0,0:3|3:0,0:0:0:0: +380,60,117951,2,0,P|414:72|440:96,1,72,2|0,1:2|0:0,0:0:0:0: +369,143,118420,6,0,L|224:144,1,144,4|2,0:3|1:2,0:0:0:0: +140,144,119123,1,0,0:0:0:0: +182,70,119357,2,0,P|194:34|190:-6,1,72,4|0,0:3|3:0,0:0:0:0: +60,120,119826,2,0,P|59:156|70:190,1,72,2|0,1:2|0:0,0:0:0:0: +128,256,120295,6,0,B|176:240|176:240|268:256,1,144,4|2,0:3|1:2,0:0:0:0: +348,224,120998,2,0,P|352:268|344:296,1,72,0|4,0:0|0:3,0:0:0:0: +268,256,121466,2,0,P|264:212|272:184,1,72,0|2,3:0|1:2,0:0:0:0: +344,136,121935,1,0,0:0:0:0: +392,64,122170,6,0,P|316:56|260:112,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: +432,140,123341,1,0,3:0:0:0: +412,224,123576,2,0,L|488:228,1,72,2|0,1:2|0:0,0:0:0:0: +332,256,124045,6,0,P|263:246|208:290,1,144,4|2,0:3|1:2,0:0:0:0: +286,327,124748,1,0,0:0:0:0: +208,290,124982,2,0,L|212:218,1,72,4|0,0:3|3:0,0:0:0:0: +135,181,125451,2,0,L|124:252,1,72,2|0,1:2|0:0,0:0:0:0: +44,204,125920,6,0,L|80:56,1,144,4|2,0:3|1:2,0:0:0:0: +236,132,126857,2,0,P|304:136|372:104,1,144,4|2,0:3|1:2,0:0:0:0: +428,40,127560,1,0,0:0:0:0: +456,124,127795,6,0,L|460:196,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +344,256,128732,2,0,L|340:328,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +268,216,129435,1,0,0:0:0:0: +180,220,129670,5,4,2:0:0:0: +256,40,130373,1,2,0:0:0:0: +64,68,131076,1,2,0:0:0:0: +92,136,131310,1,0,0:0:0:0: +64,204,131545,6,0,L|60:288,1,72 +31,343,132248,2,0,P|86:345|127:309,1,108 +332,220,133420,5,2,0:0:0:0: +256,40,134123,1,2,0:0:0:0: +448,68,134826,1,2,0:0:0:0: +420,136,135060,1,0,0:0:0:0: +448,204,135295,6,0,L|452:288,1,72,2|0,0:0|0:0,0:0:0:0: +480,343,135998,2,0,P|426:345|385:309,1,108 +256,192,137170,5,2,0:0:0:0: +156,360,137873,1,2,0:0:0:0: +356,360,138576,2,0,L|352:308,1,36,2|0,0:0|0:0,0:0:0:0: +304,268,139045,6,0,P|336:253|372:252,1,72 +448,260,139748,2,0,L|444:152,1,108 +256,192,140920,5,2,0:0:0:0: +356,24,141623,1,2,0:0:0:0: +156,24,142326,2,0,L|160:72,1,36,2|0,0:0|0:0,0:0:0:0: +208,116,142795,6,0,P|176:131|140:132,1,72,2|0,0:0|0:0,0:0:0:0: +64,124,143498,2,0,L|68:232,1,108 +68,232,144670,5,4,0:3:0:0: +216,320,145138,1,4,0:3:0:0: +304,172,145607,1,4,0:3:0:0: +156,84,146075,1,4,0:3:0:0: +296,320,146545,5,4,0:3:0:0: +208,172,147013,1,4,0:3:0:0: +356,84,147482,1,4,0:3:0:0: +444,232,147950,1,4,0:3:0:0: +296,320,148420,6,0,P|252:328|192:296,2,108.000004119873,4|4|4,0:3|0:3|0:3,0:0:0:0: +260,248,149591,1,0,0:0:0:0: +320,196,149826,2,0,L|316:140,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0: +120,236,159670,6,0,L|176:232,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0: +160,152,160138,2,0,L|104:156,1,54.0000020599366,2|0,0:0|0:0,0:0:0:0: +240,180,160607,2,0,P|292:188|344:172,1,108.000004119873,4|2,0:3|0:0,3:0:0:0: +408,120,161310,1,0,3:0:0:0: +424,200,161545,6,0,L|420:256,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0: +376,320,162013,2,0,P|396:328|480:304,2,108.000004119873,2|6|2,2:0|0:3|2:0,3:0:0:0: +312,268,163185,1,0,0:0:0:0: +296,348,163420,6,0,L|240:344,1,54.0000020599366,4|0,3:0|3:0,0:0:0:0: +160,320,163888,2,0,L|100:316,1,57.6,4|0,3:0|3:0,0:0:0:0: +64,232,164357,6,0,L|128:228,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0: +204,200,164825,2,0,L|268:196,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0: +232,108,165295,6,0,L|164:104,1,68.399998173523,4|0,3:0|3:0,0:0:0:0: +80,84,165763,2,0,L|4:80,1,72,4|0,3:0|3:0,0:0:0:0: +324,120,167170,6,0,P|388:128|456:92,1,144,4|2,0:0|1:2,0:0:0:0: +496,168,167873,1,0,0:0:0:0: +496,168,168107,2,0,P|484:204|488:256,1,72,4|0,0:3|3:0,0:0:0:0: +408,296,168576,2,0,P|398:261|378:231,1,72,2|0,1:2|0:0,0:0:0:0: +296,200,169045,6,0,B|228:228|156:204,1,144,4|2,0:3|1:2,0:0:0:0: +84,156,169748,1,0,0:0:0:0: +80,244,169982,2,0,L|76:316,1,72,4|0,0:3|3:0,0:0:0:0: +170,274,170451,2,0,L|156:204,1,72,2|0,1:2|0:0,0:0:0:0: +216,140,170920,6,0,L|284:276,1,144,4|2,0:3|1:2,0:0:0:0: +320,344,171623,1,0,0:0:0:0: +372,276,171857,2,0,P|366:240|349:207,1,72,4|0,0:3|3:0,0:0:0:0: +312,132,172326,2,0,L|276:60,1,72,2|0,1:2|0:0,0:0:0:0: +208,20,172795,6,0,P|272:36|348:12,1,144,4|2,0:3|1:2,0:0:0:0: +424,48,173498,2,0,L|412:132,1,72,0|4,0:0|0:3,0:0:0:0: +484,168,173966,2,0,L|472:252,1,72,0|2,3:0|1:2,0:0:0:0: +400,280,174435,1,0,0:0:0:0: +346,348,174670,6,0,P|414:363|472:324,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: +312,268,175841,1,0,3:0:0:0: +256,336,176076,2,0,L|184:332,1,72,2|0,1:2|0:0,0:0:0:0: +80,244,176545,6,0,B|140:248|140:248|164:244|164:244|223:247,1,144,4|2,0:3|1:2,0:0:0:0: +312,268,177248,1,0,0:0:0:0: +224,247,177482,2,0,P|240:215|272:187,1,72,4|0,0:3|3:0,0:0:0:0: +204,131,177951,2,0,P|233:111|275:103,1,72,2|0,1:2|0:0,0:0:0:0: +240,23,178420,6,0,B|280:15|316:35|316:35|376:71,1,144,4|2,0:3|1:2,0:0:0:0: +399,236,179357,2,0,B|359:244|323:224|323:224|263:188,1,144,4|2,0:3|1:2,0:0:0:0: +204,132,180060,1,0,0:0:0:0: +184,216,180295,6,0,L|188:288,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +120,156,180998,1,0,0:0:0:0: +56,96,181232,2,0,L|60:24,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: +36,180,181935,1,0,0:0:0:0: +100,240,182170,6,0,P|144:300|116:380,2,144,4|2|4,0:0|1:2|0:3,0:0:0:0: +60,316,183341,1,0,0:0:0:0: +220,352,183576,2,0,L|308:348,1,72,2|0,1:2|0:0,0:0:0:0: +396,264,184045,6,0,B|336:268|336:268|312:264|312:264|253:267,1,144,4|2,0:3|1:2,0:0:0:0: +253,267,184748,1,0,0:0:0:0: +268,180,184982,2,0,L|339:177,1,72,4|0,0:3|0:0,0:0:0:0: +164,280,185451,2,0,L|92:282,1,72,2|0,1:2|0:0,0:0:0:0: +52,208,185920,6,0,P|8:268|32:344,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: +140,212,187091,1,0,0:0:0:0: +92,284,187326,2,0,P|104:316|100:368,1,72,2|0,1:2|0:0,0:0:0:0: +52,208,187795,6,0,P|48:136|76:72,1,144,4|2,0:3|1:2,0:0:0:0: +160,52,188498,2,0,P|188:28|220:16,1,72,0|4,0:0|0:3,0:0:0:0: +232,100,188966,2,0,P|268:93|301:98,1,72,0|2,0:0|1:2,0:0:0:0: +372,152,189435,1,0,0:0:0:0: +420,224,189670,6,0,P|428:296|400:360,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: +372,152,190841,1,0,0:0:0:0: +392,68,191076,2,0,L|465:64,1,72,2|0,1:2|0:0,0:0:0:0: +304,92,191545,6,0,P|236:104|168:76,1,144,4|2,0:3|1:2,0:0:0:0: +108,12,192248,1,0,0:0:0:0: +168,76,192482,2,0,L|172:152,1,72,4|0,0:3|0:0,0:0:0:0: +80,136,192951,2,0,L|101:204,1,72,2|0,1:2|0:0,0:0:0:0: +12,220,193420,6,0,B|50:279|50:279|80:300|120:292,1,144,4|2,0:3|1:2,0:0:0:0: +284,232,194357,2,0,B|320:221|352:244|352:244|384:267|420:256,1,144,4|2,0:3|1:2,0:0:0:0: +488,200,195060,1,0,0:0:0:0: +507,284,195295,6,0,P|492:315|464:338,1,72,4|0,0:0|0:0,0:0:0:0: +380,356,195763,2,0,L|236:352,1,144,0|4,1:0|0:3,0:0:0:0: +152,328,196466,1,0,3:0:0:0: +64,336,196701,2,0,P|29:325|4:300,1,72,0|0,1:0|0:0,0:0:0:0: +76,252,197170,6,0,P|108:188|96:116,1,144,4|0,0:0|1:0,0:0:0:0: +36,56,197873,1,2,0:0:0:0: +120,32,198107,2,0,L|192:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +248,152,199045,6,0,P|280:168|304:196,1,72,4|2,0:3|0:0,0:0:0:0: +336,277,199513,2,0,P|306:296|269:303,1,72,2|0,1:2|0:0,0:0:0:0: +183,290,199982,2,0,P|180:254|193:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: +436,252,200920,6,0,P|404:188|416:116,1,144,4|0,0:3|1:0,0:0:0:0: +476,56,201623,1,2,0:0:0:0: +392,32,201857,2,0,L|320:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0: +264,152,202795,6,0,P|232:168|208:196,1,72,4|2,0:3|0:0,0:0:0:0: +176,277,203263,2,0,P|205:296|242:303,1,72,2|0,1:2|0:0,0:0:0:0: +329,290,203732,2,0,P|331:254|318:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: +72,324,204670,6,0,B|60:272|60:272|76:180,1,144,4|0,0:0|1:0,0:0:0:0: +92,96,205373,1,2,0:0:0:0: +8,124,205607,2,0,P|5:88|14:53,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +168,192,206545,6,0,P|200:174|237:173,1,72,4|2,0:3|0:0,0:0:0:0: +320,160,207013,2,0,P|318:196|301:229,1,72,2|0,1:2|0:0,0:0:0:0: +272,307,207482,2,0,P|240:287|221:256,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: +440,324,208420,6,0,B|452:272|452:272|436:180,1,144,4|0,0:3|1:0,0:0:0:0: +420,96,209123,1,2,0:0:0:0: +504,124,209357,2,0,P|507:88|498:53,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0: +344,192,210295,6,0,P|311:174|274:173,1,72,4|2,0:3|0:0,0:0:0:0: +190,156,210763,2,0,P|191:192|208:225,1,72,2|0,1:2|0:0,0:0:0:0: +288,256,211232,1,4,0:3:0:0: +132,332,211701,1,0,1:0:0:0: +28,192,212170,6,0,P|16:120|44:56,1,144,4|0,0:0|1:0,0:0:0:0: +120,16,212873,1,2,0:0:0:0: +204,32,213107,2,0,L|304:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +192,204,214045,6,0,P|196:240|216:272,1,72,4|2,0:3|0:0,0:0:0:0: +298,241,214513,2,0,P|327:219|345:186,1,72,6|0,1:2|0:0,0:0:0:0: +280,132,214982,2,0,P|246:117|209:118,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: +484,192,215920,6,0,P|496:120|468:56,1,144,4|0,0:3|1:0,0:0:0:0: +392,16,216623,1,2,0:0:0:0: +308,32,216857,2,0,L|208:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0: +320,204,217795,6,0,P|316:240|296:272,1,72,4|2,0:3|0:0,0:0:0:0: +213,241,218263,2,0,P|184:219|166:186,1,72,2|0,1:2|0:0,0:0:0:0: +232,132,218732,2,0,B|260:112|300:116|300:116|384:128,1,144,4|0,0:3|1:0,0:0:0:0: +348,336,219670,6,0,B|320:356|280:352|280:352|196:340,1,144,4|0,0:0|1:0,0:0:0:0: +124,328,220373,1,2,0:0:0:0: +54,276,220607,2,0,P|41:308|39:345,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: +156,80,221545,6,0,L|251:94,1,72,4|2,0:3|0:0,0:0:0:0: +212,169,222013,2,0,L|148:160,1,64.799998022461,2|0,1:2|0:0,0:0:0:0: +140,240,222482,2,0,L|216:252,2,57.6,4|2|0,0:3|0:0|1:0,0:0:0:0: +256,192,223420,12,0,227170,0:0:0:0: +"; + } +} diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index af482dc250..68bb3f6dd9 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -4,11 +4,17 @@ using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; +using osu.Game.Rulesets; namespace osu.Game.Tests.Beatmaps { public class TestWorkingBeatmap : WorkingBeatmap { + public TestWorkingBeatmap(RulesetInfo ruleset) + : this(new TestBeatmap(ruleset)) + { + } + public TestWorkingBeatmap(Beatmap beatmap) : base(beatmap.BeatmapInfo) { @@ -16,7 +22,6 @@ namespace osu.Game.Tests.Beatmaps } private readonly Beatmap beatmap; - protected override Beatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; protected override Track GetTrack() => new TrackVirtual(); diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index d835adb54f..b7c66a37e5 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -1,9 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.IO; using System.Linq; -using System.Text; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; @@ -59,24 +57,13 @@ namespace osu.Game.Tests.Visual } } - protected virtual Beatmap CreateBeatmap() - { - Beatmap beatmap; - - using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) - using (var reader = new StreamReader(stream)) - beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder(reader).Decode(reader); - - return beatmap; - } + protected virtual Beatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); private Player loadPlayerFor(RulesetInfo ri) => loadPlayerFor(ri.CreateInstance()); private Player loadPlayerFor(Ruleset r) { - var beatmap = CreateBeatmap(); - - beatmap.BeatmapInfo.Ruleset = r.RulesetInfo; + var beatmap = CreateBeatmap(r); working = new TestWorkingBeatmap(beatmap); working.Mods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) }; @@ -107,699 +94,5 @@ namespace osu.Game.Tests.Visual AllowLeadIn = false, AllowResults = false, }; - - private const string test_beatmap_data = - @"osu file format v14 - -[General] -AudioLeadIn: 500 -PreviewTime: 53498 -Countdown: 0 -SampleSet: Soft -StackLeniency: 0.7 -Mode: 0 -LetterboxInBreaks: 0 -WidescreenStoryboard: 1 - -[Editor] -DistanceSpacing: 1.2 -BeatDivisor: 4 -GridSize: 4 -TimelineZoom: 1 - -[Metadata] -Title:My Love -TitleUnicode:My Love -Artist:Kuba Oms -ArtistUnicode:Kuba Oms -Creator:W h i t e -Version:Hard -Source:ADHD -Tags:Monthly Beatmapping Contest Electronic folk pop w_h_i_t_e -BeatmapID:397534 -BeatmapSetID:163112 - -[Difficulty] -HPDrainRate:5 -CircleSize:4 -OverallDifficulty:6 -ApproachRate:7 -SliderMultiplier:1.44 -SliderTickRate:2 - -[Events] -//Break Periods -2,69870,83770 -2,152170,158770 -//Storyboard Layer 0 (Background) -//Storyboard Layer 1 (Fail) -//Storyboard Layer 2 (Pass) -//Storyboard Layer 3 (Foreground) -//Storyboard Sound Samples - -[TimingPoints] -2170,468.75,4,2,0,40,1,0 -4045,-100,4,2,0,30,0,0 -4162,-100,4,2,0,40,0,0 -5920,-100,4,2,0,30,0,0 -6037,-100,4,2,0,40,0,0 -7795,-100,4,2,0,30,0,0 -7912,-100,4,2,0,40,0,0 -9670,-100,4,2,0,40,0,0 -9787,-100,4,2,0,50,0,0 -11545,-100,4,2,0,40,0,0 -11662,-100,4,2,0,50,0,0 -13420,-100,4,2,0,40,0,0 -13537,-100,4,2,0,50,0,0 -15295,-100,4,2,0,40,0,0 -15412,-100,4,2,0,50,0,0 -17170,-100,4,2,0,40,0,0 -17287,-100,4,2,0,50,0,0 -19045,-100,4,2,0,40,0,0 -19162,-100,4,2,0,50,0,0 -20920,-100,4,2,0,40,0,0 -21037,-100,4,2,0,50,0,0 -22795,-100,4,2,0,40,0,0 -22912,-100,4,2,0,50,0,0 -24670,-100,4,2,0,70,0,0 -37560,-200,4,2,0,30,0,0 -38263,-200,4,2,0,5,0,0 -38966,-100,4,2,0,30,0,0 -39670,-100,4,2,0,70,0,0 -53732,-100,4,2,0,40,0,0 -54670,-100,4,2,0,80,0,1 -55138,-100,4,2,0,60,0,1 -55255,-100,4,2,0,80,0,1 -56076,-100,4,2,0,60,0,1 -56193,-100,4,2,0,80,0,1 -57013,-100,4,2,0,60,0,1 -57130,-100,4,2,0,80,0,1 -57951,-100,4,2,0,60,0,1 -58068,-100,4,2,0,80,0,1 -58888,-100,4,2,0,60,0,1 -59005,-100,4,2,0,80,0,1 -59826,-100,4,2,0,60,0,1 -59943,-100,4,2,0,80,0,1 -60763,-100,4,2,0,60,0,1 -60880,-100,4,2,0,80,0,1 -61701,-100,4,2,0,60,0,1 -61818,-100,4,2,0,80,0,1 -62638,-100,4,2,0,60,0,1 -62755,-100,4,2,0,80,0,1 -63576,-100,4,2,0,60,0,1 -63693,-100,4,2,0,80,0,1 -64513,-100,4,2,0,60,0,1 -64630,-100,4,2,0,80,0,1 -65451,-100,4,2,0,60,0,1 -65568,-100,4,2,0,80,0,1 -66388,-100,4,2,0,60,0,1 -66505,-100,4,2,0,80,0,1 -67326,-100,4,2,0,60,0,1 -67443,-100,4,2,0,80,0,1 -68263,-100,4,2,0,60,0,1 -68380,-100,4,2,0,80,0,1 -69201,-100,4,2,0,60,0,1 -69318,-100,4,2,0,80,0,1 -69670,-100,4,2,0,70,0,0 -84670,-100,4,2,0,70,0,0 -97560,-200,4,2,0,70,0,0 -97795,-200,4,2,0,30,0,0 -98966,-100,4,2,0,30,0,0 -99670,-100,4,2,0,70,0,0 -113732,-100,4,2,0,40,0,0 -114670,-100,4,2,0,80,0,1 -115138,-100,4,2,0,60,0,1 -115255,-100,4,2,0,80,0,1 -116076,-100,4,2,0,60,0,1 -116193,-100,4,2,0,80,0,1 -117013,-100,4,2,0,60,0,1 -117130,-100,4,2,0,80,0,1 -117951,-100,4,2,0,60,0,1 -118068,-100,4,2,0,80,0,1 -118888,-100,4,2,0,60,0,1 -119005,-100,4,2,0,80,0,1 -119826,-100,4,2,0,60,0,1 -119943,-100,4,2,0,80,0,1 -120763,-100,4,2,0,60,0,1 -120880,-100,4,2,0,80,0,1 -121701,-100,4,2,0,60,0,1 -121818,-100,4,2,0,80,0,1 -122638,-100,4,2,0,60,0,1 -122755,-100,4,2,0,80,0,1 -123576,-100,4,2,0,60,0,1 -123693,-100,4,2,0,80,0,1 -124513,-100,4,2,0,60,0,1 -124630,-100,4,2,0,80,0,1 -125451,-100,4,2,0,60,0,1 -125568,-100,4,2,0,80,0,1 -126388,-100,4,2,0,60,0,1 -126505,-100,4,2,0,80,0,1 -127326,-100,4,2,0,60,0,1 -127443,-100,4,2,0,80,0,1 -128263,-100,4,2,0,60,0,1 -128380,-100,4,2,0,80,0,1 -129201,-100,4,2,0,60,0,1 -129318,-100,4,2,0,80,0,1 -129670,-200,4,2,0,40,0,0 -144670,-133.333333333333,4,2,0,40,0,0 -159670,-133.333333333333,4,2,0,40,0,0 -163420,-133.333333333333,4,2,0,45,0,0 -163888,-125,4,2,0,50,0,0 -164357,-117.647058823529,4,2,0,55,0,0 -164826,-111.111111111111,4,2,0,60,0,0 -165295,-105.263157894737,4,2,0,65,0,0 -165763,-100,4,2,0,70,0,0 -166232,-100,4,2,0,40,0,0 -167170,-100,4,2,0,80,0,1 -167638,-100,4,2,0,60,0,1 -167755,-100,4,2,0,80,0,1 -168576,-100,4,2,0,60,0,1 -168693,-100,4,2,0,80,0,1 -169513,-100,4,2,0,60,0,1 -169630,-100,4,2,0,80,0,1 -170451,-100,4,2,0,60,0,1 -170568,-100,4,2,0,80,0,1 -171388,-100,4,2,0,60,0,1 -171505,-100,4,2,0,80,0,1 -172326,-100,4,2,0,60,0,1 -172443,-100,4,2,0,80,0,1 -173263,-100,4,2,0,60,0,1 -173380,-100,4,2,0,80,0,1 -174201,-100,4,2,0,60,0,1 -174318,-100,4,2,0,80,0,1 -175138,-100,4,2,0,60,0,1 -175255,-100,4,2,0,80,0,1 -176076,-100,4,2,0,60,0,1 -176193,-100,4,2,0,80,0,1 -177013,-100,4,2,0,60,0,1 -177130,-100,4,2,0,80,0,1 -177951,-100,4,2,0,60,0,1 -178068,-100,4,2,0,80,0,1 -178888,-100,4,2,0,60,0,1 -179005,-100,4,2,0,80,0,1 -179826,-100,4,2,0,60,0,1 -179943,-100,4,2,0,80,0,1 -180763,-100,4,2,0,60,0,1 -180880,-100,4,2,0,80,0,1 -180998,-100,4,2,0,80,0,0 -181466,-100,4,2,0,60,0,0 -181584,-100,4,2,0,80,0,0 -181935,-100,4,2,0,80,0,0 -182170,-100,4,2,0,80,0,1 -182638,-100,4,2,0,60,0,1 -182755,-100,4,2,0,80,0,1 -183576,-100,4,2,0,60,0,1 -183693,-100,4,2,0,80,0,1 -184513,-100,4,2,0,60,0,1 -184630,-100,4,2,0,80,0,1 -185451,-100,4,2,0,60,0,1 -185568,-100,4,2,0,80,0,1 -186388,-100,4,2,0,60,0,1 -186505,-100,4,2,0,80,0,1 -187326,-100,4,2,0,60,0,1 -187443,-100,4,2,0,80,0,1 -188263,-100,4,2,0,60,0,1 -188380,-100,4,2,0,80,0,1 -189201,-100,4,2,0,60,0,1 -189318,-100,4,2,0,80,0,1 -190138,-100,4,2,0,60,0,1 -190255,-100,4,2,0,80,0,1 -191076,-100,4,2,0,60,0,1 -191193,-100,4,2,0,80,0,1 -192013,-100,4,2,0,60,0,1 -192130,-100,4,2,0,80,0,1 -192951,-100,4,2,0,60,0,1 -193068,-100,4,2,0,80,0,1 -193888,-100,4,2,0,60,0,1 -194005,-100,4,2,0,80,0,1 -194826,-100,4,2,0,60,0,1 -194943,-100,4,2,0,80,0,1 -195295,-100,4,2,0,50,0,1 -195529,-100,4,2,0,52,0,1 -195646,-100,4,2,0,54,0,1 -195763,-100,4,2,0,56,0,1 -195880,-100,4,2,0,58,0,1 -195998,-100,4,2,0,60,0,1 -196115,-100,4,2,0,62,0,1 -196232,-100,4,2,0,64,0,1 -196349,-100,4,2,0,68,0,1 -196466,-100,4,2,0,70,0,1 -196584,-100,4,2,0,72,0,1 -196701,-100,4,2,0,74,0,1 -196818,-100,4,2,0,76,0,1 -196935,-100,4,2,0,78,0,1 -197052,-100,4,2,0,80,0,1 -197170,-100,4,2,0,80,0,0 -197873,-100,4,2,0,60,0,0 -197990,-100,4,2,0,80,0,0 -198341,-100,4,2,0,60,0,0 -199045,-100,4,2,0,80,0,0 -199279,-100,4,2,0,60,0,0 -199630,-100,4,2,0,80,0,0 -200216,-100,4,2,0,60,0,0 -200334,-100,4,2,0,80,0,0 -201623,-100,4,2,0,60,0,0 -201740,-100,4,2,0,80,0,0 -202326,-100,4,2,0,60,0,0 -202443,-100,4,2,0,80,0,0 -203029,-100,4,2,0,60,0,0 -203498,-100,4,2,0,80,0,0 -203966,-100,4,2,0,60,0,0 -204201,-100,4,2,0,80,0,0 -205373,-100,4,2,0,60,0,0 -205490,-100,4,2,0,80,0,0 -205841,-100,4,2,0,60,0,0 -206076,-100,4,2,0,60,0,0 -206545,-100,4,2,0,80,0,0 -206779,-100,4,2,0,60,0,0 -207130,-100,4,2,0,80,0,0 -207716,-100,4,2,0,60,0,0 -207951,-100,4,2,0,80,0,0 -209123,-100,4,2,0,60,0,0 -209240,-100,4,2,0,80,0,0 -209826,-100,4,2,0,60,0,0 -209943,-100,4,2,0,80,0,0 -210529,-100,4,2,0,60,0,0 -210880,-100,4,2,0,80,0,0 -211232,-100,4,2,0,60,0,0 -211701,-100,4,2,0,70,0,0 -212170,-100,4,2,0,80,0,0 -212873,-100,4,2,0,60,0,0 -212990,-100,4,2,0,80,0,0 -213341,-100,4,2,0,60,0,0 -213576,-100,4,2,0,60,0,0 -214045,-100,4,2,0,80,0,0 -214279,-100,4,2,0,60,0,0 -214630,-100,4,2,0,80,0,0 -215216,-100,4,2,0,60,0,0 -215451,-100,4,2,0,80,0,0 -216623,-100,4,2,0,60,0,0 -216740,-100,4,2,0,80,0,0 -217326,-100,4,2,0,60,0,0 -217443,-100,4,2,0,80,0,0 -218029,-100,4,2,0,60,0,0 -218498,-100,4,2,0,80,0,0 -218732,-100,4,2,0,50,0,0 -219670,-100,4,2,0,70,0,0 -220138,-100,4,2,0,65,0,0 -220373,-100,4,2,0,45,0,0 -220490,-100,4,2,0,65,0,0 -220607,-100,4,2,0,60,0,0 -220841,-100,4,2,0,35,0,0 -221076,-100,4,2,0,35,0,0 -221545,-100,4,2,0,50,0,0 -221779,-100,4,2,0,30,0,0 -222013,-111.111111111111,4,2,0,25,0,0 -222130,-111.111111111111,4,2,0,40,0,0 -222482,-125,4,2,0,40,0,0 -222716,-125,4,2,0,20,0,0 -222951,-100,4,2,0,15,0,0 -223420,-100,4,2,0,30,0,0 -224357,-100,4,2,0,25,0,0 -225295,-100,4,2,0,20,0,0 -226232,-100,4,2,0,15,0,0 -226701,-100,4,2,0,10,0,0 -227170,-100,4,2,0,5,0,0 - - -[Colours] - Combo1 : 17,254,176 -Combo2 : 173,255,95 -Combo3 : 255,88,100 -Combo4 : 255,94,55 - -[HitObjects] -320,256,2170,6,0,P|256:284|192:256,1,144,4|0,0:0|0:0,0:0:0:0: -144,184,2873,1,0,0:0:0:0: -108,260,3107,2,0,P|112:296|100:336,1,72 -28,288,3576,2,0,P|24:252|36:212,1,72,0|0,0:0|0:0,0:0:0:0: -76,140,4045,6,0,L|220:136,1,144,4|0,0:0|0:0,0:0:0:0: -292,88,4748,1,0,0:0:0:0: -292,88,4982,2,0,P|304:120|300:168,1,72 -388,168,5451,2,0,P|396:133|416:103,1,72,0|0,0:0|0:0,0:0:0:0: -472,172,5920,6,0,B|470:200|457:222|457:222|488:256|476:308,1,144,4|0,0:0|0:0,0:0:0:0: -396,280,6623,1,0,0:0:0:0: -324,328,6857,2,0,P|288:332|252:324,1,72 -180,280,7326,2,0,L|108:284,1,72,0|0,0:0|0:0,0:0:0:0: -256,192,7795,12,0,9670,0:0:0:0: -428,212,10138,1,0,0:0:0:0: -292,320,10607,1,0,0:0:0:0: -184,184,11076,2,0,L|112:180,1,72,0|0,0:0|0:0,0:0:0:0: -24,172,11545,5,6,0:0:0:0: -160,280,12013,1,0,0:0:0:0: -268,144,12482,1,0,0:0:0:0: -132,36,12951,2,0,L|204:32,1,72,0|0,0:0|0:0,0:0:0:0: -284,60,13420,6,0,P|340:100|344:180,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0: -268,144,14591,1,0,0:0:0:0: -284,228,14826,2,0,P|316:248|364:252,1,72,0|0,0:0|0:0,0:0:0:0: -436,248,15295,6,0,P|372:272|344:340,1,144,6|2,0:0|0:0,0:0:0:0: -168,338,16232,2,0,P|141:273|76:248,1,144,2|2,0:0|0:0,0:0:0:0: -4,296,16935,1,0,0:0:0:0: -80,336,17170,5,6,0:0:0:0: -44,168,17638,1,0,0:0:0:0: -212,128,18107,1,0,0:0:0:0: -248,296,18576,2,0,P|284:288|320:292,1,72,0|0,0:0|0:0,0:0:0:0: -400,324,19045,5,6,0:0:0:0: -280,200,19513,1,0,0:0:0:0: -368,52,19982,1,0,0:0:0:0: -488,176,20451,2,0,P|452:168|416:172,1,72,0|0,0:0|0:0,0:0:0:0: -336,200,20920,6,0,P|284:216|200:192,1,144,6|0,0:0|0:0,0:0:0:0: -200,192,21857,2,0,L|204:264,1,72,0|0,0:3|0:0,0:0:0:0: -117,244,22326,2,0,L|120:172,1,72,0|0,0:0|0:0,0:0:0:0: -40,152,22795,6,0,L|28:296,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0: -152,24,24201,1,0,0:0:0:0: -220,76,24435,1,0,3:0:0:0: -304,56,24670,6,0,P|288:120|296:196,1,144,4|2,0:3|0:3,0:0:0:0: -344,268,25373,1,0,0:0:0:0: -416,316,25607,2,0,P|452:312|508:316,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -244,344,26545,6,0,P|176:356|108:328,1,144,4|2,0:3|0:3,0:0:0:0: -60,256,27248,1,0,0:0:0:0: -36,172,27482,2,0,L|40:100,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -188,252,28420,6,0,P|192:184|196:100,1,144,4|2,0:3|0:3,0:0:0:0: -140,40,29123,1,0,0:0:0:0: -140,40,29357,2,0,B|172:16|220:24|220:24|288:36,1,144,0|2,0:0|0:3,0:0:0:0: -364,52,30060,1,0,0:0:0:0: -308,116,30295,6,0,B|300:168|300:168|328:256,1,144,4|2,0:3|0:3,0:0:0:0: -340,340,30998,1,0,0:0:0:0: -260,308,31232,2,0,L|188:304,1,72,0|2,0:0|0:3,0:0:0:0: -100,296,31701,1,2,0:3:0:0: -136,374,31935,1,0,0:0:0:0: -152,224,32170,6,0,P|160:152|132:88,1,144,4|2,0:3|0:3,0:0:0:0: -56,48,32873,1,0,0:0:0:0: -60,136,33107,2,0,L|56:208,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -224,76,34045,6,0,P|289:104|360:96,1,144,4|2,0:3|0:3,0:0:0:0: -432,48,34748,1,0,0:0:0:0: -440,132,34982,2,0,B|432:156|432:156|436:204,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -448,304,35920,6,0,B|412:315|380:292|380:292|348:269|312:280,1,144,4|2,0:3|0:3,0:0:0:0: -332,364,36623,1,0,0:0:0:0: -247,339,36857,2,0,P|230:308|225:273,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -312,280,37560,6,0,L|316:172,1,108 -134,35,38966,5,0,0:0:0:0: -72,96,39201,2,0,P|119:119|171:111,1,108,0|0,0:0|0:0,0:0:0:0: -192,100,39670,6,0,L|200:172,1,72,4|2,0:0|0:0,0:0:0:0: -147,240,40138,2,0,P|133:272|132:308,1,72,0|2,1:0|0:0,0:0:0:0: -216,292,40607,2,0,B|260:308|260:308|356:292,1,144,4|0,2:3|1:0,1:0:0:0: -356,292,41310,1,2,0:0:0:0: -436,327,41545,6,0,P|441:292|435:257,1,72,4|2,0:3|0:0,0:0:0:0: -364,204,42013,2,0,P|336:144|352:68,1,144,0|4,1:0|2:3,1:0:0:0: -404,0,42716,1,2,0:0:0:0: -440,80,42951,2,0,B|464:84|464:84|512:80,1,72,0|2,1:0|0:0,0:0:0:0: -351,71,43420,6,0,B|296:68|296:68|268:76|268:76|196:72,1,144,4|0,2:3|1:0,1:0:0:0: -120,68,44123,1,2,0:0:0:0: -160,144,44357,2,0,P|172:180|168:232,1,72,4|2,0:3|0:0,0:0:0:0: -76,264,44826,2,0,P|76:228|88:194,1,72,0|2,1:0|0:0,0:0:0:0: -160,144,45295,5,4,0:3:0:0: -244,164,45529,1,2,0:0:0:0: -268,248,45763,2,0,L|344:252,1,72,0|2,1:0|0:0,0:0:0:0: -408,156,46232,2,0,L|336:159,1,72,4|2,0:3|0:0,0:0:0:0: -212,72,46701,2,0,L|288:76,1,72,0|2,1:0|0:0,0:0:0:0: -400,72,47170,6,0,P|464:96|488:172,1,144,4|0,2:0|1:0,1:0:0:0: -476,248,47873,1,2,0:0:0:0: -436,324,48107,2,0,L|284:320,1,144,4|0,2:3|1:0,1:0:0:0: -204,316,48810,1,2,0:0:0:0: -127,355,49045,6,0,P|120:321|124:285,1,72,4|2,0:3|0:0,0:0:0:0: -192,232,49513,2,0,L|335:228,1,144,0|4,1:0|2:3,1:0:0:0: -412,188,50216,1,2,0:0:0:0: -444,108,50451,2,0,P|452:72|448:36,1,72,0|2,1:0|0:0,0:0:0:0: -368,68,50920,6,0,B|332:79|300:56|300:56|268:33|232:44,1,144,4|0,2:3|1:0,1:0:0:0: -152,76,51623,1,2,0:0:0:0: -76,116,51857,2,0,L|80:268,1,144,4|0,2:3|1:0,1:0:0:0: -80,260,52560,1,2,0:0:0:0: -8,308,52795,6,0,P|34:334|69:346,1,72,4|2,0:3|0:0,0:0:0:0: -148,312,53263,2,0,P|163:278|162:241,1,72,0|2,1:0|0:0,0:0:0:0: -156,156,53732,5,0,3:0:0:0: -156,156,53966,1,2,0:0:0:0: -236,196,54201,2,0,L|312:192,1,72,8|0,0:3|0:0,0:0:0:0: -368,256,54670,6,0,P|392:216|352:116,1,144,4|2,0:0|1:2,0:0:0:0: -288,92,55373,1,0,0:0:0:0: -360,40,55607,2,0,L|432:36,1,72,4|0,0:3|3:0,0:0:0:0: -288,92,56076,2,0,L|216:88,1,72,2|0,1:2|0:0,0:0:0:0: -132,72,56545,6,0,P|172:88|200:184,1,144,4|2,0:3|1:2,0:0:0:0: -143,241,57248,1,0,0:0:0:0: -65,202,57482,2,0,P|87:174|119:157,1,72,4|0,0:3|3:0,0:0:0:0: -132,324,57951,2,0,P|98:312|72:288,1,72,2|0,1:2|0:0,0:0:0:0: -143,241,58420,6,0,L|288:240,1,144,4|2,0:3|1:2,0:0:0:0: -372,240,59123,1,0,0:0:0:0: -330,314,59357,2,0,P|318:350|322:390,1,72,4|0,0:3|3:0,0:0:0:0: -452,264,59826,2,0,P|453:228|442:194,1,72,2|0,1:2|0:0,0:0:0:0: -384,128,60295,6,0,B|336:144|336:144|244:128,1,144,4|2,0:3|1:2,0:0:0:0: -164,160,60998,2,0,P|160:116|168:88,1,72,0|4,0:0|0:3,0:0:0:0: -244,128,61466,2,0,P|248:172|240:200,1,72,0|2,3:0|1:2,0:0:0:0: -168,248,61935,1,0,0:0:0:0: -120,320,62170,6,0,P|196:328|252:272,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: -80,244,63341,1,0,3:0:0:0: -100,160,63576,2,0,L|24:156,1,72,2|0,1:2|0:0,0:0:0:0: -180,128,64045,6,0,P|249:138|304:94,1,144,4|2,0:3|1:2,0:0:0:0: -226,57,64748,1,0,0:0:0:0: -304,94,64982,2,0,L|300:166,1,72,4|0,0:3|3:0,0:0:0:0: -377,203,65451,2,0,L|388:132,1,72,2|0,1:2|0:0,0:0:0:0: -468,180,65920,6,0,L|432:328,1,144,4|2,0:3|1:2,0:0:0:0: -276,252,66857,2,0,P|208:248|140:280,1,144,4|2,0:3|1:2,0:0:0:0: -84,344,67560,1,0,0:0:0:0: -56,260,67795,6,0,L|52:188,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -168,128,68732,2,0,L|172:56,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -244,168,69435,1,0,0:0:0:0: -332,164,69670,1,4,0:3:0:0: -208,328,84670,6,0,P|224:264|216:188,1,144,4|2,0:3|0:3,0:0:0:0: -168,116,85373,1,0,0:0:0:0: -96,68,85607,2,0,P|60:72|4:68,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -268,40,86545,6,0,P|336:28|404:56,1,144,4|2,0:3|0:3,0:0:0:0: -452,128,87248,1,0,0:0:0:0: -476,212,87482,2,0,L|472:284,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -324,132,88420,6,0,P|320:200|316:284,1,144,4|2,0:3|0:3,0:0:0:0: -372,344,89123,1,0,0:0:0:0: -372,344,89357,2,0,B|340:368|292:360|292:360|224:348,1,144,0|2,0:0|0:3,0:0:0:0: -148,332,90060,1,0,0:0:0:0: -204,268,90295,6,0,B|212:216|212:216|184:128,1,144,4|2,0:3|0:3,0:0:0:0: -172,44,90998,1,0,0:0:0:0: -252,76,91232,2,0,L|324:80,1,72,0|2,0:0|0:3,0:0:0:0: -412,88,91701,1,2,0:3:0:0: -377,9,91935,1,0,0:0:0:0: -360,160,92170,6,0,P|352:232|380:296,1,144,4|2,0:3|0:3,0:0:0:0: -456,336,92873,1,0,0:0:0:0: -452,248,93107,2,0,L|456:176,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -288,308,94045,6,0,P|223:280|152:288,1,144,4|2,0:3|0:3,0:0:0:0: -80,336,94748,1,0,0:0:0:0: -72,252,94982,2,0,B|80:228|80:228|76:180,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -64,80,95920,6,0,B|100:69|132:92|132:92|164:115|200:104,1,144,4|2,0:3|0:3,0:0:0:0: -180,20,96623,1,0,0:0:0:0: -265,45,96857,2,0,P|282:76|287:111,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0: -200,104,97560,1,0,0:0:0:0: -200,104,97677,1,0,0:0:0:0: -200,104,97795,6,0,B|196:142|217:166|217:166|176:180|160:220,1,144,4|0,0:3|0:0,0:0:0:0: -240,248,98966,5,0,0:0:0:0: -202,325,99201,2,0,P|254:333|301:309,1,108,0|0,0:0|0:0,0:0:0:0: -315,292,99670,6,0,L|323:220,1,72,4|2,0:0|0:0,0:0:0:0: -365,144,100138,2,0,P|379:112|380:76,1,72,0|2,1:0|0:0,0:0:0:0: -296,92,100607,2,0,B|252:76|252:76|156:92,1,144,4|0,2:3|1:0,1:0:0:0: -156,92,101310,1,2,0:0:0:0: -76,57,101545,6,0,P|71:92|77:127,1,72,4|2,0:3|0:0,0:0:0:0: -148,180,102013,2,0,P|176:240|160:316,1,144,0|4,1:0|2:3,1:0:0:0: -108,384,102716,1,2,0:0:0:0: -72,304,102951,2,0,B|48:300|48:300|0:304,1,72,0|2,1:0|0:0,0:0:0:0: -161,313,103420,6,0,B|216:316|216:316|244:308|244:308|316:312,1,144,4|0,2:3|1:0,1:0:0:0: -392,316,104123,1,2,0:0:0:0: -352,240,104357,2,0,P|340:204|344:152,1,72,4|2,0:3|0:0,0:0:0:0: -436,120,104826,2,0,P|436:156|424:190,1,72,0|2,1:0|0:0,0:0:0:0: -352,240,105295,5,4,0:3:0:0: -268,220,105529,1,2,0:0:0:0: -244,136,105763,2,0,L|168:132,1,72,0|2,1:0|0:0,0:0:0:0: -104,228,106232,2,0,L|176:225,1,72,4|2,0:3|0:0,0:0:0:0: -300,312,106701,2,0,L|224:308,1,72,0|2,1:0|0:0,0:0:0:0: -112,312,107170,6,0,P|48:288|24:212,1,144,4|0,2:0|1:0,1:0:0:0: -36,136,107873,1,2,0:0:0:0: -76,60,108107,2,0,L|228:64,1,144,4|0,2:3|1:0,1:0:0:0: -308,68,108810,1,2,0:0:0:0: -385,29,109045,6,0,P|392:63|388:99,1,72,4|2,0:3|0:0,0:0:0:0: -320,152,109513,2,0,L|177:156,1,144,0|4,1:0|2:3,1:0:0:0: -100,196,110216,1,2,0:0:0:0: -68,276,110451,2,0,P|60:312|64:348,1,72,0|2,1:0|0:0,0:0:0:0: -144,316,110920,6,0,B|180:305|212:328|212:328|244:351|280:340,1,144,4|0,2:3|1:0,1:0:0:0: -360,308,111623,1,2,0:0:0:0: -436,268,111857,2,0,L|432:116,1,144,4|0,2:3|1:0,1:0:0:0: -432,124,112560,1,2,0:0:0:0: -504,76,112795,6,0,P|478:50|443:38,1,72,4|2,0:3|0:0,0:0:0:0: -364,72,113263,2,0,P|349:106|350:143,1,72,0|2,1:0|0:0,0:0:0:0: -356,228,113732,5,0,3:0:0:0: -356,228,113966,1,2,0:0:0:0: -276,188,114201,2,0,L|200:192,1,72,8|0,0:3|0:0,0:0:0:0: -144,128,114670,6,0,P|120:168|160:268,1,144,4|2,0:0|1:2,0:0:0:0: -224,292,115373,1,0,0:0:0:0: -152,344,115607,2,0,L|80:348,1,72,4|0,0:3|3:0,0:0:0:0: -224,292,116076,2,0,L|296:296,1,72,2|0,1:2|0:0,0:0:0:0: -380,312,116545,6,0,P|340:296|312:200,1,144,4|2,0:3|1:2,0:0:0:0: -369,143,117248,1,0,0:0:0:0: -447,182,117482,2,0,P|425:210|393:227,1,72,4|0,0:3|3:0,0:0:0:0: -380,60,117951,2,0,P|414:72|440:96,1,72,2|0,1:2|0:0,0:0:0:0: -369,143,118420,6,0,L|224:144,1,144,4|2,0:3|1:2,0:0:0:0: -140,144,119123,1,0,0:0:0:0: -182,70,119357,2,0,P|194:34|190:-6,1,72,4|0,0:3|3:0,0:0:0:0: -60,120,119826,2,0,P|59:156|70:190,1,72,2|0,1:2|0:0,0:0:0:0: -128,256,120295,6,0,B|176:240|176:240|268:256,1,144,4|2,0:3|1:2,0:0:0:0: -348,224,120998,2,0,P|352:268|344:296,1,72,0|4,0:0|0:3,0:0:0:0: -268,256,121466,2,0,P|264:212|272:184,1,72,0|2,3:0|1:2,0:0:0:0: -344,136,121935,1,0,0:0:0:0: -392,64,122170,6,0,P|316:56|260:112,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: -432,140,123341,1,0,3:0:0:0: -412,224,123576,2,0,L|488:228,1,72,2|0,1:2|0:0,0:0:0:0: -332,256,124045,6,0,P|263:246|208:290,1,144,4|2,0:3|1:2,0:0:0:0: -286,327,124748,1,0,0:0:0:0: -208,290,124982,2,0,L|212:218,1,72,4|0,0:3|3:0,0:0:0:0: -135,181,125451,2,0,L|124:252,1,72,2|0,1:2|0:0,0:0:0:0: -44,204,125920,6,0,L|80:56,1,144,4|2,0:3|1:2,0:0:0:0: -236,132,126857,2,0,P|304:136|372:104,1,144,4|2,0:3|1:2,0:0:0:0: -428,40,127560,1,0,0:0:0:0: -456,124,127795,6,0,L|460:196,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -344,256,128732,2,0,L|340:328,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -268,216,129435,1,0,0:0:0:0: -180,220,129670,5,4,2:0:0:0: -256,40,130373,1,2,0:0:0:0: -64,68,131076,1,2,0:0:0:0: -92,136,131310,1,0,0:0:0:0: -64,204,131545,6,0,L|60:288,1,72 -31,343,132248,2,0,P|86:345|127:309,1,108 -332,220,133420,5,2,0:0:0:0: -256,40,134123,1,2,0:0:0:0: -448,68,134826,1,2,0:0:0:0: -420,136,135060,1,0,0:0:0:0: -448,204,135295,6,0,L|452:288,1,72,2|0,0:0|0:0,0:0:0:0: -480,343,135998,2,0,P|426:345|385:309,1,108 -256,192,137170,5,2,0:0:0:0: -156,360,137873,1,2,0:0:0:0: -356,360,138576,2,0,L|352:308,1,36,2|0,0:0|0:0,0:0:0:0: -304,268,139045,6,0,P|336:253|372:252,1,72 -448,260,139748,2,0,L|444:152,1,108 -256,192,140920,5,2,0:0:0:0: -356,24,141623,1,2,0:0:0:0: -156,24,142326,2,0,L|160:72,1,36,2|0,0:0|0:0,0:0:0:0: -208,116,142795,6,0,P|176:131|140:132,1,72,2|0,0:0|0:0,0:0:0:0: -64,124,143498,2,0,L|68:232,1,108 -68,232,144670,5,4,0:3:0:0: -216,320,145138,1,4,0:3:0:0: -304,172,145607,1,4,0:3:0:0: -156,84,146075,1,4,0:3:0:0: -296,320,146545,5,4,0:3:0:0: -208,172,147013,1,4,0:3:0:0: -356,84,147482,1,4,0:3:0:0: -444,232,147950,1,4,0:3:0:0: -296,320,148420,6,0,P|252:328|192:296,2,108.000004119873,4|4|4,0:3|0:3|0:3,0:0:0:0: -260,248,149591,1,0,0:0:0:0: -320,196,149826,2,0,L|316:140,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0: -120,236,159670,6,0,L|176:232,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0: -160,152,160138,2,0,L|104:156,1,54.0000020599366,2|0,0:0|0:0,0:0:0:0: -240,180,160607,2,0,P|292:188|344:172,1,108.000004119873,4|2,0:3|0:0,3:0:0:0: -408,120,161310,1,0,3:0:0:0: -424,200,161545,6,0,L|420:256,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0: -376,320,162013,2,0,P|396:328|480:304,2,108.000004119873,2|6|2,2:0|0:3|2:0,3:0:0:0: -312,268,163185,1,0,0:0:0:0: -296,348,163420,6,0,L|240:344,1,54.0000020599366,4|0,3:0|3:0,0:0:0:0: -160,320,163888,2,0,L|100:316,1,57.6,4|0,3:0|3:0,0:0:0:0: -64,232,164357,6,0,L|128:228,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0: -204,200,164825,2,0,L|268:196,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0: -232,108,165295,6,0,L|164:104,1,68.399998173523,4|0,3:0|3:0,0:0:0:0: -80,84,165763,2,0,L|4:80,1,72,4|0,3:0|3:0,0:0:0:0: -324,120,167170,6,0,P|388:128|456:92,1,144,4|2,0:0|1:2,0:0:0:0: -496,168,167873,1,0,0:0:0:0: -496,168,168107,2,0,P|484:204|488:256,1,72,4|0,0:3|3:0,0:0:0:0: -408,296,168576,2,0,P|398:261|378:231,1,72,2|0,1:2|0:0,0:0:0:0: -296,200,169045,6,0,B|228:228|156:204,1,144,4|2,0:3|1:2,0:0:0:0: -84,156,169748,1,0,0:0:0:0: -80,244,169982,2,0,L|76:316,1,72,4|0,0:3|3:0,0:0:0:0: -170,274,170451,2,0,L|156:204,1,72,2|0,1:2|0:0,0:0:0:0: -216,140,170920,6,0,L|284:276,1,144,4|2,0:3|1:2,0:0:0:0: -320,344,171623,1,0,0:0:0:0: -372,276,171857,2,0,P|366:240|349:207,1,72,4|0,0:3|3:0,0:0:0:0: -312,132,172326,2,0,L|276:60,1,72,2|0,1:2|0:0,0:0:0:0: -208,20,172795,6,0,P|272:36|348:12,1,144,4|2,0:3|1:2,0:0:0:0: -424,48,173498,2,0,L|412:132,1,72,0|4,0:0|0:3,0:0:0:0: -484,168,173966,2,0,L|472:252,1,72,0|2,3:0|1:2,0:0:0:0: -400,280,174435,1,0,0:0:0:0: -346,348,174670,6,0,P|414:363|472:324,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: -312,268,175841,1,0,3:0:0:0: -256,336,176076,2,0,L|184:332,1,72,2|0,1:2|0:0,0:0:0:0: -80,244,176545,6,0,B|140:248|140:248|164:244|164:244|223:247,1,144,4|2,0:3|1:2,0:0:0:0: -312,268,177248,1,0,0:0:0:0: -224,247,177482,2,0,P|240:215|272:187,1,72,4|0,0:3|3:0,0:0:0:0: -204,131,177951,2,0,P|233:111|275:103,1,72,2|0,1:2|0:0,0:0:0:0: -240,23,178420,6,0,B|280:15|316:35|316:35|376:71,1,144,4|2,0:3|1:2,0:0:0:0: -399,236,179357,2,0,B|359:244|323:224|323:224|263:188,1,144,4|2,0:3|1:2,0:0:0:0: -204,132,180060,1,0,0:0:0:0: -184,216,180295,6,0,L|188:288,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -120,156,180998,1,0,0:0:0:0: -56,96,181232,2,0,L|60:24,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: -36,180,181935,1,0,0:0:0:0: -100,240,182170,6,0,P|144:300|116:380,2,144,4|2|4,0:0|1:2|0:3,0:0:0:0: -60,316,183341,1,0,0:0:0:0: -220,352,183576,2,0,L|308:348,1,72,2|0,1:2|0:0,0:0:0:0: -396,264,184045,6,0,B|336:268|336:268|312:264|312:264|253:267,1,144,4|2,0:3|1:2,0:0:0:0: -253,267,184748,1,0,0:0:0:0: -268,180,184982,2,0,L|339:177,1,72,4|0,0:3|0:0,0:0:0:0: -164,280,185451,2,0,L|92:282,1,72,2|0,1:2|0:0,0:0:0:0: -52,208,185920,6,0,P|8:268|32:344,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: -140,212,187091,1,0,0:0:0:0: -92,284,187326,2,0,P|104:316|100:368,1,72,2|0,1:2|0:0,0:0:0:0: -52,208,187795,6,0,P|48:136|76:72,1,144,4|2,0:3|1:2,0:0:0:0: -160,52,188498,2,0,P|188:28|220:16,1,72,0|4,0:0|0:3,0:0:0:0: -232,100,188966,2,0,P|268:93|301:98,1,72,0|2,0:0|1:2,0:0:0:0: -372,152,189435,1,0,0:0:0:0: -420,224,189670,6,0,P|428:296|400:360,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0: -372,152,190841,1,0,0:0:0:0: -392,68,191076,2,0,L|465:64,1,72,2|0,1:2|0:0,0:0:0:0: -304,92,191545,6,0,P|236:104|168:76,1,144,4|2,0:3|1:2,0:0:0:0: -108,12,192248,1,0,0:0:0:0: -168,76,192482,2,0,L|172:152,1,72,4|0,0:3|0:0,0:0:0:0: -80,136,192951,2,0,L|101:204,1,72,2|0,1:2|0:0,0:0:0:0: -12,220,193420,6,0,B|50:279|50:279|80:300|120:292,1,144,4|2,0:3|1:2,0:0:0:0: -284,232,194357,2,0,B|320:221|352:244|352:244|384:267|420:256,1,144,4|2,0:3|1:2,0:0:0:0: -488,200,195060,1,0,0:0:0:0: -507,284,195295,6,0,P|492:315|464:338,1,72,4|0,0:0|0:0,0:0:0:0: -380,356,195763,2,0,L|236:352,1,144,0|4,1:0|0:3,0:0:0:0: -152,328,196466,1,0,3:0:0:0: -64,336,196701,2,0,P|29:325|4:300,1,72,0|0,1:0|0:0,0:0:0:0: -76,252,197170,6,0,P|108:188|96:116,1,144,4|0,0:0|1:0,0:0:0:0: -36,56,197873,1,2,0:0:0:0: -120,32,198107,2,0,L|192:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -248,152,199045,6,0,P|280:168|304:196,1,72,4|2,0:3|0:0,0:0:0:0: -336,277,199513,2,0,P|306:296|269:303,1,72,2|0,1:2|0:0,0:0:0:0: -183,290,199982,2,0,P|180:254|193:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: -436,252,200920,6,0,P|404:188|416:116,1,144,4|0,0:3|1:0,0:0:0:0: -476,56,201623,1,2,0:0:0:0: -392,32,201857,2,0,L|320:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0: -264,152,202795,6,0,P|232:168|208:196,1,72,4|2,0:3|0:0,0:0:0:0: -176,277,203263,2,0,P|205:296|242:303,1,72,2|0,1:2|0:0,0:0:0:0: -329,290,203732,2,0,P|331:254|318:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: -72,324,204670,6,0,B|60:272|60:272|76:180,1,144,4|0,0:0|1:0,0:0:0:0: -92,96,205373,1,2,0:0:0:0: -8,124,205607,2,0,P|5:88|14:53,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -168,192,206545,6,0,P|200:174|237:173,1,72,4|2,0:3|0:0,0:0:0:0: -320,160,207013,2,0,P|318:196|301:229,1,72,2|0,1:2|0:0,0:0:0:0: -272,307,207482,2,0,P|240:287|221:256,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: -440,324,208420,6,0,B|452:272|452:272|436:180,1,144,4|0,0:3|1:0,0:0:0:0: -420,96,209123,1,2,0:0:0:0: -504,124,209357,2,0,P|507:88|498:53,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0: -344,192,210295,6,0,P|311:174|274:173,1,72,4|2,0:3|0:0,0:0:0:0: -190,156,210763,2,0,P|191:192|208:225,1,72,2|0,1:2|0:0,0:0:0:0: -288,256,211232,1,4,0:3:0:0: -132,332,211701,1,0,1:0:0:0: -28,192,212170,6,0,P|16:120|44:56,1,144,4|0,0:0|1:0,0:0:0:0: -120,16,212873,1,2,0:0:0:0: -204,32,213107,2,0,L|304:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -192,204,214045,6,0,P|196:240|216:272,1,72,4|2,0:3|0:0,0:0:0:0: -298,241,214513,2,0,P|327:219|345:186,1,72,6|0,1:2|0:0,0:0:0:0: -280,132,214982,2,0,P|246:117|209:118,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0: -484,192,215920,6,0,P|496:120|468:56,1,144,4|0,0:3|1:0,0:0:0:0: -392,16,216623,1,2,0:0:0:0: -308,32,216857,2,0,L|208:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0: -320,204,217795,6,0,P|316:240|296:272,1,72,4|2,0:3|0:0,0:0:0:0: -213,241,218263,2,0,P|184:219|166:186,1,72,2|0,1:2|0:0,0:0:0:0: -232,132,218732,2,0,B|260:112|300:116|300:116|384:128,1,144,4|0,0:3|1:0,0:0:0:0: -348,336,219670,6,0,B|320:356|280:352|280:352|196:340,1,144,4|0,0:0|1:0,0:0:0:0: -124,328,220373,1,2,0:0:0:0: -54,276,220607,2,0,P|41:308|39:345,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0: -156,80,221545,6,0,L|251:94,1,72,4|2,0:3|0:0,0:0:0:0: -212,169,222013,2,0,L|148:160,1,64.799998022461,2|0,1:2|0:0,0:0:0:0: -140,240,222482,2,0,L|216:252,2,57.6,4|2|0,0:3|0:0|1:0,0:0:0:0: -256,192,223420,12,0,227170,0:0:0:0: -"; } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1d3baa6c0d..3d89c859c4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -896,6 +896,7 @@ + From b91524dbba0bdbfc1436dd1d1a140e296f518137 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 18:32:58 +0900 Subject: [PATCH 108/537] Give TestWorkingBeatmap's track a valid length --- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 68bb3f6dd9..02a5c8a5fc 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -1,10 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Tests.Beatmaps { @@ -24,6 +26,21 @@ namespace osu.Game.Tests.Beatmaps private readonly Beatmap beatmap; protected override Beatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; - protected override Track GetTrack() => new TrackVirtual(); + + protected override Track GetTrack() + { + var lastObject = beatmap.HitObjects.LastOrDefault(); + if (lastObject != null) + return new TestTrack(((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime) + 1000); + return new TrackVirtual(); + } + + private class TestTrack : TrackVirtual + { + public TestTrack(double length) + { + Length = length; + } + } } } From 13c20438ef93cd225005dcc47eef687b5556915e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 19:41:51 +0900 Subject: [PATCH 109/537] Fix build issues --- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index a29ee03975..3b43459428 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Beatmaps using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - return testBeatmapCache = Decoder.GetDecoder(reader).DecodeBeatmap(reader); + return testBeatmapCache = Decoder.GetDecoder(reader).Decode(reader); } private const string test_beatmap_data = From d2859d779d1de3b51cbb35edec81b98e2dea492e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Mar 2018 19:38:23 +0900 Subject: [PATCH 110/537] Create a base TestCaseEditor for rulests to derive --- osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs | 16 ++++++ .../osu.Game.Rulesets.Osu.csproj | 1 + osu.Game.Tests/Visual/TestCaseEditor.cs | 49 ------------------- osu.Game.Tests/osu.Game.Tests.csproj | 1 - osu.Game/Tests/Visual/TestCaseEditor.cs | 33 +++++++++++++ osu.Game/osu.Game.csproj | 1 + 6 files changed, 51 insertions(+), 50 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs delete mode 100644 osu.Game.Tests/Visual/TestCaseEditor.cs create mode 100644 osu.Game/Tests/Visual/TestCaseEditor.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs new file mode 100644 index 0000000000..501992def3 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseEditor : Game.Tests.Visual.TestCaseEditor + { + public TestCaseEditor() + : base(new OsuRuleset()) + { + } + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 92cac71ad3..b4c5654de4 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -130,6 +130,7 @@ + diff --git a/osu.Game.Tests/Visual/TestCaseEditor.cs b/osu.Game.Tests/Visual/TestCaseEditor.cs deleted file mode 100644 index c626ca8e7f..0000000000 --- a/osu.Game.Tests/Visual/TestCaseEditor.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Screens.Edit; -using osu.Game.Screens.Edit.Screens; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseEditor : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) }; - - private readonly Random rng; - - private BeatmapManager beatmaps; - private OsuGameBase osuGame; - - public TestCaseEditor() - { - rng = new Random(1337); - - Add(new Editor()); - AddStep("Next beatmap", nextBeatmap); - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame, BeatmapManager beatmaps) - { - this.osuGame = osuGame; - this.beatmaps = beatmaps; - } - - private void nextBeatmap() - { - var sets = beatmaps.GetAllUsableBeatmapSets(); - if (sets.Count == 0) - return; - - BeatmapInfo info = sets[rng.Next(0, sets.Count)].Beatmaps[0]; - osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(info); - } - } -} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 1cfa7bc111..ed9580211b 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -126,7 +126,6 @@ - diff --git a/osu.Game/Tests/Visual/TestCaseEditor.cs b/osu.Game/Tests/Visual/TestCaseEditor.cs new file mode 100644 index 0000000000..76eae7acea --- /dev/null +++ b/osu.Game/Tests/Visual/TestCaseEditor.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Game.Rulesets; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Screens; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Visual +{ + public abstract class TestCaseEditor : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) }; + + private readonly Ruleset ruleset; + + protected TestCaseEditor(Ruleset ruleset) + { + this.ruleset = ruleset; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + osuGame.Beatmap.Value = new TestWorkingBeatmap(ruleset.RulesetInfo); + + Child = new Editor(); + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3d89c859c4..c99ccd6945 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -902,6 +902,7 @@ + From 3354849cc9c05eb47d58abf570935f1fbf36865a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Mar 2018 22:01:18 +0900 Subject: [PATCH 111/537] Fix code formatting regression --- osu.Game/Skinning/LegacySkin.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 2caeed8480..5525cc483e 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -58,9 +58,14 @@ namespace osu.Game.Skinning private readonly SkinInfo skin; private readonly IResourceStore underlyingStore; - private string getPathForFile(string filename) => - skin.Files.FirstOrDefault(f => string.Equals(Path.GetFileNameWithoutExtension(f.Filename), filename.Split('/').Last(), StringComparison.InvariantCultureIgnoreCase))?.FileInfo - .StoragePath; + private string getPathForFile(string filename) + { + string lastPiece = filename.Split('/').Last(); + + var file = skin.Files.FirstOrDefault(f => + string.Equals(Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase)); + return file?.FileInfo.StoragePath; + } public LegacySkinResourceStore(SkinInfo skin, IResourceStore underlyingStore) { From ddc1f03a96c40b17095cbdd97151d746fe74d70b Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Mon, 12 Mar 2018 22:35:45 +0100 Subject: [PATCH 112/537] deselect autoplay button after ctrl-enter play before, the mod was removed, but the button was still active --- osu.Game/Screens/Select/PlaySongSelect.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index c347bfe70f..495292a154 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -100,6 +100,7 @@ namespace osu.Game.Screens.Select { var autoType = Ruleset.Value.CreateInstance().GetAutoplayMod().GetType(); SelectedMods.Value = SelectedMods.Value.Where(m => m.GetType() != autoType).ToArray(); + modSelect.DeselectTypes(new[] { autoType }, true); removeAutoModOnResume = false; } From 95c84ea7feb596e45e8cdf721c45c9cfe5515d4d Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Mon, 12 Mar 2018 23:00:06 +0100 Subject: [PATCH 113/537] remove (now) unnecessary line --- osu.Game/Screens/Select/PlaySongSelect.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 495292a154..09524d2eac 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -99,7 +99,6 @@ namespace osu.Game.Screens.Select if (removeAutoModOnResume) { var autoType = Ruleset.Value.CreateInstance().GetAutoplayMod().GetType(); - SelectedMods.Value = SelectedMods.Value.Where(m => m.GetType() != autoType).ToArray(); modSelect.DeselectTypes(new[] { autoType }, true); removeAutoModOnResume = false; } From 706c26c32b1f4a1912b119b7804a6f6a57b32c64 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 09:27:28 +0900 Subject: [PATCH 114/537] Remove extra whitespace --- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 3b43459428..6d80ebffd4 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -341,9 +341,8 @@ SliderTickRate:2 226701,-100,4,2,0,10,0,0 227170,-100,4,2,0,5,0,0 - [Colours] - Combo1 : 17,254,176 +Combo1 : 17,254,176 Combo2 : 173,255,95 Combo3 : 255,88,100 Combo4 : 255,94,55 From 8acba47a2b77ec6a92f51b7e187401a0b96f4f3b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 11:23:44 +0900 Subject: [PATCH 115/537] Implement mouse wheel movement in the editor --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 30 +++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index e6a51cc39b..3f01c2bd33 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -5,8 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -27,6 +29,9 @@ namespace osu.Game.Rulesets.Edit private RulesetContainer rulesetContainer; private readonly List layerContainers = new List(); + private readonly Bindable beatmap = new Bindable(); + private IAdjustableClock adjustableClock; + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; @@ -36,12 +41,15 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { + beatmap.BindTo(osuGame.Beatmap); + try { - rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value); + rulesetContainer = CreateRulesetContainer(ruleset, beatmap.Value); // TODO: should probably be done at a RulesetContainer level to share logic with Player. - rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); + adjustableClock = (IAdjustableClock)beatmap.Value.Track ?? new StopwatchClock(); + rulesetContainer.Clock = new InterpolatingFramedClock(adjustableClock); } catch (Exception e) { @@ -132,6 +140,24 @@ namespace osu.Game.Rulesets.Edit }); } + protected override bool OnWheel(InputState state) + { + var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(adjustableClock.CurrentTime); + + const int beat_snap_divisor = 4; // Todo: This should not be a constant + double beatLength = timingPoint.BeatLength / beat_snap_divisor; + int direction = state.Mouse.WheelDelta > 0 ? 1 : -1; + + double unsnappedTime = adjustableClock.CurrentTime + beatLength * direction; + + // Unsnapped time may be between two beats, so we need to snap it to the closest beat + int closestBeat = (int)Math.Round(unsnappedTime / beatLength); + double snappedTime = closestBeat * beatLength; + + adjustableClock.Seek(snappedTime); + return true; + } + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); From 6ecd2afd2420cea1b11e7b704eb13a4ba68ed9d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Mar 2018 12:59:41 +0900 Subject: [PATCH 116/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 59004b46f2..d29c8365ba 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 59004b46f2c96ac02fec712e66f9f96fe252f2fa +Subproject commit d29c8365ba3cf7924b57cf22341f4af55658764c From 6c148930b5d2ad601dba56bb87c402e988cdbfad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 14:02:37 +0900 Subject: [PATCH 117/537] Don't skip beats when scrolling in the direction of the closest beat --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 3f01c2bd33..ba31e61397 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -148,10 +148,16 @@ namespace osu.Game.Rulesets.Edit double beatLength = timingPoint.BeatLength / beat_snap_divisor; int direction = state.Mouse.WheelDelta > 0 ? 1 : -1; - double unsnappedTime = adjustableClock.CurrentTime + beatLength * direction; + // The direction is added to prevent rounding issues by enforcing that abs(unsnappedTime - currentTime) > beatLength + double unsnappedTime = adjustableClock.CurrentTime + beatLength * direction + direction; // Unsnapped time may be between two beats, so we need to snap it to the closest beat - int closestBeat = (int)Math.Round(unsnappedTime / beatLength); + int closestBeat; + if (direction > 0) + closestBeat = (int)Math.Floor(unsnappedTime / beatLength); + else + closestBeat = (int)Math.Ceiling(unsnappedTime / beatLength); + double snappedTime = closestBeat * beatLength; adjustableClock.Seek(snappedTime); From e5808196212c72ed81add684f5c4b866ba90da2e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 14:08:43 +0900 Subject: [PATCH 118/537] Rename to seekAmount --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index ba31e61397..e691335353 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -145,20 +145,20 @@ namespace osu.Game.Rulesets.Edit var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(adjustableClock.CurrentTime); const int beat_snap_divisor = 4; // Todo: This should not be a constant - double beatLength = timingPoint.BeatLength / beat_snap_divisor; + double seekAmount = timingPoint.BeatLength / beat_snap_divisor; int direction = state.Mouse.WheelDelta > 0 ? 1 : -1; // The direction is added to prevent rounding issues by enforcing that abs(unsnappedTime - currentTime) > beatLength - double unsnappedTime = adjustableClock.CurrentTime + beatLength * direction + direction; + double unsnappedTime = adjustableClock.CurrentTime + seekAmount * direction + direction; // Unsnapped time may be between two beats, so we need to snap it to the closest beat int closestBeat; if (direction > 0) - closestBeat = (int)Math.Floor(unsnappedTime / beatLength); + closestBeat = (int)Math.Floor(unsnappedTime / seekAmount); else - closestBeat = (int)Math.Ceiling(unsnappedTime / beatLength); + closestBeat = (int)Math.Ceiling(unsnappedTime / seekAmount); - double snappedTime = closestBeat * beatLength; + double snappedTime = closestBeat * seekAmount; adjustableClock.Seek(snappedTime); return true; From df352c98d6a22cfa1b527ed1e68925be0d2d8741 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 14:30:50 +0900 Subject: [PATCH 119/537] Change wheel direction to match stable --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index e691335353..bcf0b5f1e7 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Edit const int beat_snap_divisor = 4; // Todo: This should not be a constant double seekAmount = timingPoint.BeatLength / beat_snap_divisor; - int direction = state.Mouse.WheelDelta > 0 ? 1 : -1; + int direction = state.Mouse.WheelDelta > 0 ? -1 : 1; // The direction is added to prevent rounding issues by enforcing that abs(unsnappedTime - currentTime) > beatLength double unsnappedTime = adjustableClock.CurrentTime + seekAmount * direction + direction; From e32eec9259ae7d463070e1e8da06402b50afa173 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 15:22:46 +0900 Subject: [PATCH 120/537] No more caching --- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 6d80ebffd4..7dc6079959 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -17,15 +17,11 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo.Ruleset = ruleset; } - private static Beatmap testBeatmapCache; private static Beatmap createTestBeatmap() { - if (testBeatmapCache != null) - return testBeatmapCache; - using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - return testBeatmapCache = Decoder.GetDecoder(reader).Decode(reader); + return Decoder.GetDecoder(reader).Decode(reader); } private const string test_beatmap_data = From 0c705b0397d192b4ee7ca2e6242dc26c9db4fae5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 15:57:05 +0900 Subject: [PATCH 121/537] Rename to EditorTestCase --- osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs | 3 ++- .../Tests/Visual/{TestCaseEditor.cs => EditorTestCase.cs} | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) rename osu.Game/Tests/Visual/{TestCaseEditor.cs => EditorTestCase.cs} (85%) diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs index 501992def3..a11f32935e 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs @@ -2,11 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using NUnit.Framework; +using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - public class TestCaseEditor : Game.Tests.Visual.TestCaseEditor + public class TestCaseEditor : EditorTestCase { public TestCaseEditor() : base(new OsuRuleset()) diff --git a/osu.Game/Tests/Visual/TestCaseEditor.cs b/osu.Game/Tests/Visual/EditorTestCase.cs similarity index 85% rename from osu.Game/Tests/Visual/TestCaseEditor.cs rename to osu.Game/Tests/Visual/EditorTestCase.cs index 76eae7acea..ed2b47ae39 100644 --- a/osu.Game/Tests/Visual/TestCaseEditor.cs +++ b/osu.Game/Tests/Visual/EditorTestCase.cs @@ -11,13 +11,13 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { - public abstract class TestCaseEditor : OsuTestCase + public abstract class EditorTestCase : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) }; private readonly Ruleset ruleset; - protected TestCaseEditor(Ruleset ruleset) + protected EditorTestCase(Ruleset ruleset) { this.ruleset = ruleset; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c99ccd6945..7214a31639 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -902,7 +902,7 @@ - + From e97349fd63a96c26a36b0d7fe239ecf6ad51baa1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 16:00:56 +0900 Subject: [PATCH 122/537] Derive ScreenTestCase + use LoadComponentAsync --- osu.Game/Tests/Visual/EditorTestCase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/EditorTestCase.cs b/osu.Game/Tests/Visual/EditorTestCase.cs index ed2b47ae39..982a3c5d73 100644 --- a/osu.Game/Tests/Visual/EditorTestCase.cs +++ b/osu.Game/Tests/Visual/EditorTestCase.cs @@ -11,7 +11,7 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { - public abstract class EditorTestCase : OsuTestCase + public abstract class EditorTestCase : ScreenTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) }; @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual { osuGame.Beatmap.Value = new TestWorkingBeatmap(ruleset.RulesetInfo); - Child = new Editor(); + LoadComponentAsync(new Editor(), LoadScreen); } } } From d6fa7d69d7969d2f21516ad69d736acb38da1fdd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Mar 2018 16:13:07 +0900 Subject: [PATCH 123/537] Add missing fonts section --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 6a3fb82586..cfa0c28de0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -65,6 +65,7 @@ namespace osu.Game.Beatmaps.Formats Colours, HitObjects, Variables, + Fonts } internal enum LegacySampleBank From 299de4b8666eb1cbe3615981025b12e89885d26c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Mar 2018 16:54:34 +0900 Subject: [PATCH 124/537] Rewrite seeking to better handle timing point boundaries --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 78 ++++++++++++++++----- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index bcf0b5f1e7..be4fb2f1a1 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -142,28 +142,70 @@ namespace osu.Game.Rulesets.Edit protected override bool OnWheel(InputState state) { - var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(adjustableClock.CurrentTime); - - const int beat_snap_divisor = 4; // Todo: This should not be a constant - double seekAmount = timingPoint.BeatLength / beat_snap_divisor; - int direction = state.Mouse.WheelDelta > 0 ? -1 : 1; - - // The direction is added to prevent rounding issues by enforcing that abs(unsnappedTime - currentTime) > beatLength - double unsnappedTime = adjustableClock.CurrentTime + seekAmount * direction + direction; - - // Unsnapped time may be between two beats, so we need to snap it to the closest beat - int closestBeat; - if (direction > 0) - closestBeat = (int)Math.Floor(unsnappedTime / seekAmount); + if (state.Mouse.WheelDelta > 0) + SeekBackward(true); else - closestBeat = (int)Math.Ceiling(unsnappedTime / seekAmount); - - double snappedTime = closestBeat * seekAmount; - - adjustableClock.Seek(snappedTime); + SeekForward(true); return true; } + /// + /// Seeks the current time one beat-snapped beat-length backwards. + /// + /// Whether to snap to the closest beat. + public void SeekBackward(bool snapped) => seek(-1, snapped); + + /// + /// Seeks the current time one beat-snapped beat-length forwards. + /// + /// Whether to snap to the closest beat. + public void SeekForward(bool snapped) => seek(1, snapped); + + private void seek(int direction, bool snapped) + { + // Todo: This should not be a constant, but feels good for now + const int beat_snap_divisor = 4; + + var currentTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(adjustableClock.CurrentTime); + double seekAmount = currentTimingPoint.BeatLength / beat_snap_divisor; + + double seekTime = adjustableClock.CurrentTime + seekAmount * direction; + + if (!snapped) + { + adjustableClock.Seek(seekTime); + return; + } + + var nextTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > currentTimingPoint.Time); + var firstTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First(); + + if (currentTimingPoint != firstTimingPoint && seekTime < currentTimingPoint.Time) + seekTime = currentTimingPoint.Time - 1; // -1 to be in the prior timing point's boundary + else if (nextTimingPoint != null && seekTime >= nextTimingPoint.Time) + seekTime = nextTimingPoint.Time + 1; // +1 to be in the next timing point's boundary + else + { + // We will be snapping to beats within the current timing point + seekTime -= currentTimingPoint.Time; + + // When rounding below, we need to ensure that abs(seekTime - currentTime) > seekAmount + // This is done by adding direction - a small offset, to seekTime + seekTime += direction; + + // Determine the index from the current timing point of the closest beat to seekTime, accounting for scrolling direction + int closestBeat; + if (direction > 0) + closestBeat = (int)Math.Floor(seekTime / seekAmount); + else + closestBeat = (int)Math.Ceiling(seekTime / seekAmount); + + seekTime = currentTimingPoint.Time + closestBeat * seekAmount; + } + + adjustableClock.Seek(seekTime); + } + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); From ba44813c0007ac4d0099fc2466a40d1b3c8d1534 Mon Sep 17 00:00:00 2001 From: vperus Date: Tue, 13 Mar 2018 12:05:52 +0200 Subject: [PATCH 125/537] Fixed exit from game save username instead of email. Resolve #2156 --- osu.Game/Online/API/APIAccess.cs | 1 - osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 1325179e0d..bab53cb462 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -125,7 +125,6 @@ namespace osu.Game.Online.API userReq.Success += u => { LocalUser.Value = u; - Username = LocalUser.Value.Username; failureCount = 0; //we're connected! diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index c2dfea9a08..16586adc0c 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Toolbar avatar.User = new User(); break; case APIState.Online: - Text = api.Username; + Text = api.LocalUser.Value.Username; avatar.User = api.LocalUser; break; } From e43de68ad3b17fb1d2de61a87cb7876839bab884 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Mar 2018 19:13:50 +0900 Subject: [PATCH 126/537] Move colour parsing to LegacyDecoder --- .../Beatmaps/CatchBeatmapProcessor.cs | 6 +-- .../Beatmaps/OsuBeatmapProcessor.cs | 6 +-- .../Formats/LegacyBeatmapDecoderTest.cs | 2 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 4 +- osu.Game/Beatmaps/Beatmap.cs | 7 +-- osu.Game/Beatmaps/Formats/IHasComboColours.cs | 10 ++++ .../Beatmaps/Formats/IHasCustomColours.cs | 10 ++++ .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 53 ++++--------------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 49 ++++++++++++++++- .../Formats/LegacyStoryboardDecoder.cs | 6 ++- osu.Game/osu.Game.csproj | 2 + 11 files changed, 96 insertions(+), 59 deletions(-) create mode 100644 osu.Game/Beatmaps/Formats/IHasComboColours.cs create mode 100644 osu.Game/Beatmaps/Formats/IHasCustomColours.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index d3012b1981..0cdc1694f4 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { public override void PostProcess(Beatmap beatmap) { - if (beatmap.ComboColors.Count == 0) + if (beatmap.ComboColours.Count == 0) return; int index = 0; @@ -31,11 +31,11 @@ namespace osu.Game.Rulesets.Catch.Beatmaps if (obj.NewCombo) { if (lastObj != null) lastObj.LastInCombo = true; - colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count; + colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count; } obj.IndexInBeatmap = index++; - obj.ComboColour = beatmap.ComboColors[colourIndex]; + obj.ComboColour = beatmap.ComboColours[colourIndex]; lastObj = obj; } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index 3dad5b508c..bfcdec9321 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { applyStacking(beatmap); - if (beatmap.ComboColors.Count == 0) + if (beatmap.ComboColours.Count == 0) return; int comboIndex = 0; @@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Osu.Beatmaps if (obj.NewCombo) { comboIndex = 0; - colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count; + colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count; } obj.IndexInCurrentCombo = comboIndex++; - obj.ComboColour = beatmap.ComboColors[colourIndex]; + obj.ComboColour = beatmap.ComboColours[colourIndex]; } } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index b74be134c1..2c46a124d8 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { - var comboColors = decoder.Decode(stream).ComboColors; + var comboColors = decoder.Decode(stream).ComboColours; Color4[] expectedColors = { diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 80dea9d01d..c36e825252 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -102,9 +102,9 @@ namespace osu.Game.Tests.Beatmaps.Formats new Color4(255, 187, 255, 255), new Color4(255, 177, 140, 255), }; - Assert.AreEqual(expected.Length, beatmap.ComboColors.Count); + Assert.AreEqual(expected.Length, beatmap.ComboColours.Count); for (int i = 0; i < expected.Length; i++) - Assert.AreEqual(expected[i], beatmap.ComboColors[i]); + Assert.AreEqual(expected[i], beatmap.ComboColours[i]); } [Test] diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 9b00993b6e..93817b9b8f 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -9,6 +9,7 @@ using System.Linq; using osu.Game.Beatmaps.ControlPoints; using osu.Game.IO.Serialization; using Newtonsoft.Json; +using osu.Game.Beatmaps.Formats; using osu.Game.IO.Serialization.Converters; namespace osu.Game.Beatmaps @@ -16,14 +17,14 @@ namespace osu.Game.Beatmaps /// /// A Beatmap containing converted HitObjects. /// - public class Beatmap : IJsonSerializable + public class Beatmap : IJsonSerializable, IHasComboColours where T : HitObject { public BeatmapInfo BeatmapInfo = new BeatmapInfo(); public ControlPointInfo ControlPointInfo = new ControlPointInfo(); public List Breaks = new List(); - public List ComboColors = new List + public List ComboColours { get; set; } = new List { new Color4(17, 136, 170, 255), new Color4(102, 136, 0, 255), @@ -55,7 +56,7 @@ namespace osu.Game.Beatmaps BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; Breaks = original?.Breaks ?? Breaks; - ComboColors = original?.ComboColors ?? ComboColors; + ComboColours = original?.ComboColours ?? ComboColours; HitObjects = original?.HitObjects ?? HitObjects; if (original == null && Metadata == null) diff --git a/osu.Game/Beatmaps/Formats/IHasComboColours.cs b/osu.Game/Beatmaps/Formats/IHasComboColours.cs new file mode 100644 index 0000000000..da0ed1693d --- /dev/null +++ b/osu.Game/Beatmaps/Formats/IHasComboColours.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using OpenTK.Graphics; + +namespace osu.Game.Beatmaps.Formats +{ + public interface IHasComboColours + { + List ComboColours { get; set; } + } +} diff --git a/osu.Game/Beatmaps/Formats/IHasCustomColours.cs b/osu.Game/Beatmaps/Formats/IHasCustomColours.cs new file mode 100644 index 0000000000..7de5625fe5 --- /dev/null +++ b/osu.Game/Beatmaps/Formats/IHasCustomColours.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using OpenTK.Graphics; + +namespace osu.Game.Beatmaps.Formats +{ + public interface IHasCustomColours + { + Dictionary CustomColours { get; set; } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 915ea9b587..1bb67f9e75 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -5,7 +5,6 @@ using System; using System.Globalization; using System.IO; using System.Linq; -using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; @@ -19,7 +18,6 @@ namespace osu.Game.Beatmaps.Formats private Beatmap beatmap; - private bool hasCustomColours; private ConvertHitObjectParser parser; private LegacySampleBank defaultSampleBank; @@ -72,29 +70,28 @@ namespace osu.Game.Beatmaps.Formats { case Section.General: handleGeneral(line); - break; + return; case Section.Editor: handleEditor(line); - break; + return; case Section.Metadata: handleMetadata(line); - break; + return; case Section.Difficulty: handleDifficulty(line); - break; + return; case Section.Events: handleEvents(line); - break; + return; case Section.TimingPoints: handleTimingPoints(line); - break; - case Section.Colours: - handleColours(line); - break; + return; case Section.HitObjects: handleHitObjects(line); - break; + return; } + + base.ParseLine(beatmap, section, line); } private void handleGeneral(string line) @@ -364,38 +361,6 @@ namespace osu.Game.Beatmaps.Formats } } - private void handleColours(string line) - { - var pair = SplitKeyVal(line, ':'); - - string[] split = pair.Value.Split(','); - - if (split.Length != 3) - throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); - - byte r, g, b; - if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b)) - throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); - - if (!hasCustomColours) - { - beatmap.ComboColors.Clear(); - hasCustomColours = true; - } - - // Note: the combo index specified in the beatmap is discarded - if (pair.Key.StartsWith(@"Combo")) - { - beatmap.ComboColors.Add(new Color4 - { - R = r / 255f, - G = g / 255f, - B = b / 255f, - A = 1f, - }); - } - } - private void handleHitObjects(string line) { // If the ruleset wasn't specified, assume the osu!standard ruleset. diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index cfa0c28de0..b6634d0722 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using OpenTK.Graphics; namespace osu.Game.Beatmaps.Formats { @@ -40,7 +41,53 @@ namespace osu.Game.Beatmaps.Formats protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//"); - protected abstract void ParseLine(T output, Section section, string line); + protected virtual void ParseLine(T output, Section section, string line) + { + switch (section) + { + case Section.Colours: + handleColours(output, line); + return; + } + } + + private bool hasCustomColours; + + private void handleColours(T output, string line) + { + var pair = SplitKeyVal(line, ':'); + + bool isCombo = pair.Key.StartsWith(@"Combo"); + + string[] split = pair.Value.Split(','); + + if (split.Length != 3) + throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); + + if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b)) + throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); + + Color4 colour = new Color4(r, g, b, 255); + + if (isCombo) + { + if (!(output is IHasComboColours tHasComboColours)) return; + + if (!hasCustomColours) + { + // remove default colours. + tHasComboColours.ComboColours.Clear(); + hasCustomColours = true; + } + + tHasComboColours.ComboColours.Add(colour); + } + else + { + if (!(output is IHasCustomColours tHasCustomColours)) return; + tHasCustomColours.CustomColours[pair.Key] = colour; + } + } protected KeyValuePair SplitKeyVal(string line, char separator) { diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index e35276ae1a..85b0f8d42e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -46,11 +46,13 @@ namespace osu.Game.Beatmaps.Formats { case Section.Events: handleEvents(line); - break; + return; case Section.Variables: handleVariables(line); - break; + return; } + + base.ParseLine(storyboard, section, line); } private void handleEvents(string line) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1d3baa6c0d..4672cf7672 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -267,6 +267,8 @@ + + From 16eab2eebf1b11d3ac413339d20b85f37d4f391c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Mar 2018 19:14:01 +0900 Subject: [PATCH 127/537] Fix combo colours not working until now --- osu.Game/Beatmaps/BeatmapConverter.cs | 5 +++++ osu.Game/Beatmaps/Formats/IHasComboColours.cs | 3 +++ osu.Game/Beatmaps/Formats/IHasCustomColours.cs | 3 +++ 3 files changed, 11 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index 711e220b88..c35c5df89b 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -50,9 +50,14 @@ namespace osu.Game.Beatmaps protected virtual Beatmap ConvertBeatmap(Beatmap original) { var beatmap = CreateBeatmap(); + + // todo: this *must* share logic (or directly use) Beatmap's constructor. + // right now this isn't easily possible due to generic entanglement. beatmap.BeatmapInfo = original.BeatmapInfo; beatmap.ControlPointInfo = original.ControlPointInfo; beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList(); + beatmap.Breaks = original.Breaks; + beatmap.ComboColours = original.ComboColours; return beatmap; } diff --git a/osu.Game/Beatmaps/Formats/IHasComboColours.cs b/osu.Game/Beatmaps/Formats/IHasComboColours.cs index da0ed1693d..93c6c18eec 100644 --- a/osu.Game/Beatmaps/Formats/IHasComboColours.cs +++ b/osu.Game/Beatmaps/Formats/IHasComboColours.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using System.Collections.Generic; using OpenTK.Graphics; diff --git a/osu.Game/Beatmaps/Formats/IHasCustomColours.cs b/osu.Game/Beatmaps/Formats/IHasCustomColours.cs index 7de5625fe5..14614a6728 100644 --- a/osu.Game/Beatmaps/Formats/IHasCustomColours.cs +++ b/osu.Game/Beatmaps/Formats/IHasCustomColours.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using System.Collections.Generic; using OpenTK.Graphics; From 0e69ab161549dd32e0e2144bf49b11da21a4a165 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Wed, 14 Mar 2018 00:17:12 +0300 Subject: [PATCH 128/537] Introduce ScreenshotManager class --- osu.Game/Graphics/ScreenshotManager.cs | 50 ++++++++++++++++++++++++++ osu.Game/OsuGame.cs | 47 ++++-------------------- osu.Game/osu.Game.csproj | 1 + 3 files changed, 57 insertions(+), 41 deletions(-) create mode 100644 osu.Game/Graphics/ScreenshotManager.cs diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs new file mode 100644 index 0000000000..7304d653cd --- /dev/null +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -0,0 +1,50 @@ +using System; +using System.Drawing.Imaging; +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Game.Configuration; + +namespace osu.Game.Graphics +{ + public class ScreenshotManager : Drawable + { + private Bindable screenshotFormat; + private GameHost host; + private Storage storage; + + [BackgroundDependencyLoader] + private void load(GameHost host, OsuConfigManager config, Storage storage) + { + this.host = host; + this.storage = storage.GetStorageForDirectory(@"screenshots"); + + screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); + } + + public void TakeScreenshot() + { + host.TakeScreenshot(screenshotBitmap => + { + var stream = storage.GetStream($"{DateTime.Now:yyyyMMddTHHmmss}.{screenshotFormat.ToString().ToLower()}", FileAccess.Write); + + switch (screenshotFormat.Value) + { + case ScreenshotFormat.Bmp: + screenshotBitmap.Save(stream, ImageFormat.Bmp); + break; + case ScreenshotFormat.Png: + screenshotBitmap.Save(stream, ImageFormat.Png); + break; + case ScreenshotFormat.Jpg: + screenshotBitmap.Save(stream, ImageFormat.Jpeg); + break; + default: + throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); + } + }); + } + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1078548bef..18ea093e97 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; -using System.Drawing.Imaging; -using System.IO; using osu.Framework.Configuration; using osu.Framework.Screens; using osu.Game.Configuration; @@ -83,9 +81,9 @@ namespace osu.Game private Bindable configRuleset; public Bindable Ruleset = new Bindable(); - private Bindable configSkin; + private ScreenshotManager screenshotManager; - private Bindable screenshotFormat; + private Bindable configSkin; private readonly string[] args; @@ -137,9 +135,6 @@ namespace osu.Game // bind config int to database SkinInfo configSkin = LocalConfig.GetBindable(OsuSetting.Skin); - - screenshotFormat = LocalConfig.GetBindable(OsuSetting.ScreenshotFormat); - SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID; configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default; configSkin.TriggerChange(); @@ -216,6 +211,9 @@ namespace osu.Game BeatmapManager.GetStableStorage = GetStorageForStableInstall; + screenshotManager = new ScreenshotManager(); + Add(screenshotManager); + AddRange(new Drawable[] { new VolumeControlReceptor @@ -439,46 +437,13 @@ namespace osu.Game direct.ToggleVisibility(); return true; case GlobalAction.TakeScreenshot: - if (Window.ScreenshotTakenAction == null) - Window.ScreenshotTakenAction = (screenshotBitmap) => - { - var fileName = getScreenshotFileName(screenshotFormat); - - switch (screenshotFormat.Value) - { - case ScreenshotFormat.Bmp: - screenshotBitmap.Save(fileName, ImageFormat.Bmp); - break; - case ScreenshotFormat.Png: - screenshotBitmap.Save(fileName, ImageFormat.Png); - break; - case ScreenshotFormat.Jpg: - screenshotBitmap.Save(fileName, ImageFormat.Jpeg); - break; - default: - throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); - } - }; - - RequestScreenshot(); + screenshotManager.TakeScreenshot(); return true; } return false; } - private string getScreenshotFileName(ScreenshotFormat screenshotFormat) - { - // TODO Change screenshots location - var baseDirectory = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); - var screenshotsDirectory = baseDirectory.CreateSubdirectory("Screenshots"); - - var screenshotExtension = screenshotFormat.ToString().ToLower(); - var screenshots = screenshotsDirectory.GetFiles($"*.{screenshotExtension}"); - - return Path.Combine(screenshotsDirectory.FullName, $"screenshot{screenshots.Length + 1}.{screenshotExtension}"); - } - private readonly BindableDouble inactiveVolumeAdjust = new BindableDouble(); protected override void OnDeactivated() diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fdda575a6c..7576dd6f0c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -288,6 +288,7 @@ + From 1d7be2ad0b224f96dde1feb96b0dd112e722fc2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 10:14:42 +0900 Subject: [PATCH 129/537] Fix incorrect variable name --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index b6634d0722..e4aa9f5091 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -51,7 +51,7 @@ namespace osu.Game.Beatmaps.Formats } } - private bool hasCustomColours; + private bool hasComboColours; private void handleColours(T output, string line) { @@ -73,11 +73,11 @@ namespace osu.Game.Beatmaps.Formats { if (!(output is IHasComboColours tHasComboColours)) return; - if (!hasCustomColours) + if (!hasComboColours) { // remove default colours. tHasComboColours.ComboColours.Clear(); - hasCustomColours = true; + hasComboColours = true; } tHasComboColours.ComboColours.Add(colour); From afc36464503dd10e5dab9a09e1872416ce01b312 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 09:48:03 +0900 Subject: [PATCH 130/537] Move API configuration hooks out of OsuGameBase Also makes username more private, and password completely private. --- osu.Game/Online/API/APIAccess.cs | 35 ++++++++++++------- osu.Game/OsuGameBase.cs | 20 ++--------- .../Sections/General/LoginSettings.cs | 2 +- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index bab53cb462..40584006cd 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -10,6 +10,7 @@ using System.Threading; using osu.Framework.Configuration; using osu.Framework.Logging; using osu.Framework.Threading; +using osu.Game.Configuration; using osu.Game.Online.API.Requests; using osu.Game.Users; @@ -17,6 +18,7 @@ namespace osu.Game.Online.API { public class APIAccess : IAPIProvider { + private readonly OsuConfigManager config; private readonly OAuth authentication; public string Endpoint = @"https://osu.ppy.sh"; @@ -27,11 +29,12 @@ namespace osu.Game.Online.API public Scheduler Scheduler = new Scheduler(); - public string Username; + /// + /// The username/email provided by the user when initiating a login. + /// + public string ProvidedUsername { get; private set; } - //private SecurePassword password; - - public string Password; + private string password; public Bindable LocalUser { get; } = new Bindable(createGuestUser()); @@ -41,18 +44,23 @@ namespace osu.Game.Online.API set { authentication.Token = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); } } - protected bool HasLogin => Token != null || !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password); + protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (should dispose of this or at very least keep a reference). private readonly Thread thread; private readonly Logger log; - public APIAccess() + public APIAccess(OsuConfigManager config) { + this.config = config; + authentication = new OAuth(client_id, client_secret, Endpoint); log = Logger.GetLogger(LoggingTarget.Network); + ProvidedUsername = config.Get(OsuSetting.Username); + Token = config.Get(OsuSetting.Token); + thread = new Thread(run) { IsBackground = true }; thread.Start(); } @@ -111,12 +119,15 @@ namespace osu.Game.Online.API State = APIState.Connecting; - if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password)) + // save the username at this point, if the user requested for it to be. + config.Set(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty); + + if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(ProvidedUsername, password)) { //todo: this fails even on network-related issues. we should probably handle those differently. //NotificationOverlay.ShowMessage("Login failed!"); log.Add(@"Login failed!"); - Password = null; + password = null; authentication.Clear(); continue; } @@ -173,8 +184,8 @@ namespace osu.Game.Online.API { Debug.Assert(State == APIState.Offline); - Username = username; - Password = password; + ProvidedUsername = username; + this.password = password; } /// @@ -283,8 +294,8 @@ namespace osu.Game.Online.API public void Logout(bool clearUsername = true) { flushQueue(); - if (clearUsername) Username = null; - Password = null; + if (clearUsername) ProvidedUsername = null; + password = null; authentication.Clear(); LocalUser.Value = createGuestUser(); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f3c46269d5..2096318a32 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -34,7 +34,7 @@ using osu.Game.Skinning; namespace osu.Game { - public class OsuGameBase : Framework.Game, IOnlineComponent, ICanAcceptFiles + public class OsuGameBase : Framework.Game, ICanAcceptFiles { protected OsuConfigManager LocalConfig; @@ -108,11 +108,7 @@ namespace osu.Game dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); - dependencies.Cache(API = new APIAccess - { - Username = LocalConfig.Get(OsuSetting.Username), - Token = LocalConfig.Get(OsuSetting.Token) - }); + dependencies.Cache(API = new APIAccess(LocalConfig)); dependencies.CacheAs(API); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); @@ -183,8 +179,6 @@ namespace osu.Game lastBeatmap = b; }; - API.Register(this); - FileStore.Cleanup(); } @@ -211,16 +205,6 @@ namespace osu.Game private WorkingBeatmap lastBeatmap; - public void APIStateChanged(APIAccess api, APIState state) - { - switch (state) - { - case APIState.Online: - LocalConfig.Set(OsuSetting.Username, LocalConfig.Get(OsuSetting.SaveUsername) ? API.Username : string.Empty); - break; - } - } - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index a5d068adbd..4a4fc7363e 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -210,7 +210,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { PlaceholderText = "Email address", RelativeSizeAxes = Axes.X, - Text = api?.Username ?? string.Empty, + Text = api?.ProvidedUsername ?? string.Empty, TabbableContentContainer = this }, password = new OsuPasswordTextBox From 83cd2fd31763a773bdf78c2e2f6119b09df5c1f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 10:07:16 +0900 Subject: [PATCH 131/537] Move token saving logic to APIAccess --- osu.Game/Online/API/APIAccess.cs | 21 +++++++++++++++++++-- osu.Game/OsuGameBase.cs | 8 +------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 40584006cd..91b77dcf1f 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -16,7 +16,7 @@ using osu.Game.Users; namespace osu.Game.Online.API { - public class APIAccess : IAPIProvider + public class APIAccess : IAPIProvider, IDisposable { private readonly OsuConfigManager config; private readonly OAuth authentication; @@ -27,7 +27,7 @@ namespace osu.Game.Online.API private ConcurrentQueue queue = new ConcurrentQueue(); - public Scheduler Scheduler = new Scheduler(); + public readonly Scheduler Scheduler = new Scheduler(); /// /// The username/email provided by the user when initiating a login. @@ -310,6 +310,23 @@ namespace osu.Game.Online.API { Scheduler.Update(); } + + private void dispose() + { + config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? Token : string.Empty); + config.Save(); + } + + public void Dispose() + { + dispose(); + GC.SuppressFinalize(this); + } + + ~APIAccess() + { + dispose(); + } } public enum APIState diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 2096318a32..a3e4d34659 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -245,14 +245,8 @@ namespace osu.Game protected override void Dispose(bool isDisposing) { - //refresh token may have changed. - if (LocalConfig != null && API != null) - { - LocalConfig.Set(OsuSetting.Token, LocalConfig.Get(OsuSetting.SavePassword) ? API.Token : string.Empty); - LocalConfig.Save(); - } - base.Dispose(isDisposing); + API.Dispose(); } private readonly List fileImporters = new List(); From 9e09d434dd8088ec1f8f40f98a6de1c46d5d6c0d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Mar 2018 11:27:14 +0900 Subject: [PATCH 132/537] Fix argument null exception in ManiaBeatmapConverter --- .../Beatmaps/ManiaBeatmapConverter.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 2dd3468df0..4734e40803 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps yield break; } - var objects = IsForCurrentRuleset ? generateSpecific(original) : generateConverted(original, beatmap); + var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap); if (objects == null) yield break; @@ -110,10 +110,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// Method that generates hit objects for osu!mania specific beatmaps. /// /// The original hit object. + /// The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap. /// The hit objects generated. - private IEnumerable generateSpecific(HitObject original) + private IEnumerable generateSpecific(HitObject original, Beatmap originalBeatmap) { - var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern); + var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap); Pattern newPattern = generator.Generate(); lastPattern = newPattern; @@ -125,7 +126,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// Method that generates hit objects for non-osu!mania beatmaps. /// /// The original hit object. - /// The original beatmap. This is used + /// The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap. /// The hit objects generated. private IEnumerable generateConverted(HitObject original, Beatmap originalBeatmap) { @@ -164,8 +165,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator { - public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) - : base(random, hitObject, beatmap, previousPattern, null) + public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) + : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { } From 07642546bbc47357a8adc6d3416824f2eaf88f39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 10:42:58 +0900 Subject: [PATCH 133/537] Make APIAccess a component --- osu.Game/Online/API/APIAccess.cs | 24 ++++------------------- osu.Game/Online/API/APIDownloadRequest.cs | 2 +- osu.Game/Online/API/IAPIProvider.cs | 3 +-- osu.Game/OsuGameBase.cs | 24 +++++++---------------- osu.Game/Overlays/Direct/DirectPanel.cs | 2 +- 5 files changed, 14 insertions(+), 41 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 91b77dcf1f..22498d229d 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -8,15 +8,15 @@ using System.Diagnostics; using System.Net; using System.Threading; using osu.Framework.Configuration; +using osu.Framework.Graphics; using osu.Framework.Logging; -using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Online.API.Requests; using osu.Game.Users; namespace osu.Game.Online.API { - public class APIAccess : IAPIProvider, IDisposable + public class APIAccess : Component, IAPIProvider { private readonly OsuConfigManager config; private readonly OAuth authentication; @@ -27,8 +27,6 @@ namespace osu.Game.Online.API private ConcurrentQueue queue = new ConcurrentQueue(); - public readonly Scheduler Scheduler = new Scheduler(); - /// /// The username/email provided by the user when initiating a login. /// @@ -306,27 +304,13 @@ namespace osu.Game.Online.API Id = 1, }; - public void Update() + protected override void Dispose(bool isDisposing) { - Scheduler.Update(); - } + base.Dispose(isDisposing); - private void dispose() - { config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? Token : string.Empty); config.Save(); } - - public void Dispose() - { - dispose(); - GC.SuppressFinalize(this); - } - - ~APIAccess() - { - dispose(); - } } public enum APIState diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index 2dff07a847..2c6a4e02ba 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.API return request; } - private void request_Progress(long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); }); + private void request_Progress(long current, long total) => Progress?.Invoke(current, total); protected APIDownloadRequest() { diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index b3c8774209..4119691c85 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -1,13 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework; using osu.Framework.Configuration; using osu.Game.Users; namespace osu.Game.Online.API { - public interface IAPIProvider : IUpdateable + public interface IAPIProvider { /// /// The local user. diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index a3e4d34659..45fd45b4b5 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -56,8 +56,6 @@ namespace osu.Game protected override string MainResourceFile => @"osu.Game.Resources.dll"; - public APIAccess API; - private Container content; protected override Container Content => content; @@ -108,12 +106,14 @@ namespace osu.Game dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); - dependencies.Cache(API = new APIAccess(LocalConfig)); - dependencies.CacheAs(API); + var api = new APIAccess(LocalConfig); + + dependencies.Cache(api); + dependencies.CacheAs(api); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, API, Host)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Host)); dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore)); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); @@ -180,6 +180,8 @@ namespace osu.Game }; FileStore.Cleanup(); + + AddInternal(api); } private void runMigrations() @@ -237,18 +239,6 @@ namespace osu.Game base.SetHost(host); } - protected override void Update() - { - base.Update(); - API.Update(); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - API.Dispose(); - } - private readonly List fileImporters = new List(); public void Import(params string[] paths) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index e0d806c90f..cba63b4a49 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -186,7 +186,7 @@ namespace osu.Game.Overlays.Direct progressBar.FadeOut(500); }; - request.DownloadProgressed += progress => progressBar.Current.Value = progress; + request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress); request.Success += data => { From ce2997419a8dc4e054755ad825ae6120a24543ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 11:37:50 +0900 Subject: [PATCH 134/537] Expose API scheduling internally --- osu.Game/Online/API/APIAccess.cs | 2 ++ osu.Game/Online/API/APIDownloadRequest.cs | 2 +- osu.Game/Online/API/APIRequest.cs | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 22498d229d..2cb8424bcc 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -65,6 +65,8 @@ namespace osu.Game.Online.API private readonly List components = new List(); + internal void Schedule(Action action) => base.Schedule(action); + public void Register(IOnlineComponent component) { Scheduler.Add(delegate diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index 2c6a4e02ba..0a5210723d 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.API return request; } - private void request_Progress(long current, long total) => Progress?.Invoke(current, total); + private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total)); protected APIDownloadRequest() { diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 35af8eefd7..4b05df661b 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -85,7 +85,7 @@ namespace osu.Game.Online.API if (checkAndProcessFailure()) return; - api.Scheduler.Add(delegate { Success?.Invoke(); }); + api.Schedule(delegate { Success?.Invoke(); }); } public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); @@ -108,7 +108,7 @@ namespace osu.Game.Online.API { if (API == null || pendingFailure == null) return cancelled; - API.Scheduler.Add(pendingFailure); + API.Schedule(pendingFailure); pendingFailure = null; return true; } From ef8d59591445d0f18cddb4003ab7aa5ce859cf47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 11:44:19 +0900 Subject: [PATCH 135/537] Apply formatting changes --- osu.Game/Screens/Play/PlayerLoader.cs | 32 ++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 3f25ef8a5e..cdb6f36a6f 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -112,6 +112,7 @@ namespace osu.Game.Screens.Play } private bool weHandledMouseDown; + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { weHandledMouseDown = true; @@ -139,24 +140,25 @@ namespace osu.Game.Screens.Play return; } - if (pushDebounce == null) pushDebounce = Scheduler.AddDelayed(() => - { - contentOut(); - - this.Delay(250).Schedule(() => + if (pushDebounce == null) + pushDebounce = Scheduler.AddDelayed(() => { - if (!IsCurrentScreen) return; + contentOut(); - if (!Push(player)) - Exit(); - else + this.Delay(250).Schedule(() => { - //By default, we want to load the player and never be returned to. - //Note that this may change if the player we load requested a re-run. - ValidForResume = false; - } - }); - }, 500); + if (!IsCurrentScreen) return; + + if (!Push(player)) + Exit(); + else + { + //By default, we want to load the player and never be returned to. + //Note that this may change if the player we load requested a re-run. + ValidForResume = false; + } + }); + }, 500); } protected override bool OnExiting(Screen next) From d122aa80e47dbc42cab0e66d0f7faade3bfa6383 Mon Sep 17 00:00:00 2001 From: Joseph Madamba <35318437+Joehuu@users.noreply.github.com> Date: Tue, 13 Mar 2018 20:07:03 -0700 Subject: [PATCH 136/537] Edit mods description, multiplier, and code --- osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs | 2 +- osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs | 1 + osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs | 2 +- osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs | 1 - osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs | 3 +-- osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs | 1 + osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs | 3 ++- osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs | 4 ++-- osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs | 7 ++++++- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 2 ++ osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs | 4 ++-- osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs | 1 + osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 1 + osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 1 - osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs | 2 +- osu.Game/Overlays/Mods/AssistedSection.cs | 2 +- osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs | 2 +- osu.Game/Overlays/Mods/DifficultyReductionSection.cs | 2 +- osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 +- osu.Game/Rulesets/Mods/ModCinema.cs | 1 + osu.Game/Rulesets/Mods/ModDaycore.cs | 2 +- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 4 +--- osu.Game/Rulesets/Mods/ModEasy.cs | 1 - osu.Game/Rulesets/Mods/ModHalfTime.cs | 4 +--- osu.Game/Rulesets/Mods/ModHardRock.cs | 1 + osu.Game/Rulesets/Mods/ModNoFail.cs | 2 +- osu.Game/Rulesets/Mods/ModPerfect.cs | 2 ++ osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 2 +- osu.Game/Rulesets/Mods/MultiMod.cs | 2 +- 54 files changed, 100 insertions(+), 53 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs index 124af06d56..8eb8fd8435 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModDaycore : ModDaycore { - public override double ScoreMultiplier => 0.5; + public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs b/osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs index 5c025bdea0..07bc8b825a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs @@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModEasy : ModEasy { + public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!"; } } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs index 303fa6011d..947990cce5 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModHalfTime : ModHalfTime { - public override double ScoreMultiplier => 0.5; + public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index ed33bf7124..9479c9d9b0 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods public class CatchModHardRock : ModHardRock { public override double ScoreMultiplier => 1.12; - public override bool Ranked => true; } } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index 981ebda9eb..14291f744c 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModHidden : ModHidden { - public override string Description => @"Play with fading notes for a slight score advantage."; + public override string Description => @"Play with fading fruits."; public override double ScoreMultiplier => 1.06; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs index 7c7dc5e4f7..99f49e6620 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModDaycore : ModDaycore { - public override double ScoreMultiplier => 0.3; + public override double ScoreMultiplier => 0.5; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs index 64ce86e748..a9d77988c8 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModDoubleTime : ModDoubleTime { - public override double ScoreMultiplier => 1.0; + public override double ScoreMultiplier => 1; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs index 3330d87e88..a1f9e0290e 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs @@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Dual Stages"; public override string ShortenedName => "DS"; public override string Description => @"Double the stages, double the fun!"; - public override double ScoreMultiplier => 1; - public override bool Ranked => false; + public override double ScoreMultiplier => 0; public void ApplyToBeatmapConverter(BeatmapConverter beatmapConverter) { diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs index 1faed5e1c0..0b3e851c64 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs @@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModEasy : ModEasy { + public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!"; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs index 03442507d6..ca5667a400 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs @@ -9,10 +9,11 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModFadeIn : Mod { - public override string Name => "FadeIn"; + public override string Name => "Fade In"; public override string ShortenedName => "FI"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; public override ModType Type => ModType.DifficultyIncrease; + public override string Description => @"Keys appear out of nowhere!"; public override double ScoreMultiplier => 1; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs index 89eb02268e..8d8693d11f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModFlashlight : ModFlashlight { - public override double ScoreMultiplier => 1.0; + public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModHidden) }; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs index 2f8404609f..c00bb4275a 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModHalfTime : ModHalfTime { - public override double ScoreMultiplier => 0.3; + public override double ScoreMultiplier => 0.5; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs index 91edbaf0cf..8b77ea4c25 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModHardRock : ModHardRock { - public override double ScoreMultiplier => 1.0; + public override double ScoreMultiplier => 1; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs index c2fc07da89..9317dba19f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs @@ -8,8 +8,8 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModHidden : ModHidden { - public override string Description => @"The notes fade out before you hit them!"; - public override double ScoreMultiplier => 1.0; + public override string Description => @"Keys fade out before you hit them!"; + public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs index 8a6943d99b..e1c565ff9d 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey1 : ManiaKeyMod { public override int KeyCount => 1; - public override string Name => "1K"; + public override string Name => "One Key"; + public override string ShortenedName => "1K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with one key."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs index 553827ac1c..ebedd2dfdb 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey2 : ManiaKeyMod { public override int KeyCount => 2; - public override string Name => "2K"; + public override string Name => "Two Keys"; + public override string ShortenedName => "2K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with two keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs index ef048c848e..f2e05d2607 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey3 : ManiaKeyMod { public override int KeyCount => 3; - public override string Name => "3K"; + public override string Name => "Three Keys"; + public override string ShortenedName => "3K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with three keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs index 9c713d920f..cd77943f51 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey4 : ManiaKeyMod { public override int KeyCount => 4; - public override string Name => "4K"; + public override string Name => "Four Keys"; + public override string ShortenedName => "4K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with four keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs index a83faf4627..4a0a8a472f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey5 : ManiaKeyMod { public override int KeyCount => 5; - public override string Name => "5K"; + public override string Name => "Five Keys"; + public override string ShortenedName => "5K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with five keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs index d7df901048..602f07a268 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey6 : ManiaKeyMod { public override int KeyCount => 6; - public override string Name => "6K"; + public override string Name => "Six Keys"; + public override string ShortenedName => "6K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with six keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs index 4a3f9857e5..4535bafca3 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey7 : ManiaKeyMod { public override int KeyCount => 7; - public override string Name => "7K"; + public override string Name => "Seven Keys"; + public override string ShortenedName => "7K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with seven keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs index 22c301fb7a..d99d3b1fd9 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey8 : ManiaKeyMod { public override int KeyCount => 8; - public override string Name => "8K"; + public override string Name => "Eight Keys"; + public override string ShortenedName => "8K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with eight keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs index b2a0bc4ddf..16d70d7c86 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs @@ -1,11 +1,16 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey9 : ManiaKeyMod { public override int KeyCount => 9; - public override string Name => "9K"; + public override string Name => "Nine Keys"; + public override string ShortenedName => "9K"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => @"Play with nine keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index cfa5ef88b8..49a47464c8 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; @@ -14,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods { public override string Name => "Mirror"; public override string ShortenedName => "MR"; + public override FontAwesome Icon => FontAwesome.fa_question; public override ModType Type => ModType.Special; public override double ScoreMultiplier => 1; public override bool Ranked => true; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs index a977eef5e3..a007224b74 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModNightcore : ModNightcore { - public override double ScoreMultiplier => 1.0; + public override double ScoreMultiplier => 1; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs index a6cbad44d7..df0f9a5437 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs @@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Random"; public override string ShortenedName => "RD"; public override FontAwesome Icon => FontAwesome.fa_osu_dice; - public override string Description => @"Shuffle around the notes!"; - public override double ScoreMultiplier => 1; + public override string Description => @"Shuffle around the keys!"; + public override double ScoreMultiplier => 0; public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs index eb90338e2f..987bb28932 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModDaycore : ModDaycore { - public override double ScoreMultiplier => 0.5; + public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs b/osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs index 80c83bf5d8..d842b607c6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs @@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModEasy : ModEasy { + public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!"; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs index 7d009b0344..1b9291bcf3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModHalfTime : ModHalfTime { - public override double ScoreMultiplier => 0.5; + public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 29bf3e248d..74c3585d3d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModHardRock : ModHardRock, IApplicableToHitObject { public override double ScoreMultiplier => 1.06; - public override bool Ranked => true; public void ApplyToHitObject(OsuHitObject hitObject) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 4aeb76121a..1117b5bbd5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects { - public override string Description => @"Play with no approach circles and fading notes for a slight score advantage."; + public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; private const double fade_in_duration_multiplier = 0.4; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 057916c04b..c9def8c8cf 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModRelax : ModRelax { - public override string Description => "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things."; + public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index 18b212f781..401e56a3c8 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 ShortenedName => "SO"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout; - public override string Description => @"Spinners will be automatically completed"; + public override string Description => @"Spinners will be automatically completed."; public override double ScoreMultiplier => 0.9; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) }; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index b2b5130be3..613fbc4e32 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Name => "Target"; public override string ShortenedName => "TP"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_target; - public override string Description => @""; + public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs index c50878c6a3..703e6b4f1c 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModDaycore : ModDaycore { - public override double ScoreMultiplier => 0.5; + public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index 1c5e43f411..be6510459e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs @@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModEasy : ModEasy { + public override string Description => @"Beats move slower, less accuracy required, and three lives!"; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs index 9813f8b78e..6542b5a844 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModHalfTime : ModHalfTime { - public override double ScoreMultiplier => 0.5; + public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index ba304c41d8..435a0c1613 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModHardRock : ModHardRock { public override double ScoreMultiplier => 1.06; - public override bool Ranked => true; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index b0ad43b851..be987a1773 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModHidden : ModHidden { - public override string Description => @"The notes fade out before you hit them!"; + public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs index ec2385bfba..d5ad04f595 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModRelax : ModRelax { - public override string Description => @"Relax! You will no longer get dizzyfied by ninja-like spinners, demanding drumrolls or unexpected katu's."; + public override string Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's."; } } diff --git a/osu.Game/Overlays/Mods/AssistedSection.cs b/osu.Game/Overlays/Mods/AssistedSection.cs index 978b12da19..62c5002642 100644 --- a/osu.Game/Overlays/Mods/AssistedSection.cs +++ b/osu.Game/Overlays/Mods/AssistedSection.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Mods public AssistedSection() { - Header = @"Assisted"; + Header = @"Special"; } } } diff --git a/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs b/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs index cbf67893a9..1d9fdab8d5 100644 --- a/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs +++ b/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Mods public DifficultyIncreaseSection() { - Header = @"Gameplay Difficulty Increase"; + Header = @"Difficulty Increase"; } } } diff --git a/osu.Game/Overlays/Mods/DifficultyReductionSection.cs b/osu.Game/Overlays/Mods/DifficultyReductionSection.cs index c44af8fc4d..651fc222b5 100644 --- a/osu.Game/Overlays/Mods/DifficultyReductionSection.cs +++ b/osu.Game/Overlays/Mods/DifficultyReductionSection.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Mods public DifficultyReductionSection() { - Header = @"Gameplay Difficulty Reduction"; + Header = @"Difficulty Reduction"; } } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 3356a56c33..9f45cada7e 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Autoplay"; public override string ShortenedName => "AT"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto; - public override string Description => "Watch a perfect automated play through the song"; + public override string Description => "Watch a perfect automated play through the song."; public override double ScoreMultiplier => 0; public bool AllowFail => false; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index c0480b0647..015f7381fb 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mods public override string ShortenedName => "CN"; public override bool HasImplementation => false; public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema; + public override string Description => "Watch the video without visual distractions."; } } diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 180199cd70..07e55e00b8 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 ShortenedName => "DC"; public override FontAwesome Icon => FontAwesome.fa_question; - public override string Description => "whoaaaaa"; + public override string Description => "whoaaaaa..."; public override void ApplyToClock(IAdjustableClock clock) { diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index 0b8f4b0b5b..406fa3ccf2 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public class ModDoubleTime : Mod, IApplicableToClock + public abstract class ModDoubleTime : Mod, IApplicableToClock { public override string Name => "Double Time"; public override string ShortenedName => "DT"; @@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Mods public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) }; - public override double ScoreMultiplier => 1.12; - public virtual void ApplyToClock(IAdjustableClock clock) { clock.Rate = 1.5; diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index 5c5b9b1b44..7037edfa31 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -13,7 +13,6 @@ namespace osu.Game.Rulesets.Mods public override string ShortenedName => "EZ"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy; public override ModType Type => ModType.DifficultyReduction; - public override string Description => "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required."; public override double ScoreMultiplier => 0.5; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index bb9ed0047d..883225a66b 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -13,12 +13,10 @@ namespace osu.Game.Rulesets.Mods public override string ShortenedName => "HT"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime; public override ModType Type => ModType.DifficultyReduction; - public override string Description => "Less zoom"; + public override string Description => "Less zoom..."; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime) }; - public override double ScoreMultiplier => 1.12; - public virtual void ApplyToClock(IAdjustableClock clock) { clock.Rate = 0.75; diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index c4c0f38faf..c998bc123f 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Mods public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Everything just got a bit harder..."; + public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; public void ApplyToDifficulty(BeatmapDifficulty difficulty) diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 9686eff99c..8a849825a2 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModNoFail : Mod, IApplicableFailOverride { - public override string Name => "NoFail"; + public override string Name => "No Fail"; public override string ShortenedName => "NF"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail; public override ModType Type => ModType.DifficultyReduction; diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index bb12b2e39f..08942fbe12 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mods @@ -9,6 +10,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Perfect"; public override string ShortenedName => "PF"; + public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => "SS or quit."; protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1; diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 490825220c..ef9ff4c69e 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 ShortenedName => "SD"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath; public override ModType Type => ModType.DifficultyIncrease; - public override string Description => "Miss a note and fail."; + public override string Description => "Miss and fail."; public override double ScoreMultiplier => 1; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; diff --git a/osu.Game/Rulesets/Mods/MultiMod.cs b/osu.Game/Rulesets/Mods/MultiMod.cs index 1de5297e22..5548313f8e 100644 --- a/osu.Game/Rulesets/Mods/MultiMod.cs +++ b/osu.Game/Rulesets/Mods/MultiMod.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => string.Empty; public override string ShortenedName => string.Empty; public override string Description => string.Empty; - public override double ScoreMultiplier => 0.0; + public override double ScoreMultiplier => 0; public Mod[] Mods; } From ea649f96504a72be9ebbcc55c2f70b6dd32e563b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 12:01:15 +0900 Subject: [PATCH 137/537] Avoid scheduling during non-current screen --- osu.Game/Screens/Play/PlayerLoader.cs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index cdb6f36a6f..31e7313c0b 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -131,16 +131,22 @@ namespace osu.Game.Screens.Play private void pushWhenLoaded() { - Schedule(pushWhenLoaded); + if (!IsCurrentScreen) return; - if (!readyForPush) + try { - pushDebounce?.Cancel(); - pushDebounce = null; - return; - } + if (!readyForPush) + { + // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce + // if we become unready for push during the delay. + pushDebounce?.Cancel(); + pushDebounce = null; + return; + } + + if (pushDebounce != null) + return; - if (pushDebounce == null) pushDebounce = Scheduler.AddDelayed(() => { contentOut(); @@ -159,6 +165,11 @@ namespace osu.Game.Screens.Play } }); }, 500); + } + finally + { + Schedule(pushWhenLoaded); + } } protected override bool OnExiting(Screen next) From 149ee381e9b38916838a49708939840daba8a881 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 13:55:16 +0900 Subject: [PATCH 138/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index d29c8365ba..727a8fb93b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d29c8365ba3cf7924b57cf22341f4af55658764c +Subproject commit 727a8fb93b50aec18f8f83c9046243174e09de93 From 81f82d98a14a3b09f412e49891da0c46e51515ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 15:18:21 +0900 Subject: [PATCH 139/537] Rework a lot of naming and structure --- .../Selection/OsuHitObjectOverlayLayer.cs | 26 ---------- .../{HitCircleOverlay.cs => HitCircleMask.cs} | 6 +-- ...erCircleOverlay.cs => SliderCircleMask.cs} | 10 ++-- .../{SliderOverlay.cs => SliderMask.cs} | 10 ++-- .../Edit/OsuHitObjectComposer.cs | 18 +++++-- .../osu.Game.Rulesets.Osu.csproj | 7 ++- .../Visual/TestCaseEditorSelectionLayer.cs | 17 +++--- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 34 +++++++----- .../HitObjectOverlay.cs => HitObjectMask.cs} | 9 ++-- .../Edit/Layers/Selection/SelectionBox.cs | 49 ----------------- .../Screens/Compose}/Layers/BorderLayer.cs | 2 +- .../Compose/Layers/HitObjectMaskLayer.cs} | 37 +++++-------- .../Screens/Compose/Layers/SelectionBox.cs} | 15 +++--- .../Screens/Compose/Layers}/SelectionLayer.cs | 52 ++++++++++++++++--- osu.Game/Screens/Edit/Screens/EditorScreen.cs | 3 ++ osu.Game/osu.Game.csproj | 11 ++-- 16 files changed, 141 insertions(+), 165 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs rename osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/{HitCircleOverlay.cs => HitCircleMask.cs} (82%) rename osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/{SliderCircleOverlay.cs => SliderCircleMask.cs} (73%) rename osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/{SliderOverlay.cs => SliderMask.cs} (82%) rename osu.Game/Rulesets/Edit/{Layers/Selection/HitObjectOverlay.cs => HitObjectMask.cs} (56%) delete mode 100644 osu.Game/Rulesets/Edit/Layers/Selection/SelectionBox.cs rename osu.Game/{Rulesets/Edit => Screens/Edit/Screens/Compose}/Layers/BorderLayer.cs (92%) rename osu.Game/{Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs => Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs} (51%) rename osu.Game/{Rulesets/Edit/Layers/Selection/SelectionOverlay.cs => Screens/Edit/Screens/Compose/Layers/SelectionBox.cs} (83%) rename osu.Game/{Rulesets/Edit/Layers/Selection => Screens/Edit/Screens/Compose/Layers}/SelectionLayer.cs (78%) diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs deleted file mode 100644 index e0d1b34ca5..0000000000 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Edit.Layers.Selection; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; -using osu.Game.Rulesets.Osu.Objects.Drawables; - -namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection -{ - public class OsuHitObjectOverlayLayer : HitObjectOverlayLayer - { - protected override HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) - { - switch (hitObject) - { - case DrawableHitCircle circle: - return new HitCircleOverlay(circle); - case DrawableSlider slider: - return new SliderOverlay(slider); - } - - return base.CreateOverlayFor(hitObject); - } - } -} diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs similarity index 82% rename from osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs rename to osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs index ea5104af18..b48dd73bb5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs @@ -4,15 +4,15 @@ using osu.Framework.Graphics; using osu.Framework.Allocation; using osu.Game.Graphics; -using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { - public class HitCircleOverlay : HitObjectOverlay + public class HitCircleMask : HitObjectMask { - public HitCircleOverlay(DrawableHitCircle hitCircle) + public HitCircleMask(DrawableHitCircle hitCircle) : base(hitCircle) { Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs similarity index 73% rename from osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs rename to osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs index 3c7f8a067b..2de8c2f64e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs @@ -4,28 +4,28 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK; namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { - public class SliderCircleOverlay : HitObjectOverlay + public class SliderCircleMask : HitObjectMask { - public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider) + public SliderCircleMask(DrawableHitCircle sliderHead, DrawableSlider slider) : this(sliderHead, sliderHead.Position, slider) { } - public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider) + public SliderCircleMask(DrawableSliderTail sliderTail, DrawableSlider slider) : this(sliderTail, sliderTail.Position, slider) { } private readonly DrawableOsuHitObject hitObject; - private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider) + private SliderCircleMask(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider) : base(hitObject) { this.hitObject = hitObject; diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs similarity index 82% rename from osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs rename to osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs index d478130868..53f02617cd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs @@ -4,7 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; @@ -13,12 +13,12 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { - public class SliderOverlay : HitObjectOverlay + public class SliderMask : HitObjectMask { private readonly SliderBody body; private readonly DrawableSlider slider; - public SliderOverlay(DrawableSlider slider) + public SliderMask(DrawableSlider slider) : base(slider) { this.slider = slider; @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays AccentColour = Color4.Transparent, PathWidth = sliderObject.Scale * 64 }, - new SliderCircleOverlay(slider.HeadCircle, slider), - new SliderCircleOverlay(slider.TailCircle, slider), + new SliderCircleMask(slider.HeadCircle, slider), + new SliderCircleMask(slider.TailCircle, slider), }; sliderObject.PositionChanged += _ => Position = slider.Position; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 70d49a6b4f..026c85d909 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -5,10 +5,11 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Layers.Selection; using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Osu.Edit.Layers.Selection; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; @@ -32,6 +33,17 @@ namespace osu.Game.Rulesets.Osu.Edit protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; - protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer(); + public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject) + { + switch (hitObject) + { + case DrawableHitCircle circle: + return new HitCircleMask(circle); + case DrawableSlider slider: + return new SliderMask(slider); + } + + return base.CreateMaskFor(hitObject); + } } } diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index d6fe87660f..b8f56c1f5d 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -64,10 +64,9 @@ - - - - + + + diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index dc8a13d044..a7e104dd81 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -8,13 +8,12 @@ using osu.Framework.Allocation; using OpenTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Layers.Selection; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Edit.Layers.Selection; using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual @@ -24,17 +23,15 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { - typeof(SelectionBox), typeof(SelectionLayer), - typeof(SelectionOverlay), + typeof(SelectionBox), typeof(HitObjectComposer), typeof(OsuHitObjectComposer), - typeof(HitObjectOverlayLayer), - typeof(OsuHitObjectOverlayLayer), - typeof(HitObjectOverlay), - typeof(HitCircleOverlay), - typeof(SliderOverlay), - typeof(SliderCircleOverlay) + typeof(HitObjectMaskLayer), + typeof(HitObjectMask), + typeof(HitCircleMask), + typeof(SliderMask), + typeof(SliderCircleMask) }; [BackgroundDependencyLoader] diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 914640622b..3dd8d503ed 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -10,10 +10,10 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit.Layers; -using osu.Game.Rulesets.Edit.Layers.Selection; using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; namespace osu.Game.Rulesets.Edit @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Edit return; } - HitObjectOverlayLayer hitObjectOverlayLayer = CreateHitObjectOverlayLayer(); + HitObjectMaskLayer hitObjectMaskLayer = new HitObjectMaskLayer(this); SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield); var layerBelowRuleset = new BorderLayer @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Edit layerAboveRuleset.Children = new Drawable[] { selectionLayer, // Below object overlays for input - hitObjectOverlayLayer, + hitObjectMaskLayer, selectionLayer.CreateProxy() // Proxy above object overlays for selections }; @@ -106,10 +106,10 @@ namespace osu.Game.Rulesets.Edit } }; - selectionLayer.ObjectSelected += hitObjectOverlayLayer.AddOverlay; - selectionLayer.ObjectDeselected += hitObjectOverlayLayer.RemoveOverlay; - selectionLayer.SelectionCleared += hitObjectOverlayLayer.RemoveSelectionOverlay; - selectionLayer.SelectionFinished += hitObjectOverlayLayer.AddSelectionOverlay; + selectionLayer.ObjectSelected += hitObjectMaskLayer.AddOverlay; + selectionLayer.ObjectDeselected += hitObjectMaskLayer.RemoveOverlay; + selectionLayer.SelectionCleared += hitObjectMaskLayer.RemoveSelectionOverlay; + selectionLayer.SelectionFinished += hitObjectMaskLayer.AddSelectionOverlay; toolboxCollection.Items = new[] { new RadioButton("Select", () => setCompositionTool(null)) } @@ -140,14 +140,22 @@ namespace osu.Game.Rulesets.Edit protected abstract IReadOnlyList CompositionTools { get; } + /// + /// Creates a for a specific . + /// + /// The to create the overlay for. + public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null; + + /// + /// Creates a which outlines s + /// and handles all hitobject movement/pattern adjustments. + /// + /// The overlays. + public virtual SelectionBox CreateSelectionOverlay(IReadOnlyList overlays) => new SelectionBox(overlays); + /// /// Creates a which provides a layer above or below the . /// protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both }; - - /// - /// Creates the which overlays selected s. - /// - protected virtual HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new HitObjectOverlayLayer(); } } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs similarity index 56% rename from osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs rename to osu.Game/Rulesets/Edit/HitObjectMask.cs index 8c58275943..051b42fec6 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -4,13 +4,16 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; -namespace osu.Game.Rulesets.Edit.Layers.Selection +namespace osu.Game.Rulesets.Edit { - public class HitObjectOverlay : Container + /// + /// A mask placed above a adding editing functionality. + /// + public class HitObjectMask : Container { public readonly DrawableHitObject HitObject; - public HitObjectOverlay(DrawableHitObject hitObject) + public HitObjectMask(DrawableHitObject hitObject) { HitObject = hitObject; } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionBox.cs deleted file mode 100644 index 1c25846ee3..0000000000 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionBox.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shapes; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Edit.Layers.Selection -{ - /// - /// A box that represents a drag selection. - /// - public class SelectionBox : VisibilityContainer - { - public const float BORDER_RADIUS = 2; - - /// - /// Creates a new . - /// - public SelectionBox() - { - Masking = true; - BorderColour = Color4.White; - BorderThickness = BORDER_RADIUS; - - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.1f - }; - } - - public void SetDragRectangle(RectangleF rectangle) - { - var topLeft = Parent.ToLocalSpace(rectangle.TopLeft); - var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight); - - Position = topLeft; - Size = bottomRight - topLeft; - } - - public override bool DisposeOnDeathRemoval => true; - - protected override void PopIn() => this.FadeIn(250, Easing.OutQuint); - protected override void PopOut() => this.FadeOut(250, Easing.OutQuint); - } -} diff --git a/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs similarity index 92% rename from osu.Game/Rulesets/Edit/Layers/BorderLayer.cs rename to osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs index 54c30b8d89..49cf078d36 100644 --- a/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using OpenTK.Graphics; -namespace osu.Game.Rulesets.Edit.Layers +namespace osu.Game.Screens.Edit.Screens.Compose.Layers { public class BorderLayer : Container { diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs similarity index 51% rename from osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs rename to osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 24d594f59a..63b5538ad7 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -1,23 +1,25 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; -namespace osu.Game.Rulesets.Edit.Layers.Selection +namespace osu.Game.Screens.Edit.Screens.Compose.Layers { - public class HitObjectOverlayLayer : CompositeDrawable + public class HitObjectMaskLayer : CompositeDrawable { - private readonly Container overlayContainer; + private readonly HitObjectComposer composer; + private readonly Container overlayContainer; - public HitObjectOverlayLayer() + public HitObjectMaskLayer(HitObjectComposer composer) { + this.composer = composer; RelativeSizeAxes = Axes.Both; - InternalChild = overlayContainer = new Container { RelativeSizeAxes = Axes.Both }; + InternalChild = overlayContainer = new Container { RelativeSizeAxes = Axes.Both }; } /// @@ -26,7 +28,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// The to create an overlay for. public void AddOverlay(DrawableHitObject hitObject) { - var overlay = CreateOverlayFor(hitObject); + var overlay = composer.CreateMaskFor(hitObject); if (overlay == null) return; @@ -47,27 +49,14 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection existing.Expire(); } - private SelectionOverlay currentSelectionOverlay; + private SelectionBox currentSelectionBox; - public void AddSelectionOverlay() => AddInternal(currentSelectionOverlay = CreateSelectionOverlay(overlayContainer)); + public void AddSelectionOverlay() => AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer)); public void RemoveSelectionOverlay() { - currentSelectionOverlay?.Hide(); - currentSelectionOverlay?.Expire(); + currentSelectionBox?.Hide(); + currentSelectionBox?.Expire(); } - - /// - /// Creates a for a specific . - /// - /// The to create the overlay for. - protected virtual HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) => null; - - /// - /// Creates a which outlines s - /// and handles all hitobject movement/pattern adjustments. - /// - /// The overlays. - protected virtual SelectionOverlay CreateSelectionOverlay(IReadOnlyList overlays) => new SelectionOverlay(overlays); } } diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs similarity index 83% rename from osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs rename to osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs index c3bb5911f8..0e5d824559 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionOverlay.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs @@ -9,25 +9,28 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Types; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; -namespace osu.Game.Rulesets.Edit.Layers.Selection +namespace osu.Game.Screens.Edit.Screens.Compose.Layers { /// - /// A box which encloses s. + /// A box which surrounds s and provides interactive handles, context menus etc. /// - public class SelectionOverlay : VisibilityContainer + public class SelectionBox : VisibilityContainer { - private readonly IReadOnlyList overlays; + private readonly IReadOnlyList overlays; - public SelectionOverlay(IReadOnlyList overlays) + public const float BORDER_RADIUS = 2; + + public SelectionBox(IReadOnlyList overlays) { this.overlays = overlays; Masking = true; - BorderThickness = SelectionBox.BORDER_RADIUS; + BorderThickness = BORDER_RADIUS; InternalChild = new Box { diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs similarity index 78% rename from osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs rename to osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs index 2f8b9165c4..8c66007bb7 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs @@ -8,12 +8,14 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using OpenTK; +using OpenTK.Graphics; -namespace osu.Game.Rulesets.Edit.Layers.Selection +namespace osu.Game.Screens.Edit.Screens.Compose.Layers { public class SelectionLayer : CompositeDrawable { @@ -46,7 +48,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection RelativeSizeAxes = Axes.Both; } - private SelectionBox selectionBox; + private DragBox dragBox; private readonly HashSet selectedHitObjects = new HashSet(); @@ -58,20 +60,20 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection protected override bool OnDragStart(InputState state) { - AddInternal(selectionBox = new SelectionBox()); + AddInternal(dragBox = new DragBox()); return true; } protected override bool OnDrag(InputState state) { - selectionBox.Show(); + dragBox.Show(); var dragPosition = state.Mouse.NativeState.Position; var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition; var screenSpaceDragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y); - selectionBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat); + dragBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat); selectQuad(screenSpaceDragQuad); return true; @@ -79,8 +81,8 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection protected override bool OnDragEnd(InputState state) { - selectionBox.Hide(); - selectionBox.Expire(); + dragBox.Hide(); + dragBox.Expire(); finishSelection(); @@ -197,5 +199,41 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection return; SelectionFinished?.Invoke(); } + + /// + /// A box that represents a drag selection. + /// + private class DragBox : VisibilityContainer + { + /// + /// Creates a new . + /// + public DragBox() + { + Masking = true; + BorderColour = Color4.White; + BorderThickness = SelectionBox.BORDER_RADIUS; + + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f + }; + } + + public void SetDragRectangle(RectangleF rectangle) + { + var topLeft = Parent.ToLocalSpace(rectangle.TopLeft); + var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight); + + Position = topLeft; + Size = bottomRight - topLeft; + } + + public override bool DisposeOnDeathRemoval => true; + + protected override void PopIn() => this.FadeIn(250, Easing.OutQuint); + protected override void PopOut() => this.FadeOut(250, Easing.OutQuint); + } } } diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs index 2e654b4373..009830502e 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreen.cs @@ -8,6 +8,9 @@ using osu.Game.Beatmaps; namespace osu.Game.Screens.Edit.Screens { + /// + /// TODO: eventually make this inherit Screen and add a local scren stack inside the Editor. + /// public class EditorScreen : Container { public readonly Bindable Beatmap = new Bindable(); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fa99ae616a..14d2381640 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -361,10 +361,7 @@ - - - - + @@ -379,6 +376,10 @@ + + + + @@ -393,8 +394,6 @@ - - From 1eeacb7bf7bed936dfc579a7d1e22f1d0a1793ff Mon Sep 17 00:00:00 2001 From: Joseph Madamba <35318437+Joehuu@users.noreply.github.com> Date: Wed, 14 Mar 2018 01:05:19 -0700 Subject: [PATCH 140/537] Change descriptions and special section --- osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs | 3 --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 2 -- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- .../Overlays/Mods/{AssistedSection.cs => SpecialSection.cs} | 4 ++-- osu.Game/Rulesets/Mods/ModDaycore.cs | 2 +- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 2 +- osu.Game/Rulesets/Mods/ModNightcore.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 16 files changed, 7 insertions(+), 36 deletions(-) rename osu.Game/Overlays/Mods/{AssistedSection.cs => SpecialSection.cs} (86%) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs index e1c565ff9d..c0107e3758 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey1 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 1; public override string Name => "One Key"; public override string ShortenedName => "1K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with one key."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs index ebedd2dfdb..11dbe0ba76 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey2 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 2; public override string Name => "Two Keys"; public override string ShortenedName => "2K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with two keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs index f2e05d2607..94ad53d8ea 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey3 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 3; public override string Name => "Three Keys"; public override string ShortenedName => "3K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with three keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs index cd77943f51..d9c27c5ef1 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey4 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 4; public override string Name => "Four Keys"; public override string ShortenedName => "4K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with four keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs index 4a0a8a472f..e54bae93a7 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey5 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 5; public override string Name => "Five Keys"; public override string ShortenedName => "5K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with five keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs index 602f07a268..9c3bdf46b9 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey6 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 6; public override string Name => "Six Keys"; public override string ShortenedName => "6K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with six keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs index 4535bafca3..f17ac80be5 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey7 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 7; public override string Name => "Seven Keys"; public override string ShortenedName => "7K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with seven keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs index d99d3b1fd9..36a6fc838f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey8 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 8; public override string Name => "Eight Keys"; public override string ShortenedName => "8K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with eight keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs index 16d70d7c86..10f03e2480 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Graphics; - namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModKey9 : ManiaKeyMod @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override int KeyCount => 9; public override string Name => "Nine Keys"; public override string ShortenedName => "9K"; - public override FontAwesome Icon => FontAwesome.fa_question; public override string Description => @"Play with nine keys."; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index 49a47464c8..cfa5ef88b8 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; @@ -15,7 +14,6 @@ namespace osu.Game.Rulesets.Mania.Mods { public override string Name => "Mirror"; public override string ShortenedName => "MR"; - public override FontAwesome Icon => FontAwesome.fa_question; public override ModType Type => ModType.Special; public override double ScoreMultiplier => 1; public override bool Ranked => true; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d0a507be98..1ad1adadcf 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -287,7 +287,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopCentre, Action = modButtonPressed, }, - new AssistedSection + new SpecialSection { RelativeSizeAxes = Axes.X, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Mods/AssistedSection.cs b/osu.Game/Overlays/Mods/SpecialSection.cs similarity index 86% rename from osu.Game/Overlays/Mods/AssistedSection.cs rename to osu.Game/Overlays/Mods/SpecialSection.cs index 62c5002642..75b2462ff5 100644 --- a/osu.Game/Overlays/Mods/AssistedSection.cs +++ b/osu.Game/Overlays/Mods/SpecialSection.cs @@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Overlays.Mods { - public class AssistedSection : ModSection + public class SpecialSection : ModSection { protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; public override ModType ModType => ModType.Special; @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Mods SelectedColour = colours.BlueLight; } - public AssistedSection() + public SpecialSection() { Header = @"Special"; } diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 07e55e00b8..da4263875b 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 ShortenedName => "DC"; public override FontAwesome Icon => FontAwesome.fa_question; - public override string Description => "whoaaaaa..."; + public override string Description => "Whoaaaaa..."; public override void ApplyToClock(IAdjustableClock clock) { diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index 406fa3ccf2..6225a6feee 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods public override string ShortenedName => "DT"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime; public override ModType Type => ModType.DifficultyIncrease; - public override string Description => "Zoooooooooom"; + public override string Description => "Zoooooooooom..."; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) }; diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index ad4df55b91..c2925f440f 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Nightcore"; public override string ShortenedName => "NC"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore; - public override string Description => "uguuuuuuuu"; + public override string Description => "Uguuuuuuuu..."; public override void ApplyToClock(IAdjustableClock clock) { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7214a31639..21a7ba76f6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -553,7 +553,7 @@ - + From 25f738c4aeeb154c62d20202850bedeba14e33e0 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Wed, 14 Mar 2018 12:57:55 +0300 Subject: [PATCH 141/537] Do not support bmp screenshots --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Configuration/ScreenshotFormat.cs | 1 - osu.Game/Graphics/ScreenshotManager.cs | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index dd17f2c4aa..70260b349e 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -83,7 +83,7 @@ namespace osu.Game.Configuration Set(OsuSetting.Version, string.Empty); - Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Png); + Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); } public OsuConfigManager(Storage storage) : base(storage) diff --git a/osu.Game/Configuration/ScreenshotFormat.cs b/osu.Game/Configuration/ScreenshotFormat.cs index 1bc3013af9..b9309fae3a 100644 --- a/osu.Game/Configuration/ScreenshotFormat.cs +++ b/osu.Game/Configuration/ScreenshotFormat.cs @@ -7,7 +7,6 @@ namespace osu.Game.Configuration { public enum ScreenshotFormat { - Bmp = 0, // TODO: Figure out the best way to hide this from the dropdown [Description("JPG (web-friendly)")] Jpg = 1, [Description("PNG (lossless)")] diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 7304d653cd..e7f21d11fa 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -32,9 +32,6 @@ namespace osu.Game.Graphics switch (screenshotFormat.Value) { - case ScreenshotFormat.Bmp: - screenshotBitmap.Save(stream, ImageFormat.Bmp); - break; case ScreenshotFormat.Png: screenshotBitmap.Save(stream, ImageFormat.Png); break; From a438e45434502d7c239176b558d2baec228b4414 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Wed, 14 Mar 2018 12:33:08 +0100 Subject: [PATCH 142/537] make SelectTypes set mods instead of only adding new ones also made the method actually take types as parameter to make it consistent --- osu.Game/Overlays/Mods/ModSection.cs | 18 +++++++++--------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 03c1f0468c..0986931d00 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -114,19 +114,19 @@ namespace osu.Game.Overlays.Mods } /// - /// Select one or more mods in this section. + /// Select one or more mods in this section and deselects all other ones. /// - /// The types of s which should be deselected. - public void SelectTypes(IEnumerable mods) + /// The types of s which should be selected. + public void SelectTypes(IEnumerable modTypes) { foreach (var button in buttons) { - for (int i = 0; i < button.Mods.Length; i++) - { - foreach (var mod in mods) - if (mod.GetType().IsInstanceOfType(button.Mods[i])) - button.SelectAt(i); - } + int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m))); + + if (i >= 0) + button.SelectAt(i); + else + button.Deselect(); } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d0a507be98..cc4b354fa2 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Mods private void selectedModsChanged(IEnumerable obj) { foreach (ModSection section in ModSectionsContainer.Children) - section.SelectTypes(obj); + section.SelectTypes(obj.Select(m => m.GetType()).ToList()); updateMods(); } From 070decf8902eb83025ba7ed2009fe3587649be5f Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Wed, 14 Mar 2018 12:38:22 +0100 Subject: [PATCH 143/537] small code cleanups --- osu.Game/Overlays/Mods/ModSection.cs | 31 ++++++++++------------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 0986931d00..4765787caf 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -27,14 +27,8 @@ namespace osu.Game.Overlays.Mods public string Header { - get - { - return headerLabel.Text; - } - set - { - headerLabel.Text = value; - } + get => headerLabel.Text; + set => headerLabel.Text = value; } public IEnumerable SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null); @@ -47,12 +41,12 @@ namespace osu.Game.Overlays.Mods { if (m == null) return new ModButtonEmpty(); - else - return new ModButton(m) - { - SelectedColour = selectedColour, - SelectionChanged = Action, - }; + + return new ModButton(m) + { + SelectedColour = selectedColour, + SelectionChanged = Action, + }; }).ToArray(); ButtonsContainer.Children = modContainers; @@ -65,10 +59,7 @@ namespace osu.Game.Overlays.Mods private Color4 selectedColour = Color4.White; public Color4 SelectedColour { - get - { - return selectedColour; - } + get => selectedColour; set { if (value == selectedColour) return; @@ -102,13 +93,13 @@ namespace osu.Game.Overlays.Mods { Mod selected = button.SelectedMod; if (selected == null) continue; - foreach (Type type in modTypes) + foreach (var type in modTypes) if (type.IsInstanceOfType(selected)) { if (immediate) button.Deselect(); else - Scheduler.AddDelayed(() => button.Deselect(), delay += 50); + Scheduler.AddDelayed(button.Deselect, delay += 50); } } } From 0da3ae0944a8fe4d247c3bd39d997036679c23cd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Mar 2018 20:52:48 +0900 Subject: [PATCH 144/537] Add some basic tests + fixes to seek/seekforward functionality --- .../Visual/TestCaseEditorSeekSnapping.cs | 325 ++++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + osu.Game/Rulesets/Edit/HitObjectComposer.cs | 50 ++- 3 files changed, 364 insertions(+), 12 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs new file mode 100644 index 0000000000..36319c0f71 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -0,0 +1,325 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseEditorSeekSnapping : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(HitObjectComposer) }; + + private Track track; + private HitObjectComposer composer; + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + var testBeatmap = new Beatmap + { + ControlPointInfo = new ControlPointInfo + { + TimingPoints = + { + new TimingControlPoint { Time = 0, BeatLength = 200}, + new TimingControlPoint { Time = 100, BeatLength = 400 }, + new TimingControlPoint { Time = 175, BeatLength = 800 }, + new TimingControlPoint { Time = 350, BeatLength = 200 }, + new TimingControlPoint { Time = 450, BeatLength = 100 } + } + }, + HitObjects = + { + new HitCircle { StartTime = 0 }, + new HitCircle { StartTime = 5000 } + } + }; + + osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap); + track = osuGame.Beatmap.Value.Track; + + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset()) }, + new Drawable[] { new TimingPointVisualiser(testBeatmap, track) }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + } + }; + +// testSeekNoSnapping(); +// testSeekSnappingOnBeat(); +// testSeekSnappingInBetweenBeat(); +// testSeekForwardNoSnapping(); + testSeekForwardSnappingOnBeat(); + } + + /// + /// Tests whether time is correctly seeked without snapping. + /// + private void testSeekNoSnapping() + { + reset(); + + // Forwards + AddStep("Seek(0)", () => composer.SeekTo(0)); + AddAssert("Time = 0", () => track.CurrentTime == 0); + AddStep("Seek(33)", () => composer.SeekTo(33)); + AddAssert("Time = 33", () => track.CurrentTime == 33); + AddStep("Seek(89)", () => composer.SeekTo(89)); + AddAssert("Time = 89", () => track.CurrentTime == 89); + + // Backwards + AddStep("Seek(25)", () => composer.SeekTo(25)); + AddAssert("Time = 25", () => track.CurrentTime == 25); + AddStep("Seek(0)", () => composer.SeekTo(0)); + AddAssert("Time = 0", () => track.CurrentTime == 0); + } + + /// + /// Tests whether seeking to exact beat times puts us on the beat time. + /// These are the white/yellow ticks on the graph. + /// + private void testSeekSnappingOnBeat() + { + reset(); + + AddStep("Seek(0), Snap", () => composer.SeekTo(0, true)); + AddAssert("Time = 0", () => track.CurrentTime == 0); + AddStep("Seek(50), Snap", () => composer.SeekTo(50, true)); + AddAssert("Time = 50", () => track.CurrentTime == 50); + AddStep("Seek(100), Snap", () => composer.SeekTo(100, true)); + AddAssert("Time = 100", () => track.CurrentTime == 100); + AddStep("Seek(175), Snap", () => composer.SeekTo(175, true)); + AddAssert("Time = 175", () => track.CurrentTime == 175); + AddStep("Seek(350), Snap", () => composer.SeekTo(350, true)); + AddAssert("Time = 350", () => track.CurrentTime == 350); + AddStep("Seek(400), Snap", () => composer.SeekTo(400, true)); + AddAssert("Time = 400", () => track.CurrentTime == 400); + AddStep("Seek(450), Snap", () => composer.SeekTo(450, true)); + AddAssert("Time = 450", () => track.CurrentTime == 450); + } + + /// + /// Tests whether seeking to somewhere in the middle between beats puts us on the expected beats. + /// For example, snapping between a white/yellow beat should put us on either the yellow or white, depending on which one we're closer too. + /// If + /// + private void testSeekSnappingInBetweenBeat() + { + reset(); + + AddStep("Seek(24), Snap", () => composer.SeekTo(24, true)); + AddAssert("Time = 0", () => track.CurrentTime == 0); + AddStep("Seek(26), Snap", () => composer.SeekTo(26, true)); + AddAssert("Time = 50", () => track.CurrentTime == 50); + AddStep("Seek(150), Snap", () => composer.SeekTo(150, true)); + AddAssert("Time = 100", () => track.CurrentTime == 100); + AddStep("Seek(170), Snap", () => composer.SeekTo(170, true)); + AddAssert("Time = 175", () => track.CurrentTime == 175); + AddStep("Seek(274), Snap", () => composer.SeekTo(274, true)); + AddAssert("Time = 175", () => track.CurrentTime == 175); + AddStep("Seek(276), Snap", () => composer.SeekTo(276, true)); + AddAssert("Time = 350", () => track.CurrentTime == 350); + } + + /// + /// Tests that when seeking forward with no beat snapping, beats are never snapped to, nor the next timing point (if we've skipped it). + /// + private void testSeekForwardNoSnapping() + { + reset(); + + AddStep("SeekForward", () => composer.SeekForward()); + AddAssert("Time = 50", () => track.CurrentTime == 50); + AddStep("SeekForward", () => composer.SeekForward()); + AddAssert("Time = 100", () => track.CurrentTime == 100); + AddStep("SeekForward", () => composer.SeekForward()); + AddAssert("Time = 200", () => track.CurrentTime == 200); + AddStep("SeekForward", () => composer.SeekForward()); + AddAssert("Time = 400", () => track.CurrentTime == 400); + AddStep("SeekForward", () => composer.SeekForward()); + AddAssert("Time = 450", () => track.CurrentTime == 450); + } + + /// + /// Tests that when seeking forward with beat snapping, all beats are snapped to and timing points are never skipped. + /// + private void testSeekForwardSnappingOnBeat() + { + reset(); + + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 50", () => track.CurrentTime == 50); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 100", () => track.CurrentTime == 100); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 175", () => track.CurrentTime == 175); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 350", () => track.CurrentTime == 350); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 400", () => track.CurrentTime == 400); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 450", () => track.CurrentTime == 450); + } + + /// + /// Tests that when seeking forward from in-between two beats, the next beat or timing point is snapped to, and no beats are skipped. + /// + private void testSeekForwardSnappingFromInBetweenBeat() + { + reset(); + + AddStep("Seek(25)", () => composer.SeekTo(25)); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 50", () => track.CurrentTime == 50); + } + + private void reset() + { + AddStep("Reset", () => composer.SeekTo(0)); + } + + private class TestHitObjectComposer : HitObjectComposer + { + public TestHitObjectComposer(Ruleset ruleset) + : base(ruleset) + { + } + + protected override IReadOnlyList CompositionTools => new ICompositionTool[0]; + } + + private class TimingPointVisualiser : CompositeDrawable + { + private readonly Track track; + + private readonly Drawable tracker; + + public TimingPointVisualiser(Beatmap beatmap, Track track) + { + this.track = track; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Width = 0.75f; + + FillFlowContainer timelineContainer; + + InternalChildren = new Drawable[] + { + new Box + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(85f) + }, + new Container + { + Name = "Tracks", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(15), + Children = new[] + { + tracker = new Box + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + RelativePositionAxes = Axes.X, + Width = 2, + Colour = Color4.Red, + }, + timelineContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 5) + }, + } + } + }; + + var timingPoints = beatmap.ControlPointInfo.TimingPoints; + + for (int i = 0; i < timingPoints.Count; i++) + { + TimingControlPoint next = i == timingPoints.Count - 1 ? null : timingPoints[i + 1]; + timelineContainer.Add(new TimingPointTimeline(timingPoints[i], next?.Time ?? beatmap.HitObjects.Last().StartTime, track.Length)); + } + } + + protected override void Update() + { + base.Update(); + + tracker.X = (float)(track.CurrentTime / track.Length); + } + + private class TimingPointTimeline : CompositeDrawable + { + public TimingPointTimeline(TimingControlPoint timingPoint, double endTime, double fullDuration) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Box createMainTick(double time) => new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.X, + X = (float)(time / fullDuration), + Height = 10, + Width = 2 + }; + + Box createBeatTick(double time) => new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.X, + X = (float)(time / fullDuration), + Height = 5, + Width = 2, + Colour = time > endTime ? Color4.Gray : Color4.Yellow + }; + + AddInternal(createMainTick(timingPoint.Time)); + AddInternal(createMainTick(endTime)); + + for (double t = timingPoint.Time + timingPoint.BeatLength / 4; t < fullDuration; t += timingPoint.BeatLength / 4) + AddInternal(createBeatTick(t)); + } + } + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index ed9580211b..80efb0672e 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -130,6 +130,7 @@ + diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index be4fb2f1a1..952b553835 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -153,13 +153,43 @@ namespace osu.Game.Rulesets.Edit /// Seeks the current time one beat-snapped beat-length backwards. /// /// Whether to snap to the closest beat. - public void SeekBackward(bool snapped) => seek(-1, snapped); + public void SeekBackward(bool snapped = false) => seek(-1, snapped); /// /// Seeks the current time one beat-snapped beat-length forwards. /// /// Whether to snap to the closest beat. - public void SeekForward(bool snapped) => seek(1, snapped); + public void SeekForward(bool snapped = false) => seek(1, snapped); + + public void SeekTo(double seekTime, bool snapped = false) + { + // Todo: This should not be a constant, but feels good for now + const int beat_snap_divisor = 4; + + if (!snapped) + { + adjustableClock.Seek(seekTime); + return; + } + + var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime); + double beatSnapLength = timingPoint.BeatLength / beat_snap_divisor; + + // We will be snapping to beats within the timing point + seekTime -= timingPoint.Time; + + // Determine the index from the current timing point of the closest beat to seekTime + int closestBeat = (int)Math.Round(seekTime / beatSnapLength); + seekTime = timingPoint.Time + closestBeat * beatSnapLength; + + // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to + // the next timing point's start time + var nextTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + if (seekTime > nextTimingPoint?.Time) + seekTime = nextTimingPoint.Time; + + adjustableClock.Seek(seekTime); + } private void seek(int direction, bool snapped) { @@ -181,18 +211,14 @@ namespace osu.Game.Rulesets.Edit var firstTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First(); if (currentTimingPoint != firstTimingPoint && seekTime < currentTimingPoint.Time) - seekTime = currentTimingPoint.Time - 1; // -1 to be in the prior timing point's boundary - else if (nextTimingPoint != null && seekTime >= nextTimingPoint.Time) - seekTime = nextTimingPoint.Time + 1; // +1 to be in the next timing point's boundary + adjustableClock.Seek(currentTimingPoint.Time - 1); // -1 to be in the prior timing point's boundary + else if (seekTime >= nextTimingPoint?.Time) + adjustableClock.Seek(nextTimingPoint.Time); // +1 to be in the next timing point's boundary else { - // We will be snapping to beats within the current timing point + // We will be snapping to beats within the timing point seekTime -= currentTimingPoint.Time; - // When rounding below, we need to ensure that abs(seekTime - currentTime) > seekAmount - // This is done by adding direction - a small offset, to seekTime - seekTime += direction; - // Determine the index from the current timing point of the closest beat to seekTime, accounting for scrolling direction int closestBeat; if (direction > 0) @@ -201,9 +227,9 @@ namespace osu.Game.Rulesets.Edit closestBeat = (int)Math.Ceiling(seekTime / seekAmount); seekTime = currentTimingPoint.Time + closestBeat * seekAmount; - } - adjustableClock.Seek(seekTime); + adjustableClock.Seek(seekTime); + } } private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; From f693fbf92d4bddc425c085ae8deb194664691f26 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Mar 2018 23:08:43 +0900 Subject: [PATCH 145/537] Add forward snapping from inbetween beat test --- .../Visual/TestCaseEditorSeekSnapping.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index 36319c0f71..4757e6c857 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -75,7 +75,8 @@ namespace osu.Game.Tests.Visual // testSeekSnappingOnBeat(); // testSeekSnappingInBetweenBeat(); // testSeekForwardNoSnapping(); - testSeekForwardSnappingOnBeat(); +// testSeekForwardSnappingOnBeat(); + testSeekForwardSnappingFromInBetweenBeat(); } /// @@ -189,14 +190,36 @@ namespace osu.Game.Tests.Visual /// /// Tests that when seeking forward from in-between two beats, the next beat or timing point is snapped to, and no beats are skipped. + /// This will also test being extremely close to the next beat/timing point, to ensure rounding is not an issue. /// private void testSeekForwardSnappingFromInBetweenBeat() { reset(); - AddStep("Seek(25)", () => composer.SeekTo(25)); + AddStep("Seek(49)", () => composer.SeekTo(49)); AddStep("SeekForward", () => composer.SeekForward(true)); AddAssert("Time = 50", () => track.CurrentTime == 50); + AddStep("Seek(49.999)", () => composer.SeekTo(49.999)); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 50", () => track.CurrentTime == 50); + AddStep("Seek(99)", () => composer.SeekTo(99)); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 100", () => track.CurrentTime == 100); + AddStep("Seek(99.999)", () => composer.SeekTo(99.999)); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 100", () => track.CurrentTime == 100); + AddStep("Seek(174)", () => composer.SeekTo(174)); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 175", () => track.CurrentTime == 175); + AddStep("Seek(349)", () => composer.SeekTo(349)); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 350", () => track.CurrentTime == 350); + AddStep("Seek(399)", () => composer.SeekTo(399)); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 400", () => track.CurrentTime == 400); + AddStep("Seek(449)", () => composer.SeekTo(449)); + AddStep("SeekForward", () => composer.SeekForward(true)); + AddAssert("Time = 450", () => track.CurrentTime == 450); } private void reset() From 5a1af062d31c01dd9ac1830fa75c584e8f7f4b6e Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Wed, 14 Mar 2018 22:55:24 +0300 Subject: [PATCH 146/537] Handle GlobalAction.TakeScreenshot in ScreenshotManager --- osu.Game/Graphics/ScreenshotManager.cs | 21 +++++++++++++++++++-- osu.Game/OsuGame.cs | 7 +------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index e7f21d11fa..8e0c2cce50 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -3,13 +3,16 @@ using System.Drawing.Imaging; using System.IO; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; using osu.Framework.Platform; using osu.Game.Configuration; +using osu.Game.Input.Bindings; namespace osu.Game.Graphics { - public class ScreenshotManager : Drawable + public class ScreenshotManager : Container, IKeyBindingHandler, IHandleGlobalInput { private Bindable screenshotFormat; private GameHost host; @@ -24,6 +27,20 @@ namespace osu.Game.Graphics screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); } + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.TakeScreenshot: + TakeScreenshot(); + return true; + } + + return false; + } + + public bool OnReleased(GlobalAction action) => false; + public void TakeScreenshot() { host.TakeScreenshot(screenshotBitmap => diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 18ea093e97..44946dd23d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -211,9 +211,6 @@ namespace osu.Game BeatmapManager.GetStableStorage = GetStorageForStableInstall; - screenshotManager = new ScreenshotManager(); - Add(screenshotManager); - AddRange(new Drawable[] { new VolumeControlReceptor @@ -223,6 +220,7 @@ namespace osu.Game }, mainContent = new Container { RelativeSizeAxes = Axes.Both }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, + screenshotManager = new ScreenshotManager() }); loadComponentSingleFile(screenStack = new Loader(), d => @@ -436,9 +434,6 @@ namespace osu.Game case GlobalAction.ToggleDirect: direct.ToggleVisibility(); return true; - case GlobalAction.TakeScreenshot: - screenshotManager.TakeScreenshot(); - return true; } return false; From a42035f49456fe118201a4b92249f0958d9fed2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Mar 2018 13:41:06 +0900 Subject: [PATCH 147/537] Make DrawableHitObject a CompositeDrawable No reason for it to be a container. --- .../Objects/Drawable/DrawableBananaShower.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs | 4 ++-- .../Objects/Drawable/DrawableJuiceStream.cs | 2 +- .../Objects/Drawables/DrawableBarLine.cs | 6 +++--- .../Objects/Drawables/DrawableHoldNote.cs | 4 ++-- .../Objects/Drawables/DrawableHoldNoteTick.cs | 2 +- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs | 2 +- .../Objects/Drawables/DrawableHitCircle.cs | 2 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- .../Objects/Drawables/DrawableSliderTick.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 2 +- .../Objects/Drawables/DrawableBarLine.cs | 2 +- .../Objects/Drawables/DrawableBarLineMajor.cs | 4 ++-- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs | 4 ++-- .../Objects/Drawables/DrawableTaikoHitObject.cs | 2 +- osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs | 6 +++--- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 21 files changed, 29 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs index 7b0370ef88..3c6ec0703d 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable Origin = Anchor.BottomLeft; X = 0; - Child = bananaContainer = new Container { RelativeSizeAxes = Axes.Both }; + InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both }; foreach (var b in s.NestedHitObjects.Cast()) AddNested(getVisualRepresentation?.Invoke(b)); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs index c2b0552ab3..f05f51052d 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable [BackgroundDependencyLoader] private void load() { - Child = new Pulp + InternalChild = new Pulp { AccentColour = AccentColour, Size = Size diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 93a1483f6f..dcad82130e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable [BackgroundDependencyLoader] private void load() { - Children = new[] + InternalChildren = new[] { createPulp(HitObject.VisualRepresentation), border = new Circle @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable if (HitObject.HyperDash) { - Add(new Pulp + AddInternal(new Pulp { RelativePositionAxes = Axes.Both, Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs index 965ca62674..0a2763cbea 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable Origin = Anchor.BottomLeft; X = 0; - Child = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, }; + InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, }; foreach (var o in s.NestedHitObjects.Cast()) AddNested(getVisualRepresentation?.Invoke(o)); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index 91c83a62f0..83d67c855e 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables RelativeSizeAxes = Axes.X; Height = 1; - Add(new Box + AddInternal(new Box { Name = "Bar line", Anchor = Anchor.BottomCentre, @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (isMajor) { - Add(new EquilateralTriangle + AddInternal(new EquilateralTriangle { Name = "Left triangle", Anchor = Anchor.BottomLeft, @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Rotation = 90 }); - Add(new EquilateralTriangle + AddInternal(new EquilateralTriangle { Name = "Right triangle", Anchor = Anchor.BottomRight, diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 5a9ff592bc..6eb34c7005 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { RelativeSizeAxes = Axes.X; - AddRange(new Drawable[] + InternalChildren = new Drawable[] { // The hit object itself cannot be used for various elements because the tail overshoots it // So a specialized container that is updated to contain the tail height is used @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre } - }); + }; foreach (var tick in HitObject.NestedHitObjects.OfType()) { diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index f9c0b96d37..b50a5e897e 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables RelativeSizeAxes = Axes.X; Size = new Vector2(1); - Children = new[] + InternalChildren = new[] { glowContainer = new CircularContainer { diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 8944978bdd..c8aa4588a8 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Children = new Drawable[] + InternalChildren = new Drawable[] { laneGlowPiece = new LaneGlowPiece { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index d70b26e181..1f94f49598 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Position = HitObject.StackedPosition; Scale = new Vector2(h.Scale); - Children = new Drawable[] + InternalChildren = new Drawable[] { glow = new GlowPiece { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 79a4714e33..94179f30d3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Blending = BlendingMode.Additive; Origin = Anchor.Centre; - Children = new Drawable[] + InternalChildren = new Drawable[] { new SpriteIcon { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index fb3294d319..9c2d3f5e07 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Container ticks; Container repeatPoints; - Children = new Drawable[] + InternalChildren = new Drawable[] { Body = new SliderBody(s) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 058e3606f4..22bf63814c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables BorderThickness = 2; BorderColour = Color4.White; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Box { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 722ab4c6d5..2705c213d9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Spinner = s; - Children = new Drawable[] + InternalChildren = new Drawable[] { circleContainer = new Container { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs index cf6aa7d895..d3a38289a8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables RelativeSizeAxes = Axes.Y; Width = tracker_width; - Children = new[] + InternalChildren = new[] { Tracker = new Box { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs index 23c34e9863..656d9bddd4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public DrawableBarLineMajor(BarLine barLine) : base(barLine) { - Add(triangleContainer = new Container + InternalChild = triangleContainer = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables EdgeSmoothness = new Vector2(1), } } - }); + }; Tracker.Alpha = 1f; } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index bc5abce245..65a4e7bd95 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables switch (state) { case ArmedState.Hit: - Content.ScaleTo(0, 100, Easing.OutQuint).Expire(); + this.ScaleTo(0, 100, Easing.OutQuint).Expire(); break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 63e6cfb297..75e1e2a247 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables const float gravity_time = 300; const float gravity_travel_height = 200; - Content.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad); + this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad); this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out) .Then() diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index c9e488764c..4097b65375 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { FillMode = FillMode.Fit; - Add(bodyContainer = new Container + InternalChild = bodyContainer = new Container { RelativeSizeAxes = Axes.Both, Depth = 1, @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } } - }); + }; MainPiece.Add(symbol = new SwellSymbolPiece()); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index e57c2f9944..f20ad5b4aa 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables RelativeSizeAxes = Axes.Both; Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); - Add(MainPiece = CreateMainPiece()); + InternalChild = MainPiece = CreateMainPiece(); MainPiece.KiaiMode = HitObject.Kiai; } diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs index cfa4846939..745ae9ad9d 100644 --- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs @@ -140,12 +140,12 @@ namespace osu.Game.Tests.Visual { Origin = Anchor.Centre; - Add(new Box + InternalChild = new Box { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both - }); + }; switch (direction) { @@ -175,7 +175,7 @@ namespace osu.Game.Tests.Visual Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; - Add(new Box { Size = new Vector2(75) }); + InternalChild = new Box { Size = new Vector2(75) }; } protected override void UpdateState(ArmedState state) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 394b6fa9fd..02f88d9ee0 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -19,7 +19,7 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables { - public abstract class DrawableHitObject : Container, IHasAccentColour + public abstract class DrawableHitObject : CompositeDrawable, IHasAccentColour { public readonly HitObject HitObject; From 50f9d810dd49ed45dfdfea70db321430f981d3d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Mar 2018 15:40:52 +0900 Subject: [PATCH 148/537] Add more testcases + fix up seeking a bit more --- .../Visual/TestCaseEditorSeekSnapping.cs | 146 +++++++++++++++--- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 58 ++++--- 2 files changed, 163 insertions(+), 41 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index 4757e6c857..2d60c6848d 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -43,7 +43,8 @@ namespace osu.Game.Tests.Visual new TimingControlPoint { Time = 100, BeatLength = 400 }, new TimingControlPoint { Time = 175, BeatLength = 800 }, new TimingControlPoint { Time = 350, BeatLength = 200 }, - new TimingControlPoint { Time = 450, BeatLength = 100 } + new TimingControlPoint { Time = 450, BeatLength = 100 }, + new TimingControlPoint { Time = 500, BeatLength = 307.69230769230802 } } }, HitObjects = @@ -71,12 +72,15 @@ namespace osu.Game.Tests.Visual } }; -// testSeekNoSnapping(); -// testSeekSnappingOnBeat(); -// testSeekSnappingInBetweenBeat(); -// testSeekForwardNoSnapping(); -// testSeekForwardSnappingOnBeat(); + testSeekNoSnapping(); + testSeekSnappingOnBeat(); + testSeekSnappingInBetweenBeat(); + testSeekForwardNoSnapping(); + testSeekForwardSnappingOnBeat(); testSeekForwardSnappingFromInBetweenBeat(); + testSeekBackwardSnappingOnBeat(); + testSeekBackwardSnappingFromInBetweenBeat(); + testSeekingWithFloatingPointBeatLength(); } /// @@ -149,7 +153,7 @@ namespace osu.Game.Tests.Visual } /// - /// Tests that when seeking forward with no beat snapping, beats are never snapped to, nor the next timing point (if we've skipped it). + /// Tests that when seeking forward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it). /// private void testSeekForwardNoSnapping() { @@ -174,17 +178,17 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 50", () => track.CurrentTime == 50); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 100", () => track.CurrentTime == 100); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 175", () => track.CurrentTime == 175); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 350", () => track.CurrentTime == 350); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 400", () => track.CurrentTime == 400); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 450", () => track.CurrentTime == 450); } @@ -197,31 +201,131 @@ namespace osu.Game.Tests.Visual reset(); AddStep("Seek(49)", () => composer.SeekTo(49)); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 50", () => track.CurrentTime == 50); AddStep("Seek(49.999)", () => composer.SeekTo(49.999)); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 50", () => track.CurrentTime == 50); AddStep("Seek(99)", () => composer.SeekTo(99)); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 100", () => track.CurrentTime == 100); AddStep("Seek(99.999)", () => composer.SeekTo(99.999)); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 100", () => track.CurrentTime == 100); AddStep("Seek(174)", () => composer.SeekTo(174)); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 175", () => track.CurrentTime == 175); AddStep("Seek(349)", () => composer.SeekTo(349)); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 350", () => track.CurrentTime == 350); AddStep("Seek(399)", () => composer.SeekTo(399)); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 400", () => track.CurrentTime == 400); AddStep("Seek(449)", () => composer.SeekTo(449)); - AddStep("SeekForward", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddAssert("Time = 450", () => track.CurrentTime == 450); } + /// + /// Tests that when seeking backward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it). + /// + private void testSeekBackwardNoSnapping() + { + reset(); + + AddStep("Seek(450)", () => composer.SeekTo(450)); + AddStep("SeekBackward", () => composer.SeekBackward()); + AddAssert("Time = 425", () => track.CurrentTime == 425); + AddStep("SeekBackward", () => composer.SeekBackward()); + AddAssert("Time = 375", () => track.CurrentTime == 375); + AddStep("SeekBackward", () => composer.SeekBackward()); + AddAssert("Time = 325", () => track.CurrentTime == 325); + AddStep("SeekBackward", () => composer.SeekBackward()); + AddAssert("Time = 125", () => track.CurrentTime == 125); + AddStep("SeekBackward", () => composer.SeekBackward()); + AddAssert("Time = 25", () => track.CurrentTime == 25); + AddStep("SeekBackward", () => composer.SeekBackward()); + AddAssert("Time = 0", () => track.CurrentTime == 0); + } + + /// + /// Tests that when seeking backward with beat snapping, all beats are snapped to and timing points are never skipped. + /// + private void testSeekBackwardSnappingOnBeat() + { + reset(); + + AddStep("Seek(450)", () => composer.SeekTo(450)); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 400", () => track.CurrentTime == 400); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 350", () => track.CurrentTime == 350); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 175", () => track.CurrentTime == 175); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 100", () => track.CurrentTime == 100); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 50", () => track.CurrentTime == 50); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 0", () => track.CurrentTime == 0); + } + + /// + /// Tests that when seeking backward from in-between two beats, the previous beat or timing point is snapped to, and no beats are skipped. + /// This will also test being extremely close to the previous beat/timing point, to ensure rounding is not an issue. + /// + private void testSeekBackwardSnappingFromInBetweenBeat() + { + reset(); + + AddStep("Seek(451)", () => composer.SeekTo(451)); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 450", () => track.CurrentTime == 450); + AddStep("Seek(450.999)", () => composer.SeekTo(450.999)); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 450", () => track.CurrentTime == 450); + AddStep("Seek(401)", () => composer.SeekTo(401)); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 400", () => track.CurrentTime == 400); + AddStep("Seek(401.999)", () => composer.SeekTo(401.999)); + AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddAssert("Time = 400", () => track.CurrentTime == 400); + } + + /// + /// Tests that there are no rounding issues when snapping to beats within a timing point with a floating-point beatlength. + /// + private void testSeekingWithFloatingPointBeatLength() + { + reset(); + + double lastTime = 0; + + AddStep("Seek(0)", () => composer.SeekTo(0)); + + for (int i = 0; i < 20; i++) + { + AddStep("SeekForward, Snap", () => + { + lastTime = track.CurrentTime; + composer.SeekForward(true); + }); + AddAssert("Time > lastTime", () => track.CurrentTime > lastTime); + } + + for (int i = 0; i < 20; i++) + { + AddStep("SeekBackward, Snap", () => + { + lastTime = track.CurrentTime; + composer.SeekBackward(true); + }); + AddAssert("Time < lastTime", () => track.CurrentTime < lastTime); + } + + AddAssert("Time = 0", () => track.CurrentTime == 0); + } + private void reset() { AddStep("Reset", () => composer.SeekTo(0)); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 952b553835..94851e8ef5 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Logging; +using osu.Framework.MathUtils; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Layers; @@ -196,40 +197,57 @@ namespace osu.Game.Rulesets.Edit // Todo: This should not be a constant, but feels good for now const int beat_snap_divisor = 4; - var currentTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(adjustableClock.CurrentTime); - double seekAmount = currentTimingPoint.BeatLength / beat_snap_divisor; + var cpi = beatmap.Value.Beatmap.ControlPointInfo; + var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime); + if (direction < 0 && timingPoint.Time == adjustableClock.CurrentTime) + { + // When going backwards, we care about the timing point that was _previously_ active at the current time + int activeIndex = cpi.TimingPoints.IndexOf(timingPoint); + while (activeIndex > 0 && adjustableClock.CurrentTime == timingPoint.Time) + timingPoint = cpi.TimingPoints[--activeIndex]; + } + + double seekAmount = timingPoint.BeatLength / beat_snap_divisor; double seekTime = adjustableClock.CurrentTime + seekAmount * direction; - if (!snapped) + if (!snapped || cpi.TimingPoints.Count == 0) { adjustableClock.Seek(seekTime); return; } - var nextTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > currentTimingPoint.Time); - var firstTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First(); + // We will be snapping to beats within timingPoint + seekTime -= timingPoint.Time; - if (currentTimingPoint != firstTimingPoint && seekTime < currentTimingPoint.Time) - adjustableClock.Seek(currentTimingPoint.Time - 1); // -1 to be in the prior timing point's boundary - else if (seekTime >= nextTimingPoint?.Time) - adjustableClock.Seek(nextTimingPoint.Time); // +1 to be in the next timing point's boundary + // Determine the index from timingPoint of the closest beat to seekTime, accounting for scrolling direction + int closestBeat; + if (direction > 0) + closestBeat = (int)Math.Floor(seekTime / seekAmount); else + closestBeat = (int)Math.Ceiling(seekTime / seekAmount); + + seekTime = timingPoint.Time + closestBeat * seekAmount; + + // Due to the rounding above, we may end up on the same beat. This will effectively cause 0 seeking to happen + // so we can just seek to the next beat in the direction if this is the case + if (Math.Abs(adjustableClock.CurrentTime - seekTime) == 0) { - // We will be snapping to beats within the timing point - seekTime -= currentTimingPoint.Time; - - // Determine the index from the current timing point of the closest beat to seekTime, accounting for scrolling direction - int closestBeat; if (direction > 0) - closestBeat = (int)Math.Floor(seekTime / seekAmount); + closestBeat++; else - closestBeat = (int)Math.Ceiling(seekTime / seekAmount); - - seekTime = currentTimingPoint.Time + closestBeat * seekAmount; - - adjustableClock.Seek(seekTime); + closestBeat--; + seekTime = timingPoint.Time + closestBeat * seekAmount; } + + if (seekTime < timingPoint.Time) + seekTime = timingPoint.Time; + + var nextTimingPoint = cpi.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + if (seekTime >= nextTimingPoint?.Time) + seekTime = nextTimingPoint.Time; + + adjustableClock.Seek(seekTime); } private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; From 9e0aeec574ddb234f87a0427d5ece7d11a051d6c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Mar 2018 15:53:39 +0900 Subject: [PATCH 149/537] Use a decoupled clock for accurate clock times --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 25 ++++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 94851e8ef5..8934448301 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -31,7 +31,9 @@ namespace osu.Game.Rulesets.Edit private readonly List layerContainers = new List(); private readonly Bindable beatmap = new Bindable(); - private IAdjustableClock adjustableClock; + + private IAdjustableClock sourceClock; + private DecoupleableInterpolatingFramedClock adjustableClock; protected HitObjectComposer(Ruleset ruleset) { @@ -49,8 +51,12 @@ namespace osu.Game.Rulesets.Edit rulesetContainer = CreateRulesetContainer(ruleset, beatmap.Value); // TODO: should probably be done at a RulesetContainer level to share logic with Player. - adjustableClock = (IAdjustableClock)beatmap.Value.Track ?? new StopwatchClock(); - rulesetContainer.Clock = new InterpolatingFramedClock(adjustableClock); + sourceClock = (IAdjustableClock)beatmap.Value.Track ?? new StopwatchClock(); + adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + adjustableClock.ChangeSource(sourceClock); + + rulesetContainer.Clock = adjustableClock; + } catch (Exception e) { @@ -229,14 +235,11 @@ namespace osu.Game.Rulesets.Edit seekTime = timingPoint.Time + closestBeat * seekAmount; - // Due to the rounding above, we may end up on the same beat. This will effectively cause 0 seeking to happen - // so we can just seek to the next beat in the direction if this is the case - if (Math.Abs(adjustableClock.CurrentTime - seekTime) == 0) + // Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this. + // Instead, we'll go to the next beat in the direction when this is the case + if (Precision.AlmostEquals(adjustableClock.CurrentTime, seekTime)) { - if (direction > 0) - closestBeat++; - else - closestBeat--; + closestBeat += direction > 0 ? 1 : -1; seekTime = timingPoint.Time + closestBeat * seekAmount; } @@ -244,7 +247,7 @@ namespace osu.Game.Rulesets.Edit seekTime = timingPoint.Time; var nextTimingPoint = cpi.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); - if (seekTime >= nextTimingPoint?.Time) + if (seekTime > nextTimingPoint?.Time) seekTime = nextTimingPoint.Time; adjustableClock.Seek(seekTime); From 5e742eb4663a38db9389e34d341fd0a596082e25 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Mar 2018 17:10:08 +0900 Subject: [PATCH 150/537] Move editor constructions to BDL load --- osu.Game/Screens/Edit/Editor.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index bb43099352..f1b6419741 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -26,12 +26,13 @@ namespace osu.Game.Screens.Edit public override bool ShowOverlaysOnEnter => false; - private readonly Box bottomBackground; - private readonly Container screenContainer; + private Box bottomBackground; + private Container screenContainer; private EditorScreen currentScreen; - public Editor() + [BackgroundDependencyLoader] + private void load(OsuColour colours) { EditorMenuBar menuBar; TimeInfoContainer timeInfo; @@ -130,12 +131,9 @@ namespace osu.Game.Screens.Edit timeline.Beatmap.BindTo(Beatmap); playback.Beatmap.BindTo(Beatmap); menuBar.Mode.ValueChanged += onModeChanged; - } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { bottomBackground.Colour = colours.Gray2; + } private void exportBeatmap() From c8f6a6980be01eb88b56757318ca324479f77a65 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Mar 2018 17:19:47 +0900 Subject: [PATCH 151/537] Move clock construction to Editor --- .../Edit/OsuHitObjectComposer.cs | 5 +++-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 ++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 18 +++++++----------- osu.Game/Rulesets/Ruleset.cs | 3 ++- osu.Game/Screens/Edit/Editor.cs | 10 +++++++++- .../Screens/Edit/Screens/Compose/Compose.cs | 18 +++++++++++++++--- 6 files changed, 38 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 70d49a6b4f..f8991e050b 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Layers.Selection; @@ -16,8 +17,8 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuHitObjectComposer : HitObjectComposer { - public OsuHitObjectComposer(Ruleset ruleset) - : base(ruleset) + public OsuHitObjectComposer(Ruleset ruleset, IAdjustableClock adjustableClock, IFrameBasedClock framedClock) + : base(ruleset, adjustableClock, framedClock) { } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index d407835a96..5dcc8e8a6e 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -13,6 +13,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; +using osu.Framework.Timing; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Edit; @@ -137,7 +138,7 @@ namespace osu.Game.Rulesets.Osu public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score); - public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); + public override HitObjectComposer CreateHitObjectComposer(IAdjustableClock adjustableClock, IFrameBasedClock framedClock) => new OsuHitObjectComposer(this, adjustableClock, framedClock); public override string Description => "osu!"; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 8934448301..9499e15e92 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -32,12 +32,15 @@ namespace osu.Game.Rulesets.Edit private readonly Bindable beatmap = new Bindable(); - private IAdjustableClock sourceClock; - private DecoupleableInterpolatingFramedClock adjustableClock; + private readonly IAdjustableClock adjustableClock; + private readonly IFrameBasedClock framedClock; - protected HitObjectComposer(Ruleset ruleset) + protected HitObjectComposer(Ruleset ruleset, IAdjustableClock adjustableClock, IFrameBasedClock framedClock) { this.ruleset = ruleset; + this.adjustableClock = adjustableClock; + this.framedClock = framedClock; + RelativeSizeAxes = Axes.Both; } @@ -49,14 +52,7 @@ namespace osu.Game.Rulesets.Edit try { rulesetContainer = CreateRulesetContainer(ruleset, beatmap.Value); - - // TODO: should probably be done at a RulesetContainer level to share logic with Player. - sourceClock = (IAdjustableClock)beatmap.Value.Track ?? new StopwatchClock(); - adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - adjustableClock.ChangeSource(sourceClock); - - rulesetContainer.Clock = adjustableClock; - + rulesetContainer.Clock = framedClock; } catch (Exception e) { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index cba849a491..fc0c7966c6 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Overlays.Settings; @@ -53,7 +54,7 @@ namespace osu.Game.Rulesets public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null; - public virtual HitObjectComposer CreateHitObjectComposer() => null; + public virtual HitObjectComposer CreateHitObjectComposer(IAdjustableClock adjustableClock, IFrameBasedClock framedClock) => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index f1b6419741..1a145478eb 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -12,6 +12,7 @@ using osu.Game.Screens.Edit.Menus; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Framework.Allocation; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Timing; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Edit.Screens; using osu.Game.Screens.Edit.Screens.Compose; @@ -31,9 +32,16 @@ namespace osu.Game.Screens.Edit private EditorScreen currentScreen; + private DecoupleableInterpolatingFramedClock adjustableClock; + [BackgroundDependencyLoader] private void load(OsuColour colours) { + // TODO: should probably be done at a RulesetContainer level to share logic with Player. + var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); + adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + adjustableClock.ChangeSource(sourceClock); + EditorMenuBar menuBar; TimeInfoContainer timeInfo; SummaryTimeline timeline; @@ -148,7 +156,7 @@ namespace osu.Game.Screens.Edit switch (mode) { case EditorScreenMode.Compose: - currentScreen = new Compose(); + currentScreen = new Compose(adjustableClock, adjustableClock); break; case EditorScreenMode.Design: currentScreen = new Design(); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index d42c0bfdac..b0fad58084 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -1,12 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; @@ -17,9 +19,19 @@ namespace osu.Game.Screens.Edit.Screens.Compose private const float vertical_margins = 10; private const float horizontal_margins = 20; - private readonly Container composerContainer; + private Container composerContainer; - public Compose() + private readonly IAdjustableClock adjustableClock; + private readonly IFrameBasedClock framedClock; + + public Compose(IAdjustableClock adjustableClock, IFrameBasedClock framedClock) + { + this.adjustableClock = adjustableClock; + this.framedClock = framedClock; + } + + [BackgroundDependencyLoader] + private void load() { ScrollableTimeline timeline; Children = new Drawable[] @@ -90,7 +102,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose return; } - var composer = ruleset.CreateHitObjectComposer(); + var composer = ruleset.CreateHitObjectComposer(adjustableClock, framedClock); if (composer == null) { Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); From d05947ef481f09749af41d59cb27136a7404a80a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Mar 2018 18:08:37 +0900 Subject: [PATCH 152/537] Pass adjustable clocks to components, rather than relying on the track --- .../Screens/Edit/Components/PlaybackControl.cs | 17 +++++++++++------ .../Edit/Components/TimeInfoContainer.cs | 9 +++++++-- .../Timelines/Summary/Parts/MarkerPart.cs | 11 ++++++++--- .../Timelines/Summary/SummaryTimeline.cs | 5 +++-- osu.Game/Screens/Edit/Editor.cs | 6 +++--- .../Screens/Edit/Screens/Compose/Compose.cs | 9 +-------- 6 files changed, 33 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index 05e47ef5b1..71154006ce 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; +using osu.Framework.Timing; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -19,8 +20,12 @@ namespace osu.Game.Screens.Edit.Components { private readonly IconButton playButton; - public PlaybackControl() + private readonly IAdjustableClock adjustableClock; + + public PlaybackControl(IAdjustableClock adjustableClock) { + this.adjustableClock = adjustableClock; + PlaybackTabControl tabs; Children = new Drawable[] @@ -54,22 +59,22 @@ namespace osu.Game.Screens.Edit.Components } }; - tabs.Current.ValueChanged += newValue => Track.Tempo.Value = newValue; + tabs.Current.ValueChanged += newValue => Beatmap.Value.Track.Tempo.Value = newValue; } private void togglePause() { - if (Track.IsRunning) - Track.Stop(); + if (adjustableClock.IsRunning) + adjustableClock.Stop(); else - Track.Start(); + adjustableClock.Start(); } protected override void Update() { base.Update(); - playButton.Icon = Track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; + playButton.Icon = adjustableClock.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; } private class PlaybackTabControl : OsuTabControl diff --git a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs index 9a78e6e189..6bbaad432b 100644 --- a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs +++ b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics.Sprites; using System; +using osu.Framework.Timing; namespace osu.Game.Screens.Edit.Components { @@ -13,8 +14,12 @@ namespace osu.Game.Screens.Edit.Components private readonly OsuSpriteText trackTimer; - public TimeInfoContainer() + private readonly IAdjustableClock adjustableClock; + + public TimeInfoContainer(IAdjustableClock adjustableClock) { + this.adjustableClock = adjustableClock; + Children = new Drawable[] { trackTimer = new OsuSpriteText @@ -32,7 +37,7 @@ namespace osu.Game.Screens.Edit.Components { base.Update(); - trackTimer.Text = TimeSpan.FromMilliseconds(Track.CurrentTime).ToString(@"mm\:ss\:fff"); + trackTimer.Text = TimeSpan.FromMilliseconds(adjustableClock.CurrentTime).ToString(@"mm\:ss\:fff"); } } } 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 c7f40327a9..b249713581 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -19,8 +20,12 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { private readonly Drawable marker; - public MarkerPart() + private readonly IAdjustableClock adjustableClock; + + public MarkerPart(IAdjustableClock adjustableClock) { + this.adjustableClock = adjustableClock; + Add(marker = new MarkerVisualisation()); } @@ -53,12 +58,12 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts seekTo(markerPos / DrawWidth * Beatmap.Value.Track.Length); } - private void seekTo(double time) => Beatmap.Value?.Track.Seek(time); + private void seekTo(double time) => adjustableClock.Seek(time); protected override void Update() { base.Update(); - marker.X = (float)(Beatmap.Value?.Track.CurrentTime ?? 0); + marker.X = (float)adjustableClock.CurrentTime; } protected override void LoadBeatmap(WorkingBeatmap beatmap) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index 8a472dc357..9921c24083 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -18,13 +19,13 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary { private readonly Drawable timelineBar; - public SummaryTimeline() + public SummaryTimeline(IAdjustableClock adjustableClock) { TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart; Children = new[] { - markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both }, + markerPart = new MarkerPart(adjustableClock) { RelativeSizeAxes = Axes.Both }, controlPointPart = new ControlPointPart { Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 1a145478eb..cc7f77e770 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -115,9 +115,9 @@ namespace osu.Game.Screens.Edit { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 10 }, - Child = timeInfo = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, + Child = timeInfo = new TimeInfoContainer(adjustableClock) { RelativeSizeAxes = Axes.Both }, }, - timeline = new SummaryTimeline + timeline = new SummaryTimeline(adjustableClock) { RelativeSizeAxes = Axes.Both, }, @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Edit { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 10 }, - Child = playback = new PlaybackControl { RelativeSizeAxes = Axes.Both }, + Child = playback = new PlaybackControl(adjustableClock) { RelativeSizeAxes = Axes.Both }, } }, } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index b0fad58084..9a720e1608 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; -using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Screens.Edit.Screens.Compose @@ -87,14 +86,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose }; timeline.Beatmap.BindTo(Beatmap); - Beatmap.ValueChanged += beatmapChanged; - } - private void beatmapChanged(WorkingBeatmap newBeatmap) - { - composerContainer.Clear(); - - var ruleset = newBeatmap.BeatmapInfo.Ruleset?.CreateInstance(); + var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance(); if (ruleset == null) { Logger.Log("Beatmap doesn't have a ruleset assigned."); From 8fbaa86e640de70eb86cd043ebe4e53225e88c63 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Mar 2018 18:24:57 +0900 Subject: [PATCH 153/537] Fix testcases --- .../Visual/TestCaseEditorCompose.cs | 5 +- .../Visual/TestCaseEditorSeekSnapping.cs | 131 +++++++++--------- .../Visual/TestCaseEditorSelectionLayer.cs | 5 +- .../Visual/TestCaseEditorSummaryTimeline.cs | 5 +- .../Visual/TestCasePlaybackControl.cs | 5 +- 5 files changed, 84 insertions(+), 67 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index 15bccac172..edc790e809 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -4,6 +4,7 @@ using System; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose; @@ -19,7 +20,9 @@ namespace osu.Game.Tests.Visual { random = new Random(1337); - Add(compose = new Compose()); + var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + + Add(compose = new Compose(clock, clock)); AddStep("Next beatmap", nextBeatmap); } diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index 2d60c6848d..3b4b31c92a 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -10,6 +10,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets; @@ -30,9 +31,13 @@ namespace osu.Game.Tests.Visual private Track track; private HitObjectComposer composer; + private DecoupleableInterpolatingFramedClock clock; + [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { + clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + var testBeatmap = new Beatmap { ControlPointInfo = new ControlPointInfo @@ -62,8 +67,8 @@ namespace osu.Game.Tests.Visual RelativeSizeAxes = Axes.Both, Content = new[] { - new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset()) }, - new Drawable[] { new TimingPointVisualiser(testBeatmap, track) }, + new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset(), clock, clock) }, + new Drawable[] { new TimingPointVisualiser(testBeatmap, track) { Clock = clock } }, }, RowDimensions = new[] { @@ -92,17 +97,17 @@ namespace osu.Game.Tests.Visual // Forwards AddStep("Seek(0)", () => composer.SeekTo(0)); - AddAssert("Time = 0", () => track.CurrentTime == 0); + AddAssert("Time = 0", () => clock.CurrentTime == 0); AddStep("Seek(33)", () => composer.SeekTo(33)); - AddAssert("Time = 33", () => track.CurrentTime == 33); + AddAssert("Time = 33", () => clock.CurrentTime == 33); AddStep("Seek(89)", () => composer.SeekTo(89)); - AddAssert("Time = 89", () => track.CurrentTime == 89); + AddAssert("Time = 89", () => clock.CurrentTime == 89); // Backwards AddStep("Seek(25)", () => composer.SeekTo(25)); - AddAssert("Time = 25", () => track.CurrentTime == 25); + AddAssert("Time = 25", () => clock.CurrentTime == 25); AddStep("Seek(0)", () => composer.SeekTo(0)); - AddAssert("Time = 0", () => track.CurrentTime == 0); + AddAssert("Time = 0", () => clock.CurrentTime == 0); } /// @@ -114,19 +119,19 @@ namespace osu.Game.Tests.Visual reset(); AddStep("Seek(0), Snap", () => composer.SeekTo(0, true)); - AddAssert("Time = 0", () => track.CurrentTime == 0); + AddAssert("Time = 0", () => clock.CurrentTime == 0); AddStep("Seek(50), Snap", () => composer.SeekTo(50, true)); - AddAssert("Time = 50", () => track.CurrentTime == 50); + AddAssert("Time = 50", () => clock.CurrentTime == 50); AddStep("Seek(100), Snap", () => composer.SeekTo(100, true)); - AddAssert("Time = 100", () => track.CurrentTime == 100); + AddAssert("Time = 100", () => clock.CurrentTime == 100); AddStep("Seek(175), Snap", () => composer.SeekTo(175, true)); - AddAssert("Time = 175", () => track.CurrentTime == 175); + AddAssert("Time = 175", () => clock.CurrentTime == 175); AddStep("Seek(350), Snap", () => composer.SeekTo(350, true)); - AddAssert("Time = 350", () => track.CurrentTime == 350); + AddAssert("Time = 350", () => clock.CurrentTime == 350); AddStep("Seek(400), Snap", () => composer.SeekTo(400, true)); - AddAssert("Time = 400", () => track.CurrentTime == 400); + AddAssert("Time = 400", () => clock.CurrentTime == 400); AddStep("Seek(450), Snap", () => composer.SeekTo(450, true)); - AddAssert("Time = 450", () => track.CurrentTime == 450); + AddAssert("Time = 450", () => clock.CurrentTime == 450); } /// @@ -139,17 +144,17 @@ namespace osu.Game.Tests.Visual reset(); AddStep("Seek(24), Snap", () => composer.SeekTo(24, true)); - AddAssert("Time = 0", () => track.CurrentTime == 0); + AddAssert("Time = 0", () => clock.CurrentTime == 0); AddStep("Seek(26), Snap", () => composer.SeekTo(26, true)); - AddAssert("Time = 50", () => track.CurrentTime == 50); + AddAssert("Time = 50", () => clock.CurrentTime == 50); AddStep("Seek(150), Snap", () => composer.SeekTo(150, true)); - AddAssert("Time = 100", () => track.CurrentTime == 100); + AddAssert("Time = 100", () => clock.CurrentTime == 100); AddStep("Seek(170), Snap", () => composer.SeekTo(170, true)); - AddAssert("Time = 175", () => track.CurrentTime == 175); + AddAssert("Time = 175", () => clock.CurrentTime == 175); AddStep("Seek(274), Snap", () => composer.SeekTo(274, true)); - AddAssert("Time = 175", () => track.CurrentTime == 175); + AddAssert("Time = 175", () => clock.CurrentTime == 175); AddStep("Seek(276), Snap", () => composer.SeekTo(276, true)); - AddAssert("Time = 350", () => track.CurrentTime == 350); + AddAssert("Time = 350", () => clock.CurrentTime == 350); } /// @@ -160,15 +165,15 @@ namespace osu.Game.Tests.Visual reset(); AddStep("SeekForward", () => composer.SeekForward()); - AddAssert("Time = 50", () => track.CurrentTime == 50); + AddAssert("Time = 50", () => clock.CurrentTime == 50); AddStep("SeekForward", () => composer.SeekForward()); - AddAssert("Time = 100", () => track.CurrentTime == 100); + AddAssert("Time = 100", () => clock.CurrentTime == 100); AddStep("SeekForward", () => composer.SeekForward()); - AddAssert("Time = 200", () => track.CurrentTime == 200); + AddAssert("Time = 200", () => clock.CurrentTime == 200); AddStep("SeekForward", () => composer.SeekForward()); - AddAssert("Time = 400", () => track.CurrentTime == 400); + AddAssert("Time = 400", () => clock.CurrentTime == 400); AddStep("SeekForward", () => composer.SeekForward()); - AddAssert("Time = 450", () => track.CurrentTime == 450); + AddAssert("Time = 450", () => clock.CurrentTime == 450); } /// @@ -179,17 +184,17 @@ namespace osu.Game.Tests.Visual reset(); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 50", () => track.CurrentTime == 50); + AddAssert("Time = 50", () => clock.CurrentTime == 50); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 100", () => track.CurrentTime == 100); + AddAssert("Time = 100", () => clock.CurrentTime == 100); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 175", () => track.CurrentTime == 175); + AddAssert("Time = 175", () => clock.CurrentTime == 175); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 350", () => track.CurrentTime == 350); + AddAssert("Time = 350", () => clock.CurrentTime == 350); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 400", () => track.CurrentTime == 400); + AddAssert("Time = 400", () => clock.CurrentTime == 400); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 450", () => track.CurrentTime == 450); + AddAssert("Time = 450", () => clock.CurrentTime == 450); } /// @@ -202,28 +207,28 @@ namespace osu.Game.Tests.Visual AddStep("Seek(49)", () => composer.SeekTo(49)); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 50", () => track.CurrentTime == 50); + AddAssert("Time = 50", () => clock.CurrentTime == 50); AddStep("Seek(49.999)", () => composer.SeekTo(49.999)); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 50", () => track.CurrentTime == 50); + AddAssert("Time = 50", () => clock.CurrentTime == 50); AddStep("Seek(99)", () => composer.SeekTo(99)); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 100", () => track.CurrentTime == 100); + AddAssert("Time = 100", () => clock.CurrentTime == 100); AddStep("Seek(99.999)", () => composer.SeekTo(99.999)); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 100", () => track.CurrentTime == 100); + AddAssert("Time = 100", () => clock.CurrentTime == 100); AddStep("Seek(174)", () => composer.SeekTo(174)); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 175", () => track.CurrentTime == 175); + AddAssert("Time = 175", () => clock.CurrentTime == 175); AddStep("Seek(349)", () => composer.SeekTo(349)); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 350", () => track.CurrentTime == 350); + AddAssert("Time = 350", () => clock.CurrentTime == 350); AddStep("Seek(399)", () => composer.SeekTo(399)); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 400", () => track.CurrentTime == 400); + AddAssert("Time = 400", () => clock.CurrentTime == 400); AddStep("Seek(449)", () => composer.SeekTo(449)); AddStep("SeekForward, Snap", () => composer.SeekForward(true)); - AddAssert("Time = 450", () => track.CurrentTime == 450); + AddAssert("Time = 450", () => clock.CurrentTime == 450); } /// @@ -235,17 +240,17 @@ namespace osu.Game.Tests.Visual AddStep("Seek(450)", () => composer.SeekTo(450)); AddStep("SeekBackward", () => composer.SeekBackward()); - AddAssert("Time = 425", () => track.CurrentTime == 425); + AddAssert("Time = 425", () => clock.CurrentTime == 425); AddStep("SeekBackward", () => composer.SeekBackward()); - AddAssert("Time = 375", () => track.CurrentTime == 375); + AddAssert("Time = 375", () => clock.CurrentTime == 375); AddStep("SeekBackward", () => composer.SeekBackward()); - AddAssert("Time = 325", () => track.CurrentTime == 325); + AddAssert("Time = 325", () => clock.CurrentTime == 325); AddStep("SeekBackward", () => composer.SeekBackward()); - AddAssert("Time = 125", () => track.CurrentTime == 125); + AddAssert("Time = 125", () => clock.CurrentTime == 125); AddStep("SeekBackward", () => composer.SeekBackward()); - AddAssert("Time = 25", () => track.CurrentTime == 25); + AddAssert("Time = 25", () => clock.CurrentTime == 25); AddStep("SeekBackward", () => composer.SeekBackward()); - AddAssert("Time = 0", () => track.CurrentTime == 0); + AddAssert("Time = 0", () => clock.CurrentTime == 0); } /// @@ -257,17 +262,17 @@ namespace osu.Game.Tests.Visual AddStep("Seek(450)", () => composer.SeekTo(450)); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 400", () => track.CurrentTime == 400); + AddAssert("Time = 400", () => clock.CurrentTime == 400); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 350", () => track.CurrentTime == 350); + AddAssert("Time = 350", () => clock.CurrentTime == 350); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 175", () => track.CurrentTime == 175); + AddAssert("Time = 175", () => clock.CurrentTime == 175); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 100", () => track.CurrentTime == 100); + AddAssert("Time = 100", () => clock.CurrentTime == 100); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 50", () => track.CurrentTime == 50); + AddAssert("Time = 50", () => clock.CurrentTime == 50); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 0", () => track.CurrentTime == 0); + AddAssert("Time = 0", () => clock.CurrentTime == 0); } /// @@ -280,16 +285,16 @@ namespace osu.Game.Tests.Visual AddStep("Seek(451)", () => composer.SeekTo(451)); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 450", () => track.CurrentTime == 450); + AddAssert("Time = 450", () => clock.CurrentTime == 450); AddStep("Seek(450.999)", () => composer.SeekTo(450.999)); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 450", () => track.CurrentTime == 450); + AddAssert("Time = 450", () => clock.CurrentTime == 450); AddStep("Seek(401)", () => composer.SeekTo(401)); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 400", () => track.CurrentTime == 400); + AddAssert("Time = 400", () => clock.CurrentTime == 400); AddStep("Seek(401.999)", () => composer.SeekTo(401.999)); AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); - AddAssert("Time = 400", () => track.CurrentTime == 400); + AddAssert("Time = 400", () => clock.CurrentTime == 400); } /// @@ -307,23 +312,23 @@ namespace osu.Game.Tests.Visual { AddStep("SeekForward, Snap", () => { - lastTime = track.CurrentTime; + lastTime = clock.CurrentTime; composer.SeekForward(true); }); - AddAssert("Time > lastTime", () => track.CurrentTime > lastTime); + AddAssert("Time > lastTime", () => clock.CurrentTime > lastTime); } for (int i = 0; i < 20; i++) { AddStep("SeekBackward, Snap", () => { - lastTime = track.CurrentTime; + lastTime = clock.CurrentTime; composer.SeekBackward(true); }); - AddAssert("Time < lastTime", () => track.CurrentTime < lastTime); + AddAssert("Time < lastTime", () => clock.CurrentTime < lastTime); } - AddAssert("Time = 0", () => track.CurrentTime == 0); + AddAssert("Time = 0", () => clock.CurrentTime == 0); } private void reset() @@ -333,8 +338,8 @@ namespace osu.Game.Tests.Visual private class TestHitObjectComposer : HitObjectComposer { - public TestHitObjectComposer(Ruleset ruleset) - : base(ruleset) + public TestHitObjectComposer(Ruleset ruleset, IAdjustableClock adjustableClock, IFrameBasedClock framedClock) + : base(ruleset, adjustableClock, framedClock) { } @@ -409,7 +414,7 @@ namespace osu.Game.Tests.Visual { base.Update(); - tracker.X = (float)(track.CurrentTime / track.Length); + tracker.X = (float)(Time.Current / track.Length); } private class TimingPointTimeline : CompositeDrawable diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 8d12dfc517..87ad909354 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Timing; using OpenTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; @@ -62,7 +63,9 @@ namespace osu.Game.Tests.Visual }, }); - Child = new OsuHitObjectComposer(new OsuRuleset()); + var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + + Child = new OsuHitObjectComposer(new OsuRuleset(), clock, clock); } } } diff --git a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs index 26c8814bc4..6e4b0c2a72 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs @@ -12,6 +12,7 @@ using osu.Game.Beatmaps.ControlPoints; using OpenTK; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Framework.Configuration; +using osu.Framework.Timing; namespace osu.Game.Tests.Visual { @@ -29,8 +30,10 @@ namespace osu.Game.Tests.Visual { random = new Random(1337); + var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + SummaryTimeline summaryTimeline; - Add(summaryTimeline = new SummaryTimeline + Add(summaryTimeline = new SummaryTimeline(clock) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs index 43e977ba23..37bf38bbc6 100644 --- a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs +++ b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Components; using osu.Game.Tests.Beatmaps; @@ -15,7 +16,9 @@ namespace osu.Game.Tests.Visual { public TestCasePlaybackControl() { - var playback = new PlaybackControl + var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + + var playback = new PlaybackControl(clock) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From c937ef5d247706948fab0a3c6ff9b3a9b594dd87 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Mar 2018 18:26:53 +0900 Subject: [PATCH 154/537] Fix not being able to scroll before the first timing point --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 8934448301..12529c9eac 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -243,7 +243,7 @@ namespace osu.Game.Rulesets.Edit seekTime = timingPoint.Time + closestBeat * seekAmount; } - if (seekTime < timingPoint.Time) + if (seekTime < timingPoint.Time && timingPoint != cpi.TimingPoints.First()) seekTime = timingPoint.Time; var nextTimingPoint = cpi.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); From fc15c63a886d5ab9f2c7c14784df04ea1121be7d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Mar 2018 18:28:08 +0900 Subject: [PATCH 155/537] Re-order methods --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 60 ++++++++++----------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 12529c9eac..8cb6caaa12 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -168,36 +168,6 @@ namespace osu.Game.Rulesets.Edit /// Whether to snap to the closest beat. public void SeekForward(bool snapped = false) => seek(1, snapped); - public void SeekTo(double seekTime, bool snapped = false) - { - // Todo: This should not be a constant, but feels good for now - const int beat_snap_divisor = 4; - - if (!snapped) - { - adjustableClock.Seek(seekTime); - return; - } - - var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime); - double beatSnapLength = timingPoint.BeatLength / beat_snap_divisor; - - // We will be snapping to beats within the timing point - seekTime -= timingPoint.Time; - - // Determine the index from the current timing point of the closest beat to seekTime - int closestBeat = (int)Math.Round(seekTime / beatSnapLength); - seekTime = timingPoint.Time + closestBeat * beatSnapLength; - - // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to - // the next timing point's start time - var nextTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); - if (seekTime > nextTimingPoint?.Time) - seekTime = nextTimingPoint.Time; - - adjustableClock.Seek(seekTime); - } - private void seek(int direction, bool snapped) { // Todo: This should not be a constant, but feels good for now @@ -253,6 +223,36 @@ namespace osu.Game.Rulesets.Edit adjustableClock.Seek(seekTime); } + public void SeekTo(double seekTime, bool snapped = false) + { + // Todo: This should not be a constant, but feels good for now + const int beat_snap_divisor = 4; + + if (!snapped) + { + adjustableClock.Seek(seekTime); + return; + } + + var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime); + double beatSnapLength = timingPoint.BeatLength / beat_snap_divisor; + + // We will be snapping to beats within the timing point + seekTime -= timingPoint.Time; + + // Determine the index from the current timing point of the closest beat to seekTime + int closestBeat = (int)Math.Round(seekTime / beatSnapLength); + seekTime = timingPoint.Time + closestBeat * beatSnapLength; + + // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to + // the next timing point's start time + var nextTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + if (seekTime > nextTimingPoint?.Time) + seekTime = nextTimingPoint.Time; + + adjustableClock.Seek(seekTime); + } + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); From 7ded1635fa3ca0dab91fa67b0fece002e13c1103 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Mar 2018 14:43:14 +0900 Subject: [PATCH 156/537] Update submodules --- osu-framework | 2 +- osu-resources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index d29c8365ba..cd6f6e93c9 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d29c8365ba3cf7924b57cf22341f4af55658764c +Subproject commit cd6f6e93c958e3e4e98db08dd7a443cabcf4742f diff --git a/osu-resources b/osu-resources index 92ec3d10b1..7bb0782200 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 92ec3d10b12c5e9bfc1d3b05d3db174a506efd6d +Subproject commit 7bb0782200abadf73b79ed1a3bc1d5b926c6a81e From 2ba2556d2af131d2d29e4f9ff5c6cdd7e5b65573 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Mar 2018 15:24:47 +0900 Subject: [PATCH 157/537] Update submodules --- osu-framework | 2 +- osu-resources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 727a8fb93b..41e2a0a430 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 727a8fb93b50aec18f8f83c9046243174e09de93 +Subproject commit 41e2a0a4304544fb67779c21cad1435c105982d5 diff --git a/osu-resources b/osu-resources index 92ec3d10b1..7bb0782200 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 92ec3d10b12c5e9bfc1d3b05d3db174a506efd6d +Subproject commit 7bb0782200abadf73b79ed1a3bc1d5b926c6a81e From 20acc601bb09cccd318d7c90cb7b6e271d0e609b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Mar 2018 16:09:51 +0900 Subject: [PATCH 158/537] Fix up breaking taiko changes --- .../Objects/Drawables/DrawableBarLineMajor.cs | 4 ++-- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs index 656d9bddd4..19a6e4eac2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public DrawableBarLineMajor(BarLine barLine) : base(barLine) { - InternalChild = triangleContainer = new Container + AddInternal(triangleContainer = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables EdgeSmoothness = new Vector2(1), } } - }; + }); Tracker.Alpha = 1f; } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 4097b65375..37f1300d47 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { FillMode = FillMode.Fit; - InternalChild = bodyContainer = new Container + AddInternal(bodyContainer = new Container { RelativeSizeAxes = Axes.Both, Depth = 1, @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } } - }; + }); MainPiece.Add(symbol = new SwellSymbolPiece()); } From 8d790e180d8a2e221e1a691df839d48af22a017d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Mar 2018 17:02:41 +0900 Subject: [PATCH 159/537] Fix juice stream droplets spawning in incorrect locations Closes #2149. --- .../Objects/JuiceStream.cs | 75 ++++++++++--------- .../Tests/CatchBeatmapConversionTest.cs | 2 +- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index a3e5aba2db..e4d5ae698f 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -65,54 +65,59 @@ namespace osu.Game.Rulesets.Catch.Objects X = X }); - for (var span = 0; span < this.SpanCount(); span++) + double lastDropletTime = StartTime; + + for (int span = 0; span < this.SpanCount(); span++) { var spanStartTime = StartTime + span * spanDuration; var reversed = span % 2 == 1; - for (var d = tickDistance; d <= length; d += tickDistance) + for (double d = 0; d <= length; d += tickDistance) { - if (d > length - minDistanceFromEnd) - break; - var timeProgress = d / length; var distanceProgress = reversed ? 1 - timeProgress : timeProgress; - var lastTickTime = spanStartTime + timeProgress * spanDuration; - AddNested(new Droplet + double time = spanStartTime + timeProgress * spanDuration; + + double tinyTickInterval = time - lastDropletTime; + while (tinyTickInterval > 100) + tinyTickInterval /= 2; + + for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval) { - StartTime = lastTickTime, - ComboColour = ComboColour, - X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, - Samples = new List(Samples.Select(s => new SampleInfo + double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration; + + AddNested(new TinyDroplet { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) - }); - } + StartTime = t, + ComboColour = ComboColour, + X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, + Samples = new List(Samples.Select(s => new SampleInfo + { + Bank = s.Bank, + Name = @"slidertick", + Volume = s.Volume + })) + }); + } - double tinyTickInterval = tickDistance / length * spanDuration; - while (tinyTickInterval > 100) - tinyTickInterval /= 2; - - for (double t = 0; t < spanDuration; t += tinyTickInterval) - { - double progress = reversed ? 1 - t / spanDuration : t / spanDuration; - - AddNested(new TinyDroplet + if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd) { - StartTime = spanStartTime + t, - ComboColour = ComboColour, - X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, - Samples = new List(Samples.Select(s => new SampleInfo + AddNested(new Droplet { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) - }); + StartTime = time, + ComboColour = ComboColour, + X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, + Samples = new List(Samples.Select(s => new SampleInfo + { + Bank = s.Bank, + Name = @"slidertick", + Volume = s.Volume + })) + }); + } + + lastDropletTime = time; } AddNested(new Fruit diff --git a/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs index 826c900140..e40510b71b 100644 --- a/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - [TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2149")] + [TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")] public new void Test(string name) { base.Test(name); From 6f7163769d273897f018fbeda9782f6eb1cf7439 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Mar 2018 19:57:14 +0900 Subject: [PATCH 160/537] Adjust comment for readability --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 087e733d77..36419ebd1f 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Edit var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime); if (direction < 0 && timingPoint.Time == adjustableClock.CurrentTime) { - // When going backwards, we care about the timing point that was _previously_ active at the current time + // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into int activeIndex = cpi.TimingPoints.IndexOf(timingPoint); while (activeIndex > 0 && adjustableClock.CurrentTime == timingPoint.Time) timingPoint = cpi.TimingPoints[--activeIndex]; From abb5dcf678607c7e7347f07ea37fbc4850bbdddb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Mar 2018 23:53:46 +0900 Subject: [PATCH 161/537] Fix null-refing testcase This would also be fixed with BDL loading children after we're fully loaded ;). --- .../Visual/TestCaseEditorCompose.cs | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index edc790e809..945c3c3901 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -1,51 +1,28 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Timing; -using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit.Screens.Compose; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { [TestFixture] public class TestCaseEditorCompose : OsuTestCase { - private readonly Random random; - private readonly Compose compose; - - public TestCaseEditorCompose() + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) { - random = new Random(1337); + osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - - Add(compose = new Compose(clock, clock)); - AddStep("Next beatmap", nextBeatmap); - } - - private OsuGameBase osuGame; - private BeatmapManager beatmaps; - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame, BeatmapManager beatmaps) - { - this.osuGame = osuGame; - this.beatmaps = beatmaps; - + var compose = new Compose(clock, clock); compose.Beatmap.BindTo(osuGame.Beatmap); - } - private void nextBeatmap() - { - var sets = beatmaps.GetAllUsableBeatmapSets(); - if (sets.Count == 0) - return; - - var b = sets[random.Next(0, sets.Count)].Beatmaps[0]; - osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(b); + Child = compose; } } } From 604e725f3fba86cc5d65d3a37ebc9fa61c5f8d4b Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Fri, 16 Mar 2018 20:42:05 +0300 Subject: [PATCH 162/537] Remove redundant code --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 5 +++-- osu.Game/OsuGame.cs | 4 +--- .../Overlays/Settings/Sections/Graphics/DetailSettings.cs | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index f6263a05c2..97e473a797 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -68,13 +68,14 @@ namespace osu.Game.Input.Bindings DecreaseVolume, [Description("Toggle mute")] ToggleMute, - [Description("Take screenshot")] - TakeScreenshot, // In-Game Keybindings [Description("Skip Cutscene")] SkipCutscene, [Description("Quick Retry (Hold)")] QuickRetry, + + [Description("Take screenshot")] + TakeScreenshot } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 44946dd23d..ba21dc3349 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -81,8 +81,6 @@ namespace osu.Game private Bindable configRuleset; public Bindable Ruleset = new Bindable(); - private ScreenshotManager screenshotManager; - private Bindable configSkin; private readonly string[] args; @@ -220,7 +218,7 @@ namespace osu.Game }, mainContent = new Container { RelativeSizeAxes = Axes.Both }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, - screenshotManager = new ScreenshotManager() + new ScreenshotManager() }); loadComponentSingleFile(screenStack = new Loader(), d => diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index 3f4fc96d31..fa57a85454 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -1,5 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Configuration; @@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = "Rotate cursor when dragging", Bindable = config.GetBindable(OsuSetting.CursorRotation) }, - new SettingsEnumDropdown() + new SettingsEnumDropdown { LabelText = "Screenshot format", Bindable = config.GetBindable(OsuSetting.ScreenshotFormat) From 8429408974a8d89e07279b22cb8c5007b91eb81c Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Fri, 16 Mar 2018 21:05:25 +0300 Subject: [PATCH 163/537] Change screenshot file name --- osu.Game/Graphics/ScreenshotManager.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 8e0c2cce50..f1477f6b6c 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics { host.TakeScreenshot(screenshotBitmap => { - var stream = storage.GetStream($"{DateTime.Now:yyyyMMddTHHmmss}.{screenshotFormat.ToString().ToLower()}", FileAccess.Write); + var stream = getFileStream(); switch (screenshotFormat.Value) { @@ -60,5 +60,23 @@ namespace osu.Game.Graphics } }); } + + private Stream getFileStream() + { + var fileExt = screenshotFormat.ToString().ToLower(); + + var withoutIndex = $"Screenshot.{fileExt}"; + if (!storage.Exists(withoutIndex)) + return storage.GetStream(withoutIndex, FileAccess.Write); + + for (ulong i = 1; i < ulong.MaxValue; i++) + { + var indexedName = $"Screenshot-{i}.{fileExt}"; + if (!storage.Exists(indexedName)) + return storage.GetStream(indexedName, FileAccess.Write); + } + + throw new Exception($"Failed to get stream for saving {fileExt} file"); + } } } From 245200d3eed14db452976108d28f1068cb66d85d Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Fri, 16 Mar 2018 21:25:00 +0300 Subject: [PATCH 164/537] Add simple screenshot notification --- osu.Game/Graphics/ScreenshotManager.cs | 19 +++++++++++++------ osu.Game/OsuGame.cs | 3 ++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index f1477f6b6c..ae1aebfbc1 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -9,6 +9,8 @@ using osu.Framework.Input.Bindings; using osu.Framework.Platform; using osu.Game.Configuration; using osu.Game.Input.Bindings; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; namespace osu.Game.Graphics { @@ -17,12 +19,14 @@ namespace osu.Game.Graphics private Bindable screenshotFormat; private GameHost host; private Storage storage; + private NotificationOverlay notificationOverlay; [BackgroundDependencyLoader] - private void load(GameHost host, OsuConfigManager config, Storage storage) + private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay) { this.host = host; this.storage = storage.GetStorageForDirectory(@"screenshots"); + this.notificationOverlay = notificationOverlay; screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); } @@ -45,7 +49,8 @@ namespace osu.Game.Graphics { host.TakeScreenshot(screenshotBitmap => { - var stream = getFileStream(); + var fileName = getFileName(); + var stream = storage.GetStream(fileName, FileAccess.Write); switch (screenshotFormat.Value) { @@ -58,25 +63,27 @@ namespace osu.Game.Graphics default: throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); } + + notificationOverlay.Post(new SimpleNotification { Text = $"{fileName} saved" }); }); } - private Stream getFileStream() + private string getFileName() { var fileExt = screenshotFormat.ToString().ToLower(); var withoutIndex = $"Screenshot.{fileExt}"; if (!storage.Exists(withoutIndex)) - return storage.GetStream(withoutIndex, FileAccess.Write); + return withoutIndex; for (ulong i = 1; i < ulong.MaxValue; i++) { var indexedName = $"Screenshot-{i}.{fileExt}"; if (!storage.Exists(indexedName)) - return storage.GetStream(indexedName, FileAccess.Write); + return indexedName; } - throw new Exception($"Failed to get stream for saving {fileExt} file"); + throw new Exception($"Failed to find suitable file name for saving {fileExt} image"); } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ba21dc3349..0aa915ed3f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -218,7 +218,6 @@ namespace osu.Game }, mainContent = new Container { RelativeSizeAxes = Axes.Both }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, - new ScreenshotManager() }); loadComponentSingleFile(screenStack = new Loader(), d => @@ -286,6 +285,8 @@ namespace osu.Game dependencies.Cache(notifications); dependencies.Cache(dialogOverlay); + Add(new ScreenshotManager()); + // ensure only one of these overlays are open at once. var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; foreach (var overlay in singleDisplayOverlays) From e25d1f69826b62a3765521ab19bf822ad14dd14f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 16:27:52 +0900 Subject: [PATCH 165/537] Pass down editor clocks through DI --- .../Edit/OsuHitObjectComposer.cs | 5 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 +- .../Visual/TestCaseEditorCompose.cs | 10 ++- .../Visual/TestCaseEditorSeekSnapping.cs | 13 +++- .../Visual/TestCaseEditorSelectionLayer.cs | 9 ++- .../Visual/TestCaseEditorSummaryTimeline.cs | 75 ++++--------------- .../Visual/TestCasePlaybackControl.cs | 11 ++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 11 ++- osu.Game/Rulesets/Ruleset.cs | 3 +- .../Edit/Components/PlaybackControl.cs | 7 +- .../Edit/Components/TimeInfoContainer.cs | 14 ++-- .../Timelines/Summary/SummaryTimeline.cs | 16 ++-- osu.Game/Screens/Edit/Editor.cs | 18 +++-- .../Screens/Edit/Screens/Compose/Compose.cs | 12 +-- 14 files changed, 91 insertions(+), 116 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 77f48d704e..026c85d909 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; @@ -18,8 +17,8 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuHitObjectComposer : HitObjectComposer { - public OsuHitObjectComposer(Ruleset ruleset, IAdjustableClock adjustableClock, IFrameBasedClock framedClock) - : base(ruleset, adjustableClock, framedClock) + public OsuHitObjectComposer(Ruleset ruleset) + : base(ruleset) { } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5dcc8e8a6e..d407835a96 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -13,7 +13,6 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; -using osu.Framework.Timing; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Edit; @@ -138,7 +137,7 @@ namespace osu.Game.Rulesets.Osu public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score); - public override HitObjectComposer CreateHitObjectComposer(IAdjustableClock adjustableClock, IFrameBasedClock framedClock) => new OsuHitObjectComposer(this, adjustableClock, framedClock); + public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); public override string Description => "osu!"; diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index 945c3c3901..5fd0f96f4a 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -13,13 +13,21 @@ namespace osu.Game.Tests.Visual [TestFixture] public class TestCaseEditorCompose : OsuTestCase { + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - var compose = new Compose(clock, clock); + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); + + var compose = new Compose(); compose.Beatmap.BindTo(osuGame.Beatmap); Child = compose; diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index 3b4b31c92a..bfdb39dd5e 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -33,10 +33,17 @@ namespace osu.Game.Tests.Visual private DecoupleableInterpolatingFramedClock clock; + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); var testBeatmap = new Beatmap { @@ -67,7 +74,7 @@ namespace osu.Game.Tests.Visual RelativeSizeAxes = Axes.Both, Content = new[] { - new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset(), clock, clock) }, + new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset()) }, new Drawable[] { new TimingPointVisualiser(testBeatmap, track) { Clock = clock } }, }, RowDimensions = new[] @@ -338,8 +345,8 @@ namespace osu.Game.Tests.Visual private class TestHitObjectComposer : HitObjectComposer { - public TestHitObjectComposer(Ruleset ruleset, IAdjustableClock adjustableClock, IFrameBasedClock framedClock) - : base(ruleset, adjustableClock, framedClock) + public TestHitObjectComposer(Ruleset ruleset) + : base(ruleset) { } diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 79c722be40..bbbfef477a 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -35,6 +35,11 @@ namespace osu.Game.Tests.Visual typeof(SliderCircleMask) }; + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { @@ -61,8 +66,10 @@ namespace osu.Game.Tests.Visual }); var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); - Child = new OsuHitObjectComposer(new OsuRuleset(), clock, clock); + Child = new OsuHitObjectComposer(new OsuRuleset()); } } } diff --git a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs index 6e4b0c2a72..bbe2956c5d 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs @@ -4,36 +4,41 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.Audio.Track; -using osu.Framework.Graphics.Textures; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using OpenTK; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Framework.Configuration; using osu.Framework.Timing; +using osu.Game.Rulesets.Osu; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { [TestFixture] public class TestCaseEditorSummaryTimeline : OsuTestCase { - private const int length = 60000; - private readonly Random random; - public override IReadOnlyList RequiredTypes => new[] { typeof(SummaryTimeline) }; private readonly Bindable beatmap = new Bindable(); - public TestCaseEditorSummaryTimeline() + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + + [BackgroundDependencyLoader] + private void load() { - random = new Random(1337); + beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); SummaryTimeline summaryTimeline; - Add(summaryTimeline = new SummaryTimeline(clock) + Add(summaryTimeline = new SummaryTimeline { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -41,58 +46,6 @@ namespace osu.Game.Tests.Visual }); summaryTimeline.Beatmap.BindTo(beatmap); - - AddStep("New beatmap", newBeatmap); - - newBeatmap(); - } - - private void newBeatmap() - { - var b = new Beatmap(); - - for (int i = 0; i < random.Next(1, 10); i++) - b.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = random.Next(0, length) }); - - for (int i = 0; i < random.Next(1, 5); i++) - b.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = random.Next(0, length) }); - - for (int i = 0; i < random.Next(1, 5); i++) - b.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = random.Next(0, length) }); - - for (int i = 0; i < random.Next(1, 5); i++) - b.ControlPointInfo.SamplePoints.Add(new SampleControlPoint { Time = random.Next(0, length) }); - - b.BeatmapInfo.Bookmarks = new int[random.Next(10, 30)]; - for (int i = 0; i < b.BeatmapInfo.Bookmarks.Length; i++) - b.BeatmapInfo.Bookmarks[i] = random.Next(0, length); - - beatmap.Value = new TestWorkingBeatmap(b); - } - - private class TestWorkingBeatmap : WorkingBeatmap - { - private readonly Beatmap beatmap; - - public TestWorkingBeatmap(Beatmap beatmap) - : base(beatmap.BeatmapInfo) - { - this.beatmap = beatmap; - } - - protected override Texture GetBackground() => null; - - protected override Beatmap GetBeatmap() => beatmap; - - protected override Track GetTrack() => new TestTrack(); - - private class TestTrack : TrackVirtual - { - public TestTrack() - { - Length = length; - } - } } } } diff --git a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs index 37bf38bbc6..33a801e98f 100644 --- a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs +++ b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -14,16 +15,24 @@ namespace osu.Game.Tests.Visual [TestFixture] public class TestCasePlaybackControl : OsuTestCase { + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + public TestCasePlaybackControl() { var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); - var playback = new PlaybackControl(clock) + var playback = new PlaybackControl { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(200,100) }; + playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap()); Add(playback); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c2a286c6dd..ae1c8af1a4 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -32,21 +32,20 @@ namespace osu.Game.Rulesets.Edit private readonly Bindable beatmap = new Bindable(); - private readonly IAdjustableClock adjustableClock; - private readonly IFrameBasedClock framedClock; + private IAdjustableClock adjustableClock; - protected HitObjectComposer(Ruleset ruleset, IAdjustableClock adjustableClock, IFrameBasedClock framedClock) + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; - this.adjustableClock = adjustableClock; - this.framedClock = framedClock; RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) + private void load(OsuGameBase osuGame, IAdjustableClock adjustableClock, IFrameBasedClock framedClock) { + this.adjustableClock = adjustableClock; + beatmap.BindTo(osuGame.Beatmap); try diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index fc0c7966c6..cba849a491 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Overlays.Settings; @@ -54,7 +53,7 @@ namespace osu.Game.Rulesets public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null; - public virtual HitObjectComposer CreateHitObjectComposer(IAdjustableClock adjustableClock, IFrameBasedClock framedClock) => null; + public virtual HitObjectComposer CreateHitObjectComposer() => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index 71154006ce..fe2549d300 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -18,11 +18,12 @@ namespace osu.Game.Screens.Edit.Components { public class PlaybackControl : BottomBarContainer { - private readonly IconButton playButton; + private IconButton playButton; - private readonly IAdjustableClock adjustableClock; + private IAdjustableClock adjustableClock; - public PlaybackControl(IAdjustableClock adjustableClock) + [BackgroundDependencyLoader] + private void load(IAdjustableClock adjustableClock) { this.adjustableClock = adjustableClock; diff --git a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs index 6bbaad432b..5a3b6c652b 100644 --- a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs +++ b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs @@ -4,21 +4,19 @@ using osu.Framework.Graphics; using osu.Game.Graphics.Sprites; using System; +using osu.Framework.Allocation; using osu.Framework.Timing; namespace osu.Game.Screens.Edit.Components { public class TimeInfoContainer : BottomBarContainer { - private const int count_duration = 150; - private readonly OsuSpriteText trackTimer; - private readonly IAdjustableClock adjustableClock; + private IAdjustableClock adjustableClock; - public TimeInfoContainer(IAdjustableClock adjustableClock) + public TimeInfoContainer() { - this.adjustableClock = adjustableClock; Children = new Drawable[] { @@ -33,6 +31,12 @@ namespace osu.Game.Screens.Edit.Components }; } + [BackgroundDependencyLoader] + private void load(IAdjustableClock adjustableClock) + { + this.adjustableClock = adjustableClock; + } + protected override void Update() { base.Update(); diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index 9921c24083..0e80c13257 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -17,13 +17,12 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary /// public class SummaryTimeline : BottomBarContainer { - private readonly Drawable timelineBar; - - public SummaryTimeline(IAdjustableClock adjustableClock) + [BackgroundDependencyLoader] + private void load(OsuColour colours, IAdjustableClock adjustableClock) { TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart; - Children = new[] + Children = new Drawable[] { markerPart = new MarkerPart(adjustableClock) { RelativeSizeAxes = Axes.Both }, controlPointPart = new ControlPointPart @@ -40,9 +39,10 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary RelativeSizeAxes = Axes.Both, Height = 0.35f }, - timelineBar = new Container + new Container { RelativeSizeAxes = Axes.Both, + Colour = colours.Gray5, Children = new Drawable[] { new Circle @@ -81,11 +81,5 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary bookmarkPart.Beatmap.BindTo(Beatmap); breakPart.Beatmap.BindTo(Beatmap); } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - timelineBar.Colour = colours.Gray5; - } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index cc7f77e770..8b651000fd 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -32,16 +32,22 @@ namespace osu.Game.Screens.Edit private EditorScreen currentScreen; - private DecoupleableInterpolatingFramedClock adjustableClock; + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); [BackgroundDependencyLoader] private void load(OsuColour colours) { // TODO: should probably be done at a RulesetContainer level to share logic with Player. var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); - adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + var adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; adjustableClock.ChangeSource(sourceClock); + dependencies.CacheAs(adjustableClock); + dependencies.CacheAs(adjustableClock); + EditorMenuBar menuBar; TimeInfoContainer timeInfo; SummaryTimeline timeline; @@ -115,9 +121,9 @@ namespace osu.Game.Screens.Edit { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 10 }, - Child = timeInfo = new TimeInfoContainer(adjustableClock) { RelativeSizeAxes = Axes.Both }, + Child = timeInfo = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, }, - timeline = new SummaryTimeline(adjustableClock) + timeline = new SummaryTimeline { RelativeSizeAxes = Axes.Both, }, @@ -125,7 +131,7 @@ namespace osu.Game.Screens.Edit { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 10 }, - Child = playback = new PlaybackControl(adjustableClock) { RelativeSizeAxes = Axes.Both }, + Child = playback = new PlaybackControl { RelativeSizeAxes = Axes.Both }, } }, } @@ -156,7 +162,7 @@ namespace osu.Game.Screens.Edit switch (mode) { case EditorScreenMode.Compose: - currentScreen = new Compose(adjustableClock, adjustableClock); + currentScreen = new Compose(); break; case EditorScreenMode.Design: currentScreen = new Design(); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 9a720e1608..861a08fb07 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; -using osu.Framework.Timing; using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Screens.Edit.Screens.Compose @@ -20,15 +19,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose private Container composerContainer; - private readonly IAdjustableClock adjustableClock; - private readonly IFrameBasedClock framedClock; - - public Compose(IAdjustableClock adjustableClock, IFrameBasedClock framedClock) - { - this.adjustableClock = adjustableClock; - this.framedClock = framedClock; - } - [BackgroundDependencyLoader] private void load() { @@ -95,7 +85,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose return; } - var composer = ruleset.CreateHitObjectComposer(adjustableClock, framedClock); + var composer = ruleset.CreateHitObjectComposer(); if (composer == null) { Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); From 193142e90117ebba6668f40c7d69ef8f49a458b3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 16:44:46 +0900 Subject: [PATCH 166/537] Fix missed BDL case --- osu.Game.Tests/Visual/TestCasePlaybackControl.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs index 33a801e98f..9cdb3e36e3 100644 --- a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs +++ b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs @@ -20,7 +20,8 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); - public TestCasePlaybackControl() + [BackgroundDependencyLoader] + private void load() { var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; dependencies.CacheAs(clock); @@ -35,7 +36,7 @@ namespace osu.Game.Tests.Visual playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap()); - Add(playback); + Child = playback; } } } From 8d4c9eda489fb5791a2a4b931f7850a57d134108 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 18:00:11 +0900 Subject: [PATCH 167/537] Fix attempting to add selection boxes with no selection E.g. because DrawableSpinner doesn't (currently) create an overlay, SelectionBox was being constructed with 0 hitobjects and then calculating a non-finite size for itself. --- .../Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 63b5538ad7..46b09e2c23 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -51,7 +51,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private SelectionBox currentSelectionBox; - public void AddSelectionOverlay() => AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer)); + public void AddSelectionOverlay() + { + if (overlayContainer.Count > 0) + AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer)); + } public void RemoveSelectionOverlay() { From 6b035e8c53d684cb267b20ea726a5f8d3920dbe1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 19:46:26 +0900 Subject: [PATCH 168/537] Add basic structure/layout for the beat snap visualiser --- .../Visual/TestCaseBeatSnapVisualiser.cs | 31 ++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + .../Compose/BeatSnap/BeatSnapVisualiser.cs | 136 ++++++++++++++++++ .../Edit/Screens/Compose/BeatSnap/Tick.cs | 33 +++++ .../Screens/Compose/BeatSnap/TickContainer.cs | 69 +++++++++ osu.Game/osu.Game.csproj | 3 + 6 files changed, 273 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/BeatSnap/Tick.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs diff --git a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs new file mode 100644 index 0000000000..687ea51407 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Screens.Edit.Screens.Compose.BeatSnap; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseBeatSnapVisualiser : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(BeatSnapVisualiser), + typeof(Tick), + typeof(TickContainer) + }; + + [BackgroundDependencyLoader] + private void load() + { + Child = new BeatSnapVisualiser + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 80efb0672e..c244ab8050 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -116,6 +116,7 @@ + diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs new file mode 100644 index 0000000000..75a2391688 --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs @@ -0,0 +1,136 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap +{ + public class BeatSnapVisualiser : CompositeDrawable + { + public readonly Bindable Divisor = new Bindable(1); + + private TickContainer tickContainer; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Size = new Vector2(100, 110); + Masking = true; + CornerRadius = 5; + + InternalChildren = new Drawable[] + { + new Box + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + tickContainer = new TickContainer(1, 2, 3, 4, 6, 8, 12, 16) + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 5 } + } + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray4 + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 5 }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new DivisorButton + { + Icon = FontAwesome.fa_chevron_left, + }, + null, + new DivisorButton + { + Icon = FontAwesome.fa_chevron_right, + } + }, + new Drawable[] + { + null, + new TextFlowContainer(s => s.TextSize = 12) + { + Text = "beat snap divisor", + RelativeSizeAxes = Axes.X, + TextAnchor = Anchor.TopCentre + }, + }, + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 20) + } + } + } + } + } + }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 35), + } + } + }; + + tickContainer.Divisor.BindTo(Divisor); + } + + private class DivisorButton : IconButton + { + public DivisorButton() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + ButtonSize = new Vector2(20); + IconScale = new Vector2(0.7f); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IconColour = Color4.Black; + HoverColour = colours.Gray7; + FlashColour = colours.Gray9; + } + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/Tick.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/Tick.cs new file mode 100644 index 0000000000..fc0c36d787 --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/Tick.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using OpenTK; + +namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap +{ + public class Tick : Box + { + private readonly int divisor; + + public Tick(int divisor) + { + this.divisor = divisor; + + Size = new Vector2(2, 10); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (divisor >= 16) + Colour = colours.Red; + else if (divisor >= 8) + Colour = colours.Yellow; + else + Colour = colours.Gray4; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs new file mode 100644 index 0000000000..477c2c1a3b --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs @@ -0,0 +1,69 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using OpenTK; + +namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap +{ + public class TickContainer : CompositeDrawable + { + public readonly BindableInt Divisor = new BindableInt(); + + public new MarginPadding Padding { set => base.Padding = value; } + + private EquilateralTriangle marker; + + private readonly int[] availableDivisors; + private readonly float tickSpacing; + + public TickContainer(params int[] divisors) + { + availableDivisors = divisors; + tickSpacing = 1f / (availableDivisors.Length + 1); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChild = marker = new EquilateralTriangle + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.X, + Height = 7, + EdgeSmoothness = new Vector2(1), + Colour = colours.Gray4, + }; + + for (int i = 0; i < availableDivisors.Length; i++) + { + AddInternal(new Tick(availableDivisors[i]) + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopCentre, + RelativePositionAxes = Axes.X, + X = getTickPosition(i) + }); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Divisor.ValueChanged += v => updatePosition(); + updatePosition(); + } + + private void updatePosition() => marker.X = getTickPosition(Array.IndexOf(availableDivisors, Divisor.Value)); + + private float getTickPosition(int index) => (index + 1) * tickSpacing; + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 091ec3f7ac..5c19014593 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -380,6 +380,9 @@ + + + From 66e4e45882029d8304e719f87bf389f3a3bc10db Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 19:49:46 +0900 Subject: [PATCH 169/537] Offset test case a little bit for dynamic compilation message --- osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs index 687ea51407..11c279ba8d 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs @@ -24,7 +24,8 @@ namespace osu.Game.Tests.Visual Child = new BeatSnapVisualiser { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, + Y = -200 }; } } From 7182442b21a226c0cc0f09f4eecdd8f3cd420230 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 19:49:57 +0900 Subject: [PATCH 170/537] Add divisor text --- .../Compose/BeatSnap/BeatSnapVisualiser.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs index 75a2391688..68524944a6 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs @@ -6,6 +6,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; @@ -18,6 +19,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap public readonly Bindable Divisor = new Bindable(1); private TickContainer tickContainer; + private DivisorText text; [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -74,7 +76,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap { Icon = FontAwesome.fa_chevron_left, }, - null, + text = new DivisorText(), new DivisorButton { Icon = FontAwesome.fa_chevron_right, @@ -111,6 +113,28 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap }; tickContainer.Divisor.BindTo(Divisor); + text.Divisor.BindTo(Divisor); + } + + private class DivisorText : SpriteText + { + public readonly Bindable Divisor = new Bindable(); + + public DivisorText() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Divisor.ValueChanged += v => updateText(); + updateText(); + } + + private void updateText() => Text = $"1/{Divisor.Value}"; } private class DivisorButton : IconButton From 070db6315753a3e01971b6c1f1f08da41173befb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 19:52:17 +0900 Subject: [PATCH 171/537] Privatise tick to TickContainer --- .../Visual/TestCaseBeatSnapVisualiser.cs | 1 - .../Edit/Screens/Compose/BeatSnap/Tick.cs | 33 ------------------- .../Screens/Compose/BeatSnap/TickContainer.cs | 23 +++++++++++++ osu.Game/osu.Game.csproj | 1 - 4 files changed, 23 insertions(+), 35 deletions(-) delete mode 100644 osu.Game/Screens/Edit/Screens/Compose/BeatSnap/Tick.cs diff --git a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs index 11c279ba8d..5ccad31f7d 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs @@ -14,7 +14,6 @@ namespace osu.Game.Tests.Visual public override IReadOnlyList RequiredTypes => new[] { typeof(BeatSnapVisualiser), - typeof(Tick), typeof(TickContainer) }; diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/Tick.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/Tick.cs deleted file mode 100644 index fc0c36d787..0000000000 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/Tick.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using OpenTK; - -namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap -{ - public class Tick : Box - { - private readonly int divisor; - - public Tick(int divisor) - { - this.divisor = divisor; - - Size = new Vector2(2, 10); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (divisor >= 16) - Colour = colours.Red; - else if (divisor >= 8) - Colour = colours.Yellow; - else - Colour = colours.Gray4; - } - } -} diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs index 477c2c1a3b..b57fc690a8 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs @@ -65,5 +65,28 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap private void updatePosition() => marker.X = getTickPosition(Array.IndexOf(availableDivisors, Divisor.Value)); private float getTickPosition(int index) => (index + 1) * tickSpacing; + + private class Tick : Box + { + private readonly int divisor; + + public Tick(int divisor) + { + this.divisor = divisor; + + Size = new Vector2(2, 10); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (divisor >= 16) + Colour = colours.Red; + else if (divisor >= 8) + Colour = colours.Yellow; + else + Colour = colours.Gray4; + } + } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5c19014593..744ca7bcf2 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -381,7 +381,6 @@ - From fbc92bfa01048be2c04e0feb0d265af0fa43ed0f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 19:55:49 +0900 Subject: [PATCH 172/537] Add previous/next divisor button actions --- .../Compose/BeatSnap/BeatSnapVisualiser.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs index 68524944a6..f7bc6da98b 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs @@ -16,7 +16,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap { public class BeatSnapVisualiser : CompositeDrawable { + private static readonly int[] available_divisors = { 1, 2, 3, 4, 6, 8, 12, 16 }; + public readonly Bindable Divisor = new Bindable(1); + private int currentDivisorIndex = 0; private TickContainer tickContainer; private DivisorText text; @@ -75,11 +78,13 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap new DivisorButton { Icon = FontAwesome.fa_chevron_left, + Action = selectPrevious }, text = new DivisorText(), new DivisorButton { Icon = FontAwesome.fa_chevron_right, + Action = selectNext } }, new Drawable[] @@ -116,6 +121,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap text.Divisor.BindTo(Divisor); } + private void selectPrevious() + { + if (currentDivisorIndex == 0) + return; + Divisor.Value = available_divisors[--currentDivisorIndex]; + } + + private void selectNext() + { + if (currentDivisorIndex == available_divisors.Length - 1) + return; + Divisor.Value = available_divisors[++currentDivisorIndex]; + } + private class DivisorText : SpriteText { public readonly Bindable Divisor = new Bindable(); From c86ddb7ee34168bf9c755cf79ec40b96c5cb0a48 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 19:57:02 +0900 Subject: [PATCH 173/537] Add slight easing to marker movements --- osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs index b57fc690a8..deb22742bc 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap updatePosition(); } - private void updatePosition() => marker.X = getTickPosition(Array.IndexOf(availableDivisors, Divisor.Value)); + private void updatePosition() => marker.MoveToX(getTickPosition(Array.IndexOf(availableDivisors, Divisor.Value)), 100, Easing.OutQuint); private float getTickPosition(int index) => (index + 1) * tickSpacing; From c4f5b46d72cf523a03be2996e74b46a664267d08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Mar 2018 19:50:37 +0900 Subject: [PATCH 174/537] Add basic structure for skin configurations --- osu.Game/Skinning/LegacySkinDecoder.cs | 15 +++++++++++++++ osu.Game/Skinning/SkinConfiguration.cs | 18 ++++++++++++++++++ osu.Game/osu.Game.csproj | 2 ++ 3 files changed, 35 insertions(+) create mode 100644 osu.Game/Skinning/LegacySkinDecoder.cs create mode 100644 osu.Game/Skinning/SkinConfiguration.cs diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs new file mode 100644 index 0000000000..a867e08495 --- /dev/null +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps.Formats; + +namespace osu.Game.Skinning +{ + public class LegacySkinDecoder : LegacyDecoder + { + public LegacySkinDecoder(int version) + : base(version) + { + } + } +} diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs new file mode 100644 index 0000000000..eac77ae753 --- /dev/null +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps.Formats; +using OpenTK.Graphics; + +namespace osu.Game.Skinning +{ + public class SkinConfiguration : IHasComboColours, IHasCustomColours + { + public readonly SkinInfo SkinInfo = new SkinInfo(); + + public List ComboColours { get; set; } = new List(); + + public Dictionary CustomColours { get; set; } = new Dictionary(); + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 091ec3f7ac..b325e52ed1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -873,7 +873,9 @@ + + From 62e908e22c45939f79901014e15c88b9cfff3be7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 18:41:48 +0900 Subject: [PATCH 175/537] Add default separator character --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 8 ++++---- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 1bb67f9e75..74b7d0272e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -96,7 +96,7 @@ namespace osu.Game.Beatmaps.Formats private void handleGeneral(string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); var metadata = beatmap.BeatmapInfo.Metadata; switch (pair.Key) @@ -155,7 +155,7 @@ namespace osu.Game.Beatmaps.Formats private void handleEditor(string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); switch (pair.Key) { @@ -179,7 +179,7 @@ namespace osu.Game.Beatmaps.Formats private void handleMetadata(string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); var metadata = beatmap.BeatmapInfo.Metadata; switch (pair.Key) @@ -220,7 +220,7 @@ namespace osu.Game.Beatmaps.Formats private void handleDifficulty(string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; switch (pair.Key) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index e4aa9f5091..67d497ba83 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps.Formats private void handleColours(T output, string line) { - var pair = SplitKeyVal(line, ':'); + var pair = SplitKeyVal(line); bool isCombo = pair.Key.StartsWith(@"Combo"); @@ -89,7 +89,7 @@ namespace osu.Game.Beatmaps.Formats } } - protected KeyValuePair SplitKeyVal(string line, char separator) + protected KeyValuePair SplitKeyVal(string line, char separator = ':') { var split = line.Trim().Split(new[] { separator }, 2); From 8e52d91180b66c13833247504363169de96b8d1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 18:42:13 +0900 Subject: [PATCH 176/537] Handle missing files without hard failure Also adds support for lookups with file extensions --- osu.Game/Skinning/LegacySkin.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5525cc483e..0e508e4527 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -60,10 +60,12 @@ namespace osu.Game.Skinning private string getPathForFile(string filename) { + bool hasExtension = filename.Contains('.'); + string lastPiece = filename.Split('/').Last(); var file = skin.Files.FirstOrDefault(f => - string.Equals(Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase)); + string.Equals(hasExtension ? f.Filename : Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase)); return file?.FileInfo.StoragePath; } @@ -73,9 +75,17 @@ namespace osu.Game.Skinning this.underlyingStore = underlyingStore; } - public Stream GetStream(string name) => underlyingStore.GetStream(getPathForFile(name)); + public Stream GetStream(string name) + { + string path = getPathForFile(name); + return path == null ? null : underlyingStore.GetStream(path); + } - byte[] IResourceStore.Get(string name) => underlyingStore.Get(getPathForFile(name)); + byte[] IResourceStore.Get(string name) + { + string path = getPathForFile(name); + return path == null ? null : underlyingStore.Get(path); + } } } } From 397b06283ac1deac1d212f61a97024c42881227e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Mar 2018 20:05:31 +0900 Subject: [PATCH 177/537] Add basic skin configuration decoding support --- osu.Game/Skinning/LegacySkin.cs | 5 +++++ osu.Game/Skinning/LegacySkinDecoder.cs | 27 ++++++++++++++++++++++++-- osu.Game/Skinning/Skin.cs | 2 ++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 0e508e4527..27f15474ba 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -25,6 +25,11 @@ namespace osu.Game.Skinning storage = new LegacySkinResourceStore(skin, storage); samples = audioManager.GetSampleManager(storage); textures = new TextureStore(new RawTextureLoaderStore(storage)); + + var decoder = new LegacySkinDecoder(); + + using (StreamReader reader = new StreamReader(storage.GetStream("skin.ini"))) + Configuration = decoder.Decode(reader); } public override Drawable GetDrawableComponent(string componentName) diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index a867e08495..9a881f9241 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -7,9 +7,32 @@ namespace osu.Game.Skinning { public class LegacySkinDecoder : LegacyDecoder { - public LegacySkinDecoder(int version) - : base(version) + public LegacySkinDecoder() + : base(1) { } + + protected override void ParseLine(SkinConfiguration output, Section section, string line) + { + switch (section) + { + case Section.General: + var pair = SplitKeyVal(line); + + switch (pair.Key) + { + case @"Name": + output.SkinInfo.Name = pair.Value; + break; + case @"Author": + output.SkinInfo.Creator = pair.Value; + break; + } + + return; + } + + base.ParseLine(output, section, line); + } } } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index fafbdec8f0..7b4e894dfd 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -10,6 +10,8 @@ namespace osu.Game.Skinning { public readonly SkinInfo SkinInfo; + public virtual SkinConfiguration Configuration { get; protected set; } + public abstract Drawable GetDrawableComponent(string componentName); public abstract SampleChannel GetSample(string sampleName); From ec851648da6df51ce9d31622d00d9c0bbc9ceb57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 19:09:30 +0900 Subject: [PATCH 178/537] Add better ToString output from SkinInfo --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 +- osu.Game/Skinning/SkinInfo.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index bc0b8b4aaa..5df5304751 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.Name, s.ID)); + void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); skins.ItemAdded += _ => reloadSkins(); skins.ItemRemoved += _ => reloadSkins(); diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 45c8b97f63..5080b65a37 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -24,5 +24,7 @@ namespace osu.Game.Skinning public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" }; public bool Equals(SkinInfo other) => other != null && ID == other.ID; + + public override string ToString() => $"\"{Name}\" by {Creator}"; } } From 7272ba2f1b5429a9fffdb0999d9c4324922b47ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 19:35:43 +0900 Subject: [PATCH 179/537] Add migration for skins which didn't get a proper name assigned Also correctly imports new skins --- osu.Game/Skinning/SkinManager.cs | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 88d51eca10..fa65b923fb 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -39,6 +39,31 @@ namespace osu.Game.Skinning Name = archive.Name }; + protected override void Populate(SkinInfo model, ArchiveReader archive) + { + base.Populate(model, archive); + populate(model); + } + + /// + /// Populate a from its (if possible). + /// + /// + private void populate(SkinInfo model) + { + Skin reference = GetSkin(model); + if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name)) + { + model.Name = reference.Configuration.SkinInfo.Name; + model.Creator = reference.Configuration.SkinInfo.Creator; + } + else + { + model.Name = model.Name.Replace(".osk", ""); + model.Creator = "Unknown"; + } + } + /// /// Retrieve a instance for the provided /// @@ -65,6 +90,16 @@ namespace osu.Game.Skinning if (skin.SkinInfo != CurrentSkinInfo.Value) throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead."); }; + + // migrate older imports which didn't have access to skin.ini + using (ContextFactory.GetForWrite()) + { + foreach (var skinInfo in ModelStore.ConsumableItems.Where(s => s.Name.EndsWith(".osk"))) + { + populate(skinInfo); + Update(skinInfo); + } + } } /// From e96dad441d79b18fb2ce69f32b58aa263e2e703c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:14:13 +0900 Subject: [PATCH 180/537] Offset the icon buttons a little bit to look a bit more centered Maybe it's just me, but this is really triggering me. --- .../Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs index f7bc6da98b..72bd1d3620 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs @@ -163,6 +163,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap Anchor = Anchor.Centre; Origin = Anchor.Centre; + // Small offset to look a bit better centered along with the divisor text + Y = 1; + ButtonSize = new Vector2(20); IconScale = new Vector2(0.7f); } From 32fecc6ff4930b4f1f8cd6dfa9cd725aa80f0032 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:14:34 +0900 Subject: [PATCH 181/537] Adjust icon scale + text --- .../Screens/Compose/BeatSnap/BeatSnapVisualiser.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs index 72bd1d3620..076fa926eb 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap private static readonly int[] available_divisors = { 1, 2, 3, 4, 6, 8, 12, 16 }; public readonly Bindable Divisor = new Bindable(1); - private int currentDivisorIndex = 0; + private int currentDivisorIndex; private TickContainer tickContainer; private DivisorText text; @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap new Drawable[] { null, - new TextFlowContainer(s => s.TextSize = 12) + new TextFlowContainer(s => s.TextSize = 10) { Text = "beat snap divisor", RelativeSizeAxes = Axes.X, @@ -145,6 +145,12 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap Origin = Anchor.Centre; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.BlueLighter; + } + protected override void LoadComplete() { base.LoadComplete(); @@ -167,7 +173,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap Y = 1; ButtonSize = new Vector2(20); - IconScale = new Vector2(0.7f); + IconScale = new Vector2(0.6f); } [BackgroundDependencyLoader] From b25c564ecb5134b633d36143f8100ff6496227a9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:14:46 +0900 Subject: [PATCH 182/537] Integrate into editor's compose screen --- .../Visual/TestCaseBeatSnapVisualiser.cs | 4 ++- .../Visual/TestCaseEditorCompose.cs | 3 +++ .../Compose/BeatSnap/BeatSnapVisualiser.cs | 1 - .../Screens/Edit/Screens/Compose/Compose.cs | 26 ++++++++++++++----- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs index 5ccad31f7d..632c54ecfd 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Screens.Compose.BeatSnap; +using OpenTK; namespace osu.Game.Tests.Visual { @@ -24,7 +25,8 @@ namespace osu.Game.Tests.Visual { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Y = -200 + Y = -200, + Size = new Vector2(100, 110) }; } } diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index 15bccac172..221b40e2ba 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; @@ -12,6 +13,8 @@ namespace osu.Game.Tests.Visual [TestFixture] public class TestCaseEditorCompose : OsuTestCase { + public override IReadOnlyList RequiredTypes => new[] { typeof(Compose) }; + private readonly Random random; private readonly Compose compose; diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs index 076fa926eb..696d491c21 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs @@ -27,7 +27,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap [BackgroundDependencyLoader] private void load(OsuColour colours) { - Size = new Vector2(100, 110); Masking = true; CornerRadius = 5; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index d42c0bfdac..a3098b7712 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Screens.Edit.Screens.Compose.BeatSnap; using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Screens.Edit.Screens.Compose @@ -47,15 +48,28 @@ namespace osu.Game.Screens.Edit.Screens.Compose Name = "Timeline content", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, - Children = new Drawable[] + Child = new GridContainer { - new Container + RelativeSizeAxes = Axes.Both, + Content = new[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 115 }, - Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 5 }, + Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } + }, + new BeatSnapVisualiser { RelativeSizeAxes = Axes.Both } + }, + }, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 90), } - } + }, } } } From 18368d2446ff66e6647cebf368efa3a68057e8fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Mar 2018 20:15:17 +0900 Subject: [PATCH 183/537] Make import notifications fail when any imports fail --- osu.Game/Database/ArchiveModelManager.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index a65593ff82..dac38b2405 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -88,7 +88,8 @@ namespace osu.Game.Database List imported = new List(); - int i = 0; + int success = 0; + int errors = 0; foreach (string path in paths) { if (notification.State == ProgressNotificationState.Cancelled) @@ -97,11 +98,11 @@ namespace osu.Game.Database try { - notification.Text = $"Importing ({i} of {paths.Length})\n{Path.GetFileName(path)}"; + notification.Text = $"Importing ({success} of {paths.Length})\n{Path.GetFileName(path)}"; using (ArchiveReader reader = getReaderFrom(path)) imported.Add(Import(reader)); - notification.Progress = (float)++i / paths.Length; + notification.Progress = (float)++success / paths.Length; // We may or may not want to delete the file depending on where it is stored. // e.g. reconstructing/repairing database with items from default storage. @@ -121,10 +122,11 @@ namespace osu.Game.Database { e = e.InnerException ?? e; Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); + errors++; } } - notification.State = ProgressNotificationState.Completed; + notification.State = errors == 0 ? ProgressNotificationState.Completed : ProgressNotificationState.Cancelled; } /// From 879dbc75b5e6fb8907e2ffefc53fe769adee048a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:20:29 +0900 Subject: [PATCH 184/537] Nest TickContainer in BeatSnapVisualiser --- .../Compose/BeatSnap/BeatSnapVisualiser.cs | 82 +++++++++++++++++ .../Screens/Compose/BeatSnap/TickContainer.cs | 92 ------------------- osu.Game/osu.Game.csproj | 1 - 3 files changed, 82 insertions(+), 93 deletions(-) delete mode 100644 osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs index 696d491c21..8262c06ba8 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -183,5 +184,86 @@ namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap FlashColour = colours.Gray9; } } + + private class TickContainer : CompositeDrawable + { + public readonly BindableInt Divisor = new BindableInt(); + + public new MarginPadding Padding + { + set => base.Padding = value; + } + + private EquilateralTriangle marker; + + private readonly int[] availableDivisors; + private readonly float tickSpacing; + + public TickContainer(params int[] divisors) + { + availableDivisors = divisors; + tickSpacing = 1f / (availableDivisors.Length + 1); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChild = marker = new EquilateralTriangle + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.X, + Height = 7, + EdgeSmoothness = new Vector2(1), + Colour = colours.Gray4, + }; + + for (int i = 0; i < availableDivisors.Length; i++) + { + AddInternal(new Tick(availableDivisors[i]) + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopCentre, + RelativePositionAxes = Axes.X, + X = getTickPosition(i) + }); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Divisor.ValueChanged += v => updatePosition(); + updatePosition(); + } + + private void updatePosition() => marker.MoveToX(getTickPosition(Array.IndexOf(availableDivisors, Divisor.Value)), 100, Easing.OutQuint); + + private float getTickPosition(int index) => (index + 1) * tickSpacing; + + private class Tick : Box + { + private readonly int divisor; + + public Tick(int divisor) + { + this.divisor = divisor; + + Size = new Vector2(2, 10); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (divisor >= 16) + Colour = colours.Red; + else if (divisor >= 8) + Colour = colours.Yellow; + else + Colour = colours.Gray4; + } + } + } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs deleted file mode 100644 index deb22742bc..0000000000 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/TickContainer.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using OpenTK; - -namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap -{ - public class TickContainer : CompositeDrawable - { - public readonly BindableInt Divisor = new BindableInt(); - - public new MarginPadding Padding { set => base.Padding = value; } - - private EquilateralTriangle marker; - - private readonly int[] availableDivisors; - private readonly float tickSpacing; - - public TickContainer(params int[] divisors) - { - availableDivisors = divisors; - tickSpacing = 1f / (availableDivisors.Length + 1); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - InternalChild = marker = new EquilateralTriangle - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomCentre, - RelativePositionAxes = Axes.X, - Height = 7, - EdgeSmoothness = new Vector2(1), - Colour = colours.Gray4, - }; - - for (int i = 0; i < availableDivisors.Length; i++) - { - AddInternal(new Tick(availableDivisors[i]) - { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopCentre, - RelativePositionAxes = Axes.X, - X = getTickPosition(i) - }); - } - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Divisor.ValueChanged += v => updatePosition(); - updatePosition(); - } - - private void updatePosition() => marker.MoveToX(getTickPosition(Array.IndexOf(availableDivisors, Divisor.Value)), 100, Easing.OutQuint); - - private float getTickPosition(int index) => (index + 1) * tickSpacing; - - private class Tick : Box - { - private readonly int divisor; - - public Tick(int divisor) - { - this.divisor = divisor; - - Size = new Vector2(2, 10); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (divisor >= 16) - Colour = colours.Red; - else if (divisor >= 8) - Colour = colours.Yellow; - else - Colour = colours.Gray4; - } - } - } -} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 744ca7bcf2..75b69fad76 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -381,7 +381,6 @@ - From df0b8a24c523ef03b0a62db852c14333fadf5152 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:21:03 +0900 Subject: [PATCH 185/537] Rename + renamespace --- osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs | 5 ++--- osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 1 - .../BeatSnapVisualiser.cs => DrawableBeatDivisor.cs} | 2 +- osu.Game/osu.Game.csproj | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) rename osu.Game/Screens/Edit/Screens/Compose/{BeatSnap/BeatSnapVisualiser.cs => DrawableBeatDivisor.cs} (96%) diff --git a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs index 632c54ecfd..a17b2328ba 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Screens.Edit.Screens.Compose.BeatSnap; +using osu.Game.Screens.Edit.Screens.Compose; using OpenTK; namespace osu.Game.Tests.Visual @@ -14,8 +14,7 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { - typeof(BeatSnapVisualiser), - typeof(TickContainer) + typeof(BeatSnapVisualiser) }; [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index a3098b7712..cd4e0b9c4f 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Game.Beatmaps; -using osu.Game.Screens.Edit.Screens.Compose.BeatSnap; using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Screens.Edit.Screens.Compose diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs similarity index 96% rename from osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs rename to osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs index 8262c06ba8..5e2601ca50 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatSnap/BeatSnapVisualiser.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.UserInterface; using OpenTK; using OpenTK.Graphics; -namespace osu.Game.Screens.Edit.Screens.Compose.BeatSnap +namespace osu.Game.Screens.Edit.Screens.Compose { public class BeatSnapVisualiser : CompositeDrawable { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 75b69fad76..8bdefbfeb5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -380,7 +380,7 @@ - + From 46f10b392dfbb0f0adc1fca88222d43ce09b758e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:23:31 +0900 Subject: [PATCH 186/537] Fix merge errors --- osu.Game.Tests/Visual/TestCaseEditorCompose.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index 5cfd82e1e8..cd25bc1683 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -16,6 +16,7 @@ namespace osu.Game.Tests.Visual public class TestCaseEditorCompose : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(Compose) }; + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) From b71c123214fc56035eaeda692144427876fe64c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Mar 2018 20:26:16 +0900 Subject: [PATCH 187/537] Allow import of skins which don't have ini files --- osu.Game/Skinning/LegacySkin.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 27f15474ba..b531d791b0 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -26,10 +26,13 @@ namespace osu.Game.Skinning samples = audioManager.GetSampleManager(storage); textures = new TextureStore(new RawTextureLoaderStore(storage)); - var decoder = new LegacySkinDecoder(); + Stream stream = storage.GetStream("skin.ini"); - using (StreamReader reader = new StreamReader(storage.GetStream("skin.ini"))) - Configuration = decoder.Decode(reader); + if (stream != null) + using (StreamReader reader = new StreamReader(stream)) + Configuration = new LegacySkinDecoder().Decode(reader); + else + Configuration = new SkinConfiguration(); } public override Drawable GetDrawableComponent(string componentName) From 5ecbc5612c8a75b8a7a14b4ac807ef726cf64afe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:30:07 +0900 Subject: [PATCH 188/537] Integrate with dependency injection --- .../Visual/TestCaseBeatSnapVisualiser.cs | 7 ++++ .../Screens/Compose/BindableBeatDivisor.cs | 15 +++++++ .../Screens/Edit/Screens/Compose/Compose.cs | 9 +++++ .../Screens/Compose/DrawableBeatDivisor.cs | 40 +++++++++---------- osu.Game/osu.Game.csproj | 1 + 5 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs diff --git a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs index a17b2328ba..d72b59f5c5 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs @@ -17,9 +17,16 @@ namespace osu.Game.Tests.Visual typeof(BeatSnapVisualiser) }; + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + [BackgroundDependencyLoader] private void load() { + dependencies.Cache(new BindableBeatDivisor()); + Child = new BeatSnapVisualiser { Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs new file mode 100644 index 0000000000..eb68bce71b --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; + +namespace osu.Game.Screens.Edit.Screens.Compose +{ + public class BindableBeatDivisor : Bindable + { + public BindableBeatDivisor() + : base(1) + { + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index d32dc92ce5..c044c949e8 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -17,11 +17,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose private const float vertical_margins = 10; private const float horizontal_margins = 20; + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); + private Container composerContainer; + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + [BackgroundDependencyLoader] private void load() { + dependencies.Cache(beatDivisor); + ScrollableTimeline timeline; Children = new Drawable[] { diff --git a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs index 5e2601ca50..af028763a0 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs @@ -19,15 +19,14 @@ namespace osu.Game.Screens.Edit.Screens.Compose { private static readonly int[] available_divisors = { 1, 2, 3, 4, 6, 8, 12, 16 }; - public readonly Bindable Divisor = new Bindable(1); + private readonly Bindable beatDivisor = new Bindable(1); private int currentDivisorIndex; - private TickContainer tickContainer; - private DivisorText text; - [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, BindableBeatDivisor beatDivisor) { + this.beatDivisor.BindTo(beatDivisor); + Masking = true; CornerRadius = 5; @@ -46,7 +45,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose { new Drawable[] { - tickContainer = new TickContainer(1, 2, 3, 4, 6, 8, 12, 16) + new TickContainer(1, 2, 3, 4, 6, 8, 12, 16) { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = 5 } @@ -80,7 +79,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose Icon = FontAwesome.fa_chevron_left, Action = selectPrevious }, - text = new DivisorText(), + new DivisorText(), new DivisorButton { Icon = FontAwesome.fa_chevron_right, @@ -116,28 +115,25 @@ namespace osu.Game.Screens.Edit.Screens.Compose } } }; - - tickContainer.Divisor.BindTo(Divisor); - text.Divisor.BindTo(Divisor); } private void selectPrevious() { if (currentDivisorIndex == 0) return; - Divisor.Value = available_divisors[--currentDivisorIndex]; + beatDivisor.Value = available_divisors[--currentDivisorIndex]; } private void selectNext() { if (currentDivisorIndex == available_divisors.Length - 1) return; - Divisor.Value = available_divisors[++currentDivisorIndex]; + beatDivisor.Value = available_divisors[++currentDivisorIndex]; } private class DivisorText : SpriteText { - public readonly Bindable Divisor = new Bindable(); + private readonly Bindable beatDivisor = new Bindable(); public DivisorText() { @@ -146,8 +142,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, BindableBeatDivisor beatDivisor) { + this.beatDivisor.BindTo(beatDivisor); + Colour = colours.BlueLighter; } @@ -155,11 +153,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose { base.LoadComplete(); - Divisor.ValueChanged += v => updateText(); + beatDivisor.ValueChanged += v => updateText(); updateText(); } - private void updateText() => Text = $"1/{Divisor.Value}"; + private void updateText() => Text = $"1/{beatDivisor.Value}"; } private class DivisorButton : IconButton @@ -187,7 +185,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose private class TickContainer : CompositeDrawable { - public readonly BindableInt Divisor = new BindableInt(); + private readonly Bindable beatDivisor = new Bindable(); public new MarginPadding Padding { @@ -206,8 +204,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, BindableBeatDivisor beatDivisor) { + this.beatDivisor.BindTo(beatDivisor); + InternalChild = marker = new EquilateralTriangle { Anchor = Anchor.BottomLeft, @@ -234,11 +234,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose { base.LoadComplete(); - Divisor.ValueChanged += v => updatePosition(); + beatDivisor.ValueChanged += v => updatePosition(); updatePosition(); } - private void updatePosition() => marker.MoveToX(getTickPosition(Array.IndexOf(availableDivisors, Divisor.Value)), 100, Easing.OutQuint); + private void updatePosition() => marker.MoveToX(getTickPosition(Array.IndexOf(availableDivisors, beatDivisor.Value)), 100, Easing.OutQuint); private float getTickPosition(int index) => (index + 1) * tickSpacing; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8bdefbfeb5..42f58e9eee 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -380,6 +380,7 @@ + From c5eab7a2279caef7a695ae10ade1dba4b1303af3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:34:24 +0900 Subject: [PATCH 189/537] Actually rename to DrawableBeatDivisor... --- osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs | 4 ++-- osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 2 +- osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs index d72b59f5c5..23e343c36e 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { - typeof(BeatSnapVisualiser) + typeof(DrawableBeatDivisor) }; private DependencyContainer dependencies; @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual { dependencies.Cache(new BindableBeatDivisor()); - Child = new BeatSnapVisualiser + Child = new DrawableBeatDivisor { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index c044c949e8..41bdc25bcf 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose Padding = new MarginPadding { Right = 5 }, Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } }, - new BeatSnapVisualiser { RelativeSizeAxes = Axes.Both } + new DrawableBeatDivisor { RelativeSizeAxes = Axes.Both } }, }, ColumnDimensions = new[] diff --git a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs index af028763a0..1a4ab05254 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs @@ -15,7 +15,7 @@ using OpenTK.Graphics; namespace osu.Game.Screens.Edit.Screens.Compose { - public class BeatSnapVisualiser : CompositeDrawable + public class DrawableBeatDivisor : CompositeDrawable { private static readonly int[] available_divisors = { 1, 2, 3, 4, 6, 8, 12, 16 }; From 994c7bfabdaed12ceebc16959a3c7157c43a99a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Mar 2018 20:30:45 +0900 Subject: [PATCH 190/537] Further improvements to messaging --- osu.Game/Database/ArchiveModelManager.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index dac38b2405..bdc7c58238 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -79,7 +79,6 @@ namespace osu.Game.Database var notification = new ProgressNotification { Text = "Import is initialising...", - CompletionText = "Import successful!", Progress = 0, State = ProgressNotificationState.Active, }; @@ -88,7 +87,7 @@ namespace osu.Game.Database List imported = new List(); - int success = 0; + int current = 0; int errors = 0; foreach (string path in paths) { @@ -98,11 +97,11 @@ namespace osu.Game.Database try { - notification.Text = $"Importing ({success} of {paths.Length})\n{Path.GetFileName(path)}"; + notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}"; using (ArchiveReader reader = getReaderFrom(path)) imported.Add(Import(reader)); - notification.Progress = (float)++success / paths.Length; + notification.Progress = (float)(current - 1) / paths.Length; // We may or may not want to delete the file depending on where it is stored. // e.g. reconstructing/repairing database with items from default storage. @@ -126,7 +125,8 @@ namespace osu.Game.Database } } - notification.State = errors == 0 ? ProgressNotificationState.Completed : ProgressNotificationState.Cancelled; + notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; + notification.State = ProgressNotificationState.Completed; } /// @@ -333,7 +333,7 @@ namespace osu.Game.Database { if (ZipFile.IsZipFile(path)) return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path)); - return new LegacyFilesystemReader(path); + return new LegacyFilesystemReader(path); } } } From f565cc861d9b176d29312016ed820ce6f1b3e297 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Mar 2018 20:41:12 +0900 Subject: [PATCH 191/537] Hard bail on attempting to import an invalid archive format --- osu.Game/Database/ArchiveModelManager.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index a65593ff82..4c60db3a23 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -13,6 +13,7 @@ using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.IPC; using osu.Game.Overlays.Notifications; +using SharpCompress.Common; using FileInfo = osu.Game.IO.FileInfo; namespace osu.Game.Database @@ -331,7 +332,9 @@ namespace osu.Game.Database { if (ZipFile.IsZipFile(path)) return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path)); - return new LegacyFilesystemReader(path); + if (Directory.Exists(path)) + return new LegacyFilesystemReader(path); + throw new InvalidFormatException($"{path} is not a valid archive"); } } } From b1d09500f22a00a548c0ceecce45d8027ac67b4a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:42:06 +0900 Subject: [PATCH 192/537] Integrate beat snap divisor into editor seeking --- .../Visual/TestCaseEditorSeekSnapping.cs | 4 ++++ .../Visual/TestCaseEditorSelectionLayer.cs | 2 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 15 ++++++--------- .../Edit/Screens/Compose/BindableBeatDivisor.cs | 4 ++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index bfdb39dd5e..e9e966a826 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Tests.Beatmaps; using OpenTK; using OpenTK.Graphics; @@ -31,6 +32,8 @@ namespace osu.Game.Tests.Visual private Track track; private HitObjectComposer composer; + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(4); + private DecoupleableInterpolatingFramedClock clock; private DependencyContainer dependencies; @@ -44,6 +47,7 @@ namespace osu.Game.Tests.Visual clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; dependencies.CacheAs(clock); dependencies.CacheAs(clock); + dependencies.Cache(beatDivisor); var testBeatmap = new Beatmap { diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index bbbfef477a..62289ea7cd 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Tests.Beatmaps; @@ -68,6 +69,7 @@ namespace osu.Game.Tests.Visual var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; dependencies.CacheAs(clock); dependencies.CacheAs(clock); + dependencies.Cache(new BindableBeatDivisor()); Child = new OsuHitObjectComposer(new OsuRuleset()); } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index ae1c8af1a4..7ab9ff9164 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -16,6 +16,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; @@ -31,6 +32,7 @@ namespace osu.Game.Rulesets.Edit private readonly List layerContainers = new List(); private readonly Bindable beatmap = new Bindable(); + private readonly Bindable beatDivisor = new Bindable(); private IAdjustableClock adjustableClock; @@ -42,9 +44,10 @@ namespace osu.Game.Rulesets.Edit } [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame, IAdjustableClock adjustableClock, IFrameBasedClock framedClock) + private void load(OsuGameBase osuGame, IAdjustableClock adjustableClock, IFrameBasedClock framedClock, BindableBeatDivisor beatDivisor) { this.adjustableClock = adjustableClock; + this.beatDivisor.BindTo(beatDivisor); beatmap.BindTo(osuGame.Beatmap); @@ -167,9 +170,6 @@ namespace osu.Game.Rulesets.Edit private void seek(int direction, bool snapped) { - // Todo: This should not be a constant, but feels good for now - const int beat_snap_divisor = 4; - var cpi = beatmap.Value.Beatmap.ControlPointInfo; var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime); @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Edit timingPoint = cpi.TimingPoints[--activeIndex]; } - double seekAmount = timingPoint.BeatLength / beat_snap_divisor; + double seekAmount = timingPoint.BeatLength / beatDivisor; double seekTime = adjustableClock.CurrentTime + seekAmount * direction; if (!snapped || cpi.TimingPoints.Count == 0) @@ -222,9 +222,6 @@ namespace osu.Game.Rulesets.Edit public void SeekTo(double seekTime, bool snapped = false) { - // Todo: This should not be a constant, but feels good for now - const int beat_snap_divisor = 4; - if (!snapped) { adjustableClock.Seek(seekTime); @@ -232,7 +229,7 @@ namespace osu.Game.Rulesets.Edit } var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime); - double beatSnapLength = timingPoint.BeatLength / beat_snap_divisor; + double beatSnapLength = timingPoint.BeatLength / beatDivisor; // We will be snapping to beats within the timing point seekTime -= timingPoint.Time; diff --git a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs index eb68bce71b..df2521dc10 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs @@ -7,8 +7,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose { public class BindableBeatDivisor : Bindable { - public BindableBeatDivisor() - : base(1) + public BindableBeatDivisor(int value = 1) + : base(value) { } } From fa5fd46f8545f53baf476e9572a51e426e3a194c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Mar 2018 20:42:42 +0900 Subject: [PATCH 193/537] Clean up + rename testcase --- ...tSnapVisualiser.cs => TestCaseDrawableBeatDivisor.cs} | 9 +-------- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) rename osu.Game.Tests/Visual/{TestCaseBeatSnapVisualiser.cs => TestCaseDrawableBeatDivisor.cs} (77%) diff --git a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs b/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs similarity index 77% rename from osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs rename to osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs index 23e343c36e..104ce1557c 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSnapVisualiser.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Screens.Compose; @@ -10,13 +8,8 @@ using OpenTK; namespace osu.Game.Tests.Visual { - public class TestCaseBeatSnapVisualiser : OsuTestCase + public class TestCaseDrawableBeatDivisor : OsuTestCase { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableBeatDivisor) - }; - private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index c244ab8050..b5ab229f8a 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -116,7 +116,7 @@ - + From 553fd3b7897f9edce3b78604f0e33d72b08f18e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Mar 2018 21:00:27 +0900 Subject: [PATCH 194/537] Give DefaultSkin an empty Configuration --- osu.Game/Skinning/DefaultSkin.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index e40a43d400..c469e91250 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -11,6 +11,7 @@ namespace osu.Game.Skinning public DefaultSkin() : base(SkinInfo.Default) { + Configuration = new SkinConfiguration(); } public override Drawable GetDrawableComponent(string componentName) From ee73bd4568494c26df000b80a0f94ccf91e62383 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Mon, 19 Mar 2018 22:39:00 +0300 Subject: [PATCH 195/537] Update ScreenshotManager inline with framework changes --- osu.Game/Graphics/ScreenshotManager.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index ae1aebfbc1..8028b744c9 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -36,7 +36,7 @@ namespace osu.Game.Graphics switch (action) { case GlobalAction.TakeScreenshot: - TakeScreenshot(); + TakeScreenshotAsync(); return true; } @@ -45,9 +45,9 @@ namespace osu.Game.Graphics public bool OnReleased(GlobalAction action) => false; - public void TakeScreenshot() + public async void TakeScreenshotAsync() { - host.TakeScreenshot(screenshotBitmap => + using (var bitmap = await host.TakeScreenshotAsync()) { var fileName = getFileName(); var stream = storage.GetStream(fileName, FileAccess.Write); @@ -55,17 +55,17 @@ namespace osu.Game.Graphics switch (screenshotFormat.Value) { case ScreenshotFormat.Png: - screenshotBitmap.Save(stream, ImageFormat.Png); + bitmap.Save(stream, ImageFormat.Png); break; case ScreenshotFormat.Jpg: - screenshotBitmap.Save(stream, ImageFormat.Jpeg); + bitmap.Save(stream, ImageFormat.Jpeg); break; default: throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); } notificationOverlay.Post(new SimpleNotification { Text = $"{fileName} saved" }); - }); + } } private string getFileName() From 50192b21e3a1cdd9d6f31e794075f5f389cd4b12 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Mar 2018 13:57:25 +0900 Subject: [PATCH 196/537] Give DrawableBeatDivisor a ctor --- .../Visual/TestCaseDrawableBeatDivisor.cs | 9 +---- .../Screens/Edit/Screens/Compose/Compose.cs | 2 +- .../Screens/Compose/DrawableBeatDivisor.cs | 34 +++++++++---------- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs b/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs index 104ce1557c..238fd09fd8 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs @@ -10,17 +10,10 @@ namespace osu.Game.Tests.Visual { public class TestCaseDrawableBeatDivisor : OsuTestCase { - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); - [BackgroundDependencyLoader] private void load() { - dependencies.Cache(new BindableBeatDivisor()); - - Child = new DrawableBeatDivisor + Child = new DrawableBeatDivisor(new BindableBeatDivisor()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 41bdc25bcf..2603832437 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose Padding = new MarginPadding { Right = 5 }, Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } }, - new DrawableBeatDivisor { RelativeSizeAxes = Axes.Both } + new DrawableBeatDivisor(beatDivisor) { RelativeSizeAxes = Axes.Both } }, }, ColumnDimensions = new[] diff --git a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs index 1a4ab05254..08221009cc 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs @@ -19,14 +19,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose { private static readonly int[] available_divisors = { 1, 2, 3, 4, 6, 8, 12, 16 }; - private readonly Bindable beatDivisor = new Bindable(1); + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); private int currentDivisorIndex; - [BackgroundDependencyLoader] - private void load(OsuColour colours, BindableBeatDivisor beatDivisor) + public DrawableBeatDivisor(BindableBeatDivisor beatDivisor) { this.beatDivisor.BindTo(beatDivisor); + } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { Masking = true; CornerRadius = 5; @@ -45,7 +48,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose { new Drawable[] { - new TickContainer(1, 2, 3, 4, 6, 8, 12, 16) + new TickContainer(beatDivisor, 1, 2, 3, 4, 6, 8, 12, 16) { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = 5 } @@ -79,7 +82,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose Icon = FontAwesome.fa_chevron_left, Action = selectPrevious }, - new DivisorText(), + new DivisorText(beatDivisor), new DivisorButton { Icon = FontAwesome.fa_chevron_right, @@ -135,17 +138,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose { private readonly Bindable beatDivisor = new Bindable(); - public DivisorText() + public DivisorText(BindableBeatDivisor beatDivisor) { + this.beatDivisor.BindTo(beatDivisor); + Anchor = Anchor.Centre; Origin = Anchor.Centre; } [BackgroundDependencyLoader] - private void load(OsuColour colours, BindableBeatDivisor beatDivisor) + private void load(OsuColour colours) { - this.beatDivisor.BindTo(beatDivisor); - Colour = colours.BlueLighter; } @@ -187,27 +190,24 @@ namespace osu.Game.Screens.Edit.Screens.Compose { private readonly Bindable beatDivisor = new Bindable(); - public new MarginPadding Padding - { - set => base.Padding = value; - } + public new MarginPadding Padding { set => base.Padding = value; } private EquilateralTriangle marker; private readonly int[] availableDivisors; private readonly float tickSpacing; - public TickContainer(params int[] divisors) + public TickContainer(BindableBeatDivisor beatDivisor, params int[] divisors) { + this.beatDivisor.BindTo(beatDivisor); + availableDivisors = divisors; tickSpacing = 1f / (availableDivisors.Length + 1); } [BackgroundDependencyLoader] - private void load(OsuColour colours, BindableBeatDivisor beatDivisor) + private void load(OsuColour colours) { - this.beatDivisor.BindTo(beatDivisor); - InternalChild = marker = new EquilateralTriangle { Anchor = Anchor.BottomLeft, From 786e6242e166d5a9651edb51e541ccdc79157506 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Mar 2018 14:01:06 +0900 Subject: [PATCH 197/537] Make bdl beat divisor nullable --- osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs | 2 -- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 11 +++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 62289ea7cd..bbbfef477a 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -14,7 +14,6 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Tests.Beatmaps; @@ -69,7 +68,6 @@ namespace osu.Game.Tests.Visual var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; dependencies.CacheAs(clock); dependencies.CacheAs(clock); - dependencies.Cache(new BindableBeatDivisor()); Child = new OsuHitObjectComposer(new OsuRuleset()); } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 7ab9ff9164..c076b53f51 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Edit private readonly List layerContainers = new List(); private readonly Bindable beatmap = new Bindable(); - private readonly Bindable beatDivisor = new Bindable(); + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); private IAdjustableClock adjustableClock; @@ -43,11 +44,13 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both; } - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame, IAdjustableClock adjustableClock, IFrameBasedClock framedClock, BindableBeatDivisor beatDivisor) + [BackgroundDependencyLoader(true)] + private void load([NotNull] OsuGameBase osuGame, [NotNull] IAdjustableClock adjustableClock, [NotNull] IFrameBasedClock framedClock, [CanBeNull] BindableBeatDivisor beatDivisor) { this.adjustableClock = adjustableClock; - this.beatDivisor.BindTo(beatDivisor); + + if (beatDivisor != null) + this.beatDivisor.BindTo(beatDivisor); beatmap.BindTo(osuGame.Beatmap); From cb3d0db555d73fe665421ab21f36dc7ae0011da8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Mar 2018 15:58:04 +0900 Subject: [PATCH 198/537] Move combo colours completely out of HitObjects --- .../Beatmaps/CatchBeatmapProcessor.cs | 2 +- .../Objects/BananaShower.cs | 15 ---------- .../Objects/CatchHitObject.cs | 9 +++--- .../Objects/Drawable/DrawableDroplet.cs | 17 +++++++++-- .../Objects/Drawable/DrawableFruit.cs | 30 ++++++++++++++++++- .../Objects/Drawable/DrawableJuiceStream.cs | 1 - .../Objects/JuiceStream.cs | 4 --- .../Tests/TestCaseFruitObjects.cs | 30 ------------------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 1 - .../Objects/Drawables/DrawableHoldNote.cs | 28 +++++++---------- .../Drawables/DrawableManiaHitObject.cs | 12 -------- .../Objects/Drawables/DrawableNote.cs | 9 ++---- .../Objects/Drawables/DrawableHitCircle.cs | 29 +++++++++++------- .../Objects/Drawables/DrawableOsuHitObject.cs | 1 - .../Objects/Drawables/DrawableSlider.cs | 14 +++++++-- .../Objects/Drawables/Pieces/SliderBody.cs | 2 ++ osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 10 ++++--- osu.Game.Rulesets.Osu/Objects/Slider.cs | 10 +++---- .../Tests/TestCaseHitCircle.cs | 2 -- osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs | 6 ---- .../Objects/Drawables/DrawableDrumRoll.cs | 19 ++++++------ 21 files changed, 115 insertions(+), 136 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 0cdc1694f4..924a52a858 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } obj.IndexInBeatmap = index++; - obj.ComboColour = beatmap.ComboColours[colourIndex]; + obj.AccentColour = beatmap.ComboColours[colourIndex]; lastObj = obj; } diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 89bd73f8fb..487345019b 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -3,7 +3,6 @@ using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Types; -using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects { @@ -32,25 +31,11 @@ namespace osu.Game.Rulesets.Catch.Objects AddNested(new Banana { Samples = Samples, - ComboColour = getNextComboColour(), StartTime = i, X = RNG.NextSingle() }); } - private Color4 getNextComboColour() - { - switch (RNG.Next(0, 3)) - { - default: - return new Color4(255, 240, 0, 255); - case 1: - return new Color4(255, 192, 0, 255); - case 2: - return new Color4(214, 221, 28, 255); - } - } - public double EndTime => StartTime + Duration; public double Duration { get; set; } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 559bf47842..a6ab18bbf7 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -5,24 +5,25 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; -using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects { - public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo + public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboIndex { public const double OBJECT_RADIUS = 44; public float X { get; set; } - public Color4 ComboColour { get; set; } - public int IndexInBeatmap { get; set; } public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4); public virtual bool NewCombo { get; set; } + public int IndexInCurrentCombo { get; set; } + + public int ComboIndex { get; set; } + /// /// The next fruit starts a new combo. Used for explodey. /// diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs index f05f51052d..719cf0a110 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs @@ -5,28 +5,39 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawable { public class DrawableDroplet : PalpableCatchHitObject { + private Pulp pulp; + public DrawableDroplet(Droplet h) : base(h) { Origin = Anchor.Centre; Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4; - AccentColour = h.ComboColour; Masking = false; } [BackgroundDependencyLoader] private void load() { - InternalChild = new Pulp + InternalChild = pulp = new Pulp { - AccentColour = AccentColour, Size = Size }; } + + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + pulp.AccentColour = AccentColour; + } + } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index dcad82130e..03c2444d8c 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable Origin = Anchor.Centre; Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS); - AccentColour = HitObject.ComboColour; Masking = false; Rotation = (float)(RNG.NextDouble() - 0.5f) * 40; @@ -33,6 +32,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable [BackgroundDependencyLoader] private void load() { + // todo: this should come from the skin. + AccentColour = colourForRrepesentation(HitObject.VisualRepresentation); + InternalChildren = new[] { createPulp(HitObject.VisualRepresentation), @@ -273,5 +275,31 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1); } + + private Color4 colourForRrepesentation(FruitVisualRepresentation representation) + { + switch (representation) + { + default: + case FruitVisualRepresentation.Pear: + return new Color4(17, 136, 170, 255); + case FruitVisualRepresentation.Grape: + return new Color4(204, 102, 0, 255); + case FruitVisualRepresentation.Raspberry: + return new Color4(121, 9, 13, 255); + case FruitVisualRepresentation.Pineapple: + return new Color4(102, 136, 0, 255); + case FruitVisualRepresentation.Banana: + switch (RNG.Next(0, 3)) + { + default: + return new Color4(255, 240, 0, 255); + case 1: + return new Color4(255, 192, 0, 255); + case 2: + return new Color4(214, 221, 28, 255); + } + } + } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs index 0a2763cbea..b3532e2473 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs @@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable var catchObject = (DrawableCatchHitObject)h; catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false; - catchObject.AccentColour = HitObject.ComboColour; dropletContainer.Add(h); base.AddNested(h); diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index a3e5aba2db..1e4051c5aa 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -60,7 +60,6 @@ namespace osu.Game.Rulesets.Catch.Objects AddNested(new Fruit { Samples = Samples, - ComboColour = ComboColour, StartTime = StartTime, X = X }); @@ -82,7 +81,6 @@ namespace osu.Game.Rulesets.Catch.Objects AddNested(new Droplet { StartTime = lastTickTime, - ComboColour = ComboColour, X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo { @@ -104,7 +102,6 @@ namespace osu.Game.Rulesets.Catch.Objects AddNested(new TinyDroplet { StartTime = spanStartTime + t, - ComboColour = ComboColour, X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo { @@ -118,7 +115,6 @@ namespace osu.Game.Rulesets.Catch.Objects AddNested(new Fruit { Samples = Samples, - ComboColour = ComboColour, StartTime = spanStartTime + spanDuration, X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH }); diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs index 16266196e7..595ca6cb24 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs @@ -6,13 +6,11 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using osu.Game.Tests.Visual; using OpenTK; -using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Tests { @@ -62,8 +60,6 @@ namespace osu.Game.Rulesets.Catch.Tests Scale = 1.5f, }; - fruit.ComboColour = colourForRrepesentation(fruit.VisualRepresentation); - return new DrawableFruit(fruit) { Anchor = Anchor.Centre, @@ -74,31 +70,5 @@ namespace osu.Game.Rulesets.Catch.Tests LifetimeEnd = double.PositiveInfinity, }; } - - private Color4 colourForRrepesentation(FruitVisualRepresentation representation) - { - switch (representation) - { - default: - case FruitVisualRepresentation.Pear: - return new Color4(17, 136, 170, 255); - case FruitVisualRepresentation.Grape: - return new Color4(204, 102, 0, 255); - case FruitVisualRepresentation.Raspberry: - return new Color4(121, 9, 13, 255); - case FruitVisualRepresentation.Pineapple: - return new Color4(102, 136, 0, 255); - case FruitVisualRepresentation.Banana: - switch (RNG.Next(0, 3)) - { - default: - return new Color4(255, 240, 0, 255); - case 1: - return new Color4(255, 192, 0, 255); - case 2: - return new Color4(214, 221, 28, 255); - } - } - } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 7c548f70d4..bf2f9db4a8 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -54,7 +54,6 @@ namespace osu.Game.Rulesets.Catch.UI if (caughtFruit == null) return; - caughtFruit.AccentColour = fruit.AccentColour; caughtFruit.RelativePositionAxes = Axes.None; caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 6eb34c7005..c3d6a69a72 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -8,7 +8,6 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using OpenTK.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mania.Judgements; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Scoring; @@ -24,7 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables private readonly GlowPiece glowPiece; private readonly BodyPiece bodyPiece; - private readonly Container tickContainer; private readonly Container fullHeightContainer; /// @@ -40,6 +38,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public DrawableHoldNote(HoldNote hitObject, ManiaAction action) : base(hitObject, action) { + Container tickContainer; RelativeSizeAxes = Axes.X; InternalChildren = new Drawable[] @@ -57,7 +56,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X, }, - tickContainer = new Container { RelativeSizeAxes = Axes.Both }, + tickContainer = new Container + { + RelativeSizeAxes = Axes.Both, + ChildrenEnumerable = HitObject.NestedHitObjects.OfType().Select(tick => new DrawableHoldNoteTick(tick) + { + HoldStartTime = () => holdStartTime + }) + }, head = new DrawableHeadNote(this, action) { Anchor = Anchor.TopCentre, @@ -70,16 +76,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables } }; - foreach (var tick in HitObject.NestedHitObjects.OfType()) - { - var drawableTick = new DrawableHoldNoteTick(tick) - { - HoldStartTime = () => holdStartTime - }; - - tickContainer.Add(drawableTick); - AddNested(drawableTick); - } + foreach (var tick in tickContainer) + AddNested(tick); AddNested(head); AddNested(tail); @@ -90,12 +88,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables get { return base.AccentColour; } set { - if (base.AccentColour == value) - return; base.AccentColour = value; - tickContainer.Children.ForEach(t => t.AccentColour = value); - glowPiece.AccentColour = value; bodyPiece.AccentColour = value; head.AccentColour = value; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 0a1624b464..3aec8d25f9 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using OpenTK.Graphics; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Objects.Drawables @@ -28,16 +27,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (action != null) Action = action.Value; } - - public override Color4 AccentColour - { - get { return base.AccentColour; } - set - { - if (base.AccentColour == value) - return; - base.AccentColour = value; - } - } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index c8aa4588a8..c171325fb2 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -48,13 +48,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables get { return base.AccentColour; } set { - if (base.AccentColour == value) - return; base.AccentColour = value; - - laneGlowPiece.AccentColour = value; - GlowPiece.AccentColour = value; - headPiece.AccentColour = value; + laneGlowPiece.AccentColour = AccentColour; + GlowPiece.AccentColour = AccentColour; + headPiece.AccentColour = AccentColour; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 1f94f49598..9066a9ef92 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -21,7 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly NumberPiece number; private readonly GlowPiece glow; - public DrawableHitCircle(HitCircle h) : base(h) + public DrawableHitCircle(HitCircle h) + : base(h) { Origin = Anchor.Centre; @@ -30,13 +32,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables InternalChildren = new Drawable[] { - glow = new GlowPiece - { - Colour = AccentColour - }, + glow = new GlowPiece(), circle = new CirclePiece { - Colour = AccentColour, Hit = () => { if (AllJudged) @@ -52,15 +50,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, ring = new RingPiece(), flash = new FlashPiece(), - explode = new ExplodePiece - { - Colour = AccentColour, - }, + explode = new ExplodePiece(), ApproachCircle = new ApproachCircle { Alpha = 0, Scale = new Vector2(4), - Colour = AccentColour, } }; @@ -70,6 +64,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; } + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + explode.Colour = AccentColour; + glow.Colour = AccentColour; + circle.Colour = AccentColour; + ApproachCircle.Colour = AccentColour; + } + } + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (!userTriggered) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index c8e42fa44f..2e59e2dc60 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected DrawableOsuHitObject(OsuHitObject hitObject) : base(hitObject) { - AccentColour = HitObject.ComboColour; Alpha = 0; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 9c2d3f5e07..3872821b96 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -41,7 +42,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { Body = new SliderBody(s) { - AccentColour = AccentColour, PathWidth = s.Scale * 64, }, ticks = new Container { RelativeSizeAxes = Axes.Both }, @@ -50,7 +50,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { BypassAutoSizeAxes = Axes.Both, Scale = new Vector2(s.Scale), - AccentColour = AccentColour, AlwaysPresent = true, Alpha = 0 }, @@ -87,6 +86,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; } + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + Body.AccentColour = AccentColour; + Ball.AccentColour = AccentColour; + } + } + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 8c0eb7ff7d..26186a0049 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -173,6 +173,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces texture.SetData(upload); path.Texture = texture; + + container.ForceRedraw(); } private void computeSize() diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index d9aed23414..5d1908fa6e 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -6,13 +6,11 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using OpenTK; using osu.Game.Rulesets.Objects.Types; -using OpenTK.Graphics; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Edit.Types; namespace osu.Game.Rulesets.Osu.Objects { - public abstract class OsuHitObject : HitObject, IHasCombo, IHasEditablePosition + public abstract class OsuHitObject : HitObject, IHasComboIndex, IHasPosition { public const double OBJECT_RADIUS = 64; @@ -53,10 +51,14 @@ namespace osu.Game.Rulesets.Osu.Objects public float Scale { get; set; } = 1; - public Color4 ComboColour { get; set; } = Color4.Gray; public virtual bool NewCombo { get; set; } + public int IndexInCurrentCombo { get; set; } + public int ComboIndex { get; set; } + + public bool LastInCombo { get; set; } + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index a633e3957e..469c4ddcb4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -99,10 +99,10 @@ namespace osu.Game.Rulesets.Osu.Objects { StartTime = StartTime, Position = Position, - IndexInCurrentCombo = IndexInCurrentCombo, - ComboColour = ComboColour, Samples = Samples, - SampleControlPoint = SampleControlPoint + SampleControlPoint = SampleControlPoint, + IndexInCurrentCombo = IndexInCurrentCombo, + ComboIndex = ComboIndex, }; TailCircle = new SliderCircle(this) @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects StartTime = EndTime, Position = EndPosition, IndexInCurrentCombo = IndexInCurrentCombo, - ComboColour = ComboColour + ComboIndex = ComboIndex, }; AddNested(HeadCircle); @@ -160,7 +160,6 @@ namespace osu.Game.Rulesets.Osu.Objects Position = Position + Curve.PositionAt(distanceProgress), StackHeight = StackHeight, Scale = Scale, - ComboColour = ComboColour, Samples = sampleList }); } @@ -179,7 +178,6 @@ namespace osu.Game.Rulesets.Osu.Objects Position = Position + Curve.PositionAt(repeat % 2), StackHeight = StackHeight, Scale = Scale, - ComboColour = ComboColour, Samples = new List(RepeatSamples[repeatIndex]) }); } diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs index f40d9c05d1..b0cfa43f15 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs @@ -10,7 +10,6 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Tests.Visual; using OpenTK; -using OpenTK.Graphics; using osu.Game.Rulesets.Osu.Judgements; using System.Collections.Generic; using System; @@ -61,7 +60,6 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000 + timeOffset, Position = positionOffset.Value, - ComboColour = Color4.LightSeaGreen }; circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs index b68f59877b..e819d8fff5 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -117,7 +117,6 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-(distance / 2), 0), - ComboColour = Color4.LightSeaGreen, ControlPoints = new List { Vector2.Zero, @@ -138,7 +137,6 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ComboColour = Color4.LightSeaGreen, ControlPoints = new List { Vector2.Zero, @@ -162,7 +160,6 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ComboColour = Color4.LightSeaGreen, ControlPoints = new List { Vector2.Zero, @@ -189,7 +186,6 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Bezier, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ComboColour = Color4.LightSeaGreen, ControlPoints = new List { Vector2.Zero, @@ -215,7 +211,6 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(0, 0), - ComboColour = Color4.LightSeaGreen, ControlPoints = new List { Vector2.Zero, @@ -245,7 +240,6 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-100, 0), - ComboColour = Color4.LightSeaGreen, CurveType = CurveType.Catmull, ControlPoints = new List { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index f98e6b936e..2bb2d478c3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -20,11 +20,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public class DrawableDrumRoll : DrawableTaikoHitObject { /// - /// Number of rolling hits required to reach the dark/final accent colour. + /// Number of rolling hits required to reach the dark/final colour. /// - private const int rolling_hits_for_dark_accent = 5; - - private Color4 accentDarkColour; + private const int rolling_hits_for_engaged_colour = 5; /// /// Rolling number of tick hits. This increases for hits and decreases for misses. @@ -53,11 +51,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) => false; + private Color4 colourIdle; + private Color4 colourEngaged; + [BackgroundDependencyLoader] private void load(OsuColour colours) { - MainPiece.AccentColour = AccentColour = colours.YellowDark; - accentDarkColour = colours.YellowDarker; + MainPiece.AccentColour = colourIdle = colours.YellowDark; + colourEngaged = colours.YellowDarker; } private void onTickJudgement(DrawableHitObject obj, Judgement judgement) @@ -67,10 +68,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables else rollingHits--; - rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent); + rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour); - Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1); - MainPiece.FadeAccent(newAccent, 100); + Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); + MainPiece.FadeAccent(newColour, 100); } protected override void CheckForJudgements(bool userTriggered, double timeOffset) From c38c26eacbac8b3c39fe68b8beb51fd60662d73c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 15:45:40 +0900 Subject: [PATCH 199/537] Move combo index processing to BeatmapProcessor --- .../Beatmaps/CatchBeatmapProcessor.cs | 22 +++------------ .../Beatmaps/OsuBeatmapProcessor.cs | 19 +------------ osu.Game/Beatmaps/BeatmapProcessor.cs | 27 ++++++++++++++++++- .../Rulesets/Objects/Types/IHasComboIndex.cs | 26 ++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 5 files changed, 57 insertions(+), 38 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 924a52a858..1bebe9dae0 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -16,29 +16,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { public override void PostProcess(Beatmap beatmap) { - if (beatmap.ComboColours.Count == 0) - return; - - int index = 0; - int colourIndex = 0; - - CatchHitObject lastObj = null; - initialiseHyperDash(beatmap.HitObjects); + base.PostProcess(beatmap); + + int index = 0; foreach (var obj in beatmap.HitObjects) - { - if (obj.NewCombo) - { - if (lastObj != null) lastObj.LastInCombo = true; - colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count; - } - obj.IndexInBeatmap = index++; - obj.AccentColour = beatmap.ComboColours[colourIndex]; - - lastObj = obj; - } } private void initialiseHyperDash(List objects) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index bfcdec9321..42b22a71ec 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -13,24 +13,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps public override void PostProcess(Beatmap beatmap) { applyStacking(beatmap); - - if (beatmap.ComboColours.Count == 0) - return; - - int comboIndex = 0; - int colourIndex = 0; - - foreach (var obj in beatmap.HitObjects) - { - if (obj.NewCombo) - { - comboIndex = 0; - colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count; - } - - obj.IndexInCurrentCombo = comboIndex++; - obj.ComboColour = beatmap.ComboColours[colourIndex]; - } + base.PostProcess(beatmap); } private void applyStacking(Beatmap beatmap) diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index 9b528699ef..83b2867df7 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -1,7 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps { @@ -19,6 +21,29 @@ namespace osu.Game.Beatmaps /// /// /// The Beatmap to process. - public virtual void PostProcess(Beatmap beatmap) { } + public virtual void PostProcess(Beatmap beatmap) + { + IHasComboIndex lastObj = null; + + foreach (var obj in beatmap.HitObjects.OfType()) + { + if (obj.NewCombo) + { + obj.IndexInCurrentCombo = 0; + if (lastObj != null) + { + lastObj.LastInCombo = true; + obj.ComboIndex = lastObj.ComboIndex + 1; + } + } + else if (lastObj != null) + { + obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1; + obj.ComboIndex = lastObj.ComboIndex; + } + + lastObj = obj; + } + } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs b/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs new file mode 100644 index 0000000000..68474a6e2c --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that is part of a combo and has extended information about its position relative to other combo objects. + /// + public interface IHasComboIndex : IHasCombo + { + /// + /// The offset of this hitobject in the current combo. + /// + int IndexInCurrentCombo { get; set; } + + /// + /// The offset of this hitobject in the current combo. + /// + int ComboIndex { get; set; } + + /// + /// Whether this is the last object in the current combo. + /// + bool LastInCombo { get; set; } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 091ec3f7ac..5ece1a18ac 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -374,6 +374,7 @@ + From dbcf755618fc2cd425b66f7ae784b652749f1bae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Mar 2018 20:45:04 +0900 Subject: [PATCH 200/537] Make Beatmaps parsable as skins --- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 18 +++++++++++ osu.Game/Beatmaps/WorkingBeatmap.cs | 11 +++++++ osu.Game/Database/IHasFiles.cs | 2 ++ osu.Game/Skinning/BeatmapSkin.cs | 31 +++++++++++++++++++ osu.Game/Skinning/LegacySkin.cs | 27 +++++++++------- osu.Game/Skinning/Skin.cs | 27 +++++++++++++++- osu.Game/osu.Game.csproj | 1 + 7 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Skinning/BeatmapSkin.cs diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index fb11684309..d7d17c980c 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -10,6 +10,7 @@ using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps.Formats; using osu.Game.Graphics.Textures; +using osu.Game.Skinning; using osu.Game.Storyboards; namespace osu.Game.Beatmaps @@ -100,6 +101,23 @@ namespace osu.Game.Beatmaps return storyboard; } + + protected override Skin GetSkin() + { + Skin skin; + try + { + // todo: this needs an AudioManager + skin = new BeatmapSkin(BeatmapInfo, store); + } + catch (Exception e) + { + Logger.Error(e, "Skin failed to load"); + skin = new DefaultSkin(); + } + + return skin; + } } } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8a2a7b01a1..5c0ad7685b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -14,6 +14,7 @@ using osu.Framework.IO.File; using System.IO; using osu.Game.IO.Serialization; using System.Diagnostics; +using osu.Game.Skinning; namespace osu.Game.Beatmaps { @@ -40,6 +41,7 @@ namespace osu.Game.Beatmaps track = new AsyncLazy(populateTrack); waveform = new AsyncLazy(populateWaveform); storyboard = new AsyncLazy(populateStoryboard); + skin = new AsyncLazy(populateSkin); } /// @@ -56,6 +58,7 @@ namespace osu.Game.Beatmaps protected abstract Beatmap GetBeatmap(); protected abstract Texture GetBackground(); protected abstract Track GetTrack(); + protected virtual Skin GetSkin() => new DefaultSkin(); protected virtual Waveform GetWaveform() => new Waveform(); protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; @@ -109,6 +112,13 @@ namespace osu.Game.Beatmaps private Storyboard populateStoryboard() => GetStoryboard(); + public bool SkinLoaded => skin.IsResultAvailable; + public Skin Skin => skin.Value.Result; + public async Task GetSkinAsync() => await skin.Value; + private readonly AsyncLazy skin; + + private Skin populateSkin() => GetSkin(); + public void TransferTo(WorkingBeatmap other) { if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) @@ -123,6 +133,7 @@ namespace osu.Game.Beatmaps if (BackgroundLoaded) Background?.Dispose(); if (WaveformLoaded) Waveform?.Dispose(); if (StoryboardLoaded) Storyboard?.Dispose(); + if (SkinLoaded) Skin?.Dispose(); } /// diff --git a/osu.Game/Database/IHasFiles.cs b/osu.Game/Database/IHasFiles.cs index deaf75360c..faf3f16dfe 100644 --- a/osu.Game/Database/IHasFiles.cs +++ b/osu.Game/Database/IHasFiles.cs @@ -10,6 +10,8 @@ namespace osu.Game.Database /// /// The model representing a file. public interface IHasFiles + where TFile : INamedFileInfo + { List Files { get; set; } } diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs new file mode 100644 index 0000000000..beab2a42d7 --- /dev/null +++ b/osu.Game/Skinning/BeatmapSkin.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; +using osu.Game.Beatmaps; + +namespace osu.Game.Skinning +{ + public class BeatmapSkin : LegacySkin + { + public BeatmapSkin(BeatmapInfo beatmap, IResourceStore storage) + : base(new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }) + { + storage = new LegacySkinResourceStore(beatmap.BeatmapSet, storage); + + // todo: sample support + // samples = audioManager.GetSampleManager(storage); + + Textures = new TextureStore(new RawTextureLoaderStore(storage)); + + var decoder = new LegacySkinDecoder(); + + using (StreamReader reader = new StreamReader(storage.GetStream(beatmap.Path))) + { + Configuration = decoder.Decode(reader); + } + } + } +} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index b531d791b0..bae0d5c997 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -10,24 +10,24 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Game.Database; namespace osu.Game.Skinning { public class LegacySkin : Skin { - private readonly TextureStore textures; + protected TextureStore Textures; private readonly SampleManager samples; public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) - : base(skin) + : this(skin) { - storage = new LegacySkinResourceStore(skin, storage); + storage = new LegacySkinResourceStore(skin, storage); samples = audioManager.GetSampleManager(storage); - textures = new TextureStore(new RawTextureLoaderStore(storage)); + Textures = new TextureStore(new RawTextureLoaderStore(storage)); Stream stream = storage.GetStream("skin.ini"); - if (stream != null) using (StreamReader reader = new StreamReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); @@ -35,6 +35,10 @@ namespace osu.Game.Skinning Configuration = new SkinConfiguration(); } + protected LegacySkin(SkinInfo skin) : base(skin) + { + } + public override Drawable GetDrawableComponent(string componentName) { switch (componentName) @@ -53,7 +57,7 @@ namespace osu.Game.Skinning break; } - var texture = textures.Get(componentName); + var texture = Textures.Get(componentName); if (texture == null) return null; return new Sprite { Texture = texture }; @@ -61,9 +65,10 @@ namespace osu.Game.Skinning public override SampleChannel GetSample(string sampleName) => samples.Get(sampleName); - private class LegacySkinResourceStore : IResourceStore + protected class LegacySkinResourceStore : IResourceStore + where T : INamedFileInfo { - private readonly SkinInfo skin; + private readonly IHasFiles source; private readonly IResourceStore underlyingStore; private string getPathForFile(string filename) @@ -72,14 +77,14 @@ namespace osu.Game.Skinning string lastPiece = filename.Split('/').Last(); - var file = skin.Files.FirstOrDefault(f => + var file = source.Files.FirstOrDefault(f => string.Equals(hasExtension ? f.Filename : Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase)); return file?.FileInfo.StoragePath; } - public LegacySkinResourceStore(SkinInfo skin, IResourceStore underlyingStore) + public LegacySkinResourceStore(IHasFiles source, IResourceStore underlyingStore) { - this.skin = skin; + this.source = source; this.underlyingStore = underlyingStore; } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 7b4e894dfd..53bcf30b0e 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -1,12 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; namespace osu.Game.Skinning { - public abstract class Skin + public abstract class Skin : IDisposable { public readonly SkinInfo SkinInfo; @@ -20,5 +21,29 @@ namespace osu.Game.Skinning { SkinInfo = skin; } + + #region Disposal + + ~Skin() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private bool isDisposed; + + protected virtual void Dispose(bool isDisposing) + { + if (isDisposed) + return; + isDisposed = true; + } + + #endregion } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b325e52ed1..50c272e827 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -871,6 +871,7 @@ + From 9a0fc9e29aa14c86cb8c038e576b4e1e50e70cc2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Mar 2018 15:13:09 +0900 Subject: [PATCH 201/537] Move combo colours completely out of beatmap --- .../Beatmaps/CatchBeatmapProcessor.cs | 6 ------ .../Beatmaps/OsuBeatmapProcessor.cs | 6 ------ .../Formats/LegacyBeatmapDecoderTest.cs | 3 ++- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 19 ------------------- osu.Game/Beatmaps/Beatmap.cs | 13 +------------ osu.Game/Beatmaps/BeatmapConverter.cs | 1 - 6 files changed, 3 insertions(+), 45 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 0cdc1694f4..47acc1c926 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -16,11 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { public override void PostProcess(Beatmap beatmap) { - if (beatmap.ComboColours.Count == 0) - return; - int index = 0; - int colourIndex = 0; CatchHitObject lastObj = null; @@ -31,11 +27,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps if (obj.NewCombo) { if (lastObj != null) lastObj.LastInCombo = true; - colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count; } obj.IndexInBeatmap = index++; - obj.ComboColour = beatmap.ComboColours[colourIndex]; lastObj = obj; } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index bfcdec9321..5b72d1ac6e 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -14,22 +14,16 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { applyStacking(beatmap); - if (beatmap.ComboColours.Count == 0) - return; - int comboIndex = 0; - int colourIndex = 0; foreach (var obj in beatmap.HitObjects) { if (obj.NewCombo) { comboIndex = 0; - colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count; } obj.IndexInCurrentCombo = comboIndex++; - obj.ComboColour = beatmap.ComboColours[colourIndex]; } } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 2c46a124d8..bc878b599b 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -11,6 +11,7 @@ using osu.Game.Audio; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; +using osu.Game.Skinning; namespace osu.Game.Tests.Beatmaps.Formats { @@ -163,7 +164,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeBeatmapColors() { - var decoder = new LegacyBeatmapDecoder(); + var decoder = new LegacySkinDecoder(); using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index c36e825252..f37672b5cc 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -12,7 +12,6 @@ using osu.Game.IO.Serialization; using osu.Game.Rulesets.Objects.Types; using osu.Game.Tests.Resources; using OpenTK; -using OpenTK.Graphics; namespace osu.Game.Tests.Beatmaps.Formats { @@ -89,24 +88,6 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(2, difficulty.SliderTickRate); } - [Test] - public void TestDecodeColors() - { - var beatmap = decodeAsJson(normal); - Color4[] expected = - { - new Color4(142, 199, 255, 255), - new Color4(255, 128, 128, 255), - new Color4(128, 255, 255, 255), - new Color4(128, 255, 128, 255), - new Color4(255, 187, 255, 255), - new Color4(255, 177, 140, 255), - }; - Assert.AreEqual(expected.Length, beatmap.ComboColours.Count); - for (int i = 0; i < expected.Length; i++) - Assert.AreEqual(expected[i], beatmap.ComboColours[i]); - } - [Test] public void TestDecodeHitObjects() { diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 93817b9b8f..60cf93fd91 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; using System.Collections.Generic; @@ -9,7 +8,6 @@ using System.Linq; using osu.Game.Beatmaps.ControlPoints; using osu.Game.IO.Serialization; using Newtonsoft.Json; -using osu.Game.Beatmaps.Formats; using osu.Game.IO.Serialization.Converters; namespace osu.Game.Beatmaps @@ -17,21 +15,13 @@ namespace osu.Game.Beatmaps /// /// A Beatmap containing converted HitObjects. /// - public class Beatmap : IJsonSerializable, IHasComboColours + public class Beatmap : IJsonSerializable where T : HitObject { public BeatmapInfo BeatmapInfo = new BeatmapInfo(); public ControlPointInfo ControlPointInfo = new ControlPointInfo(); public List Breaks = new List(); - public List ComboColours { get; set; } = new List - { - new Color4(17, 136, 170, 255), - new Color4(102, 136, 0, 255), - new Color4(204, 102, 0, 255), - new Color4(121, 9, 13, 255) - }; - [JsonIgnore] public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; @@ -56,7 +46,6 @@ namespace osu.Game.Beatmaps BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; Breaks = original?.Breaks ?? Breaks; - ComboColours = original?.ComboColours ?? ComboColours; HitObjects = original?.HitObjects ?? HitObjects; if (original == null && Metadata == null) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index c35c5df89b..2003b845d9 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -57,7 +57,6 @@ namespace osu.Game.Beatmaps beatmap.ControlPointInfo = original.ControlPointInfo; beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList(); beatmap.Breaks = original.Breaks; - beatmap.ComboColours = original.ComboColours; return beatmap; } From d1c2aa71d07f620221624c81ae84d932dc9edb34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Mar 2018 00:14:29 +0900 Subject: [PATCH 202/537] Add beatmap sample override support --- osu.Game.Tests/Visual/TestCasePlaySongSelect.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 8 ++++++-- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 8 +++++--- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Skinning/BeatmapSkin.cs | 6 +++--- osu.Game/Skinning/LegacySkin.cs | 6 +++--- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index cede0160bc..5fd8fcc9c3 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext()); dependencies.Cache(rulesets = new RulesetStore(factory)); - dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null) + dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null) { DefaultBeatmap = defaultBeatmap = game.Beatmap.Default }); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 817a3388e2..1113e38d7a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using osu.Framework.Audio; using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Platform; @@ -55,6 +56,8 @@ namespace osu.Game.Beatmaps private readonly APIAccess api; + private readonly AudioManager audioManager; + private readonly List currentDownloads = new List(); /// @@ -62,7 +65,7 @@ namespace osu.Game.Beatmaps /// public Func GetStableStorage { private get; set; } - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null) : base(storage, contextFactory, new BeatmapStore(contextFactory), importHost) { beatmaps = (BeatmapStore)ModelStore; @@ -71,6 +74,7 @@ namespace osu.Game.Beatmaps this.rulesets = rulesets; this.api = api; + this.audioManager = audioManager; } protected override void Populate(BeatmapSetInfo model, ArchiveReader archive) @@ -217,7 +221,7 @@ namespace osu.Game.Beatmaps if (beatmapInfo.Metadata == null) beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo); + WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo, audioManager); previous?.TransferTo(working); diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index d7d17c980c..58b51085a4 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Linq; +using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; @@ -20,11 +21,13 @@ namespace osu.Game.Beatmaps protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap { private readonly IResourceStore store; + private readonly AudioManager audioManager; - public BeatmapManagerWorkingBeatmap(IResourceStore store, BeatmapInfo beatmapInfo) + public BeatmapManagerWorkingBeatmap(IResourceStore store, BeatmapInfo beatmapInfo, AudioManager audioManager) : base(beatmapInfo) { this.store = store; + this.audioManager = audioManager; } protected override Beatmap GetBeatmap() @@ -107,8 +110,7 @@ namespace osu.Game.Beatmaps Skin skin; try { - // todo: this needs an AudioManager - skin = new BeatmapSkin(BeatmapInfo, store); + skin = new BeatmapSkin(BeatmapInfo, store, audioManager); } catch (Exception e) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 45fd45b4b5..f5d7d15a47 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -113,7 +113,7 @@ namespace osu.Game dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Host)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore)); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs index beab2a42d7..815aac2f64 100644 --- a/osu.Game/Skinning/BeatmapSkin.cs +++ b/osu.Game/Skinning/BeatmapSkin.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.IO; +using osu.Framework.Audio; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Beatmaps; @@ -10,13 +11,12 @@ namespace osu.Game.Skinning { public class BeatmapSkin : LegacySkin { - public BeatmapSkin(BeatmapInfo beatmap, IResourceStore storage) + public BeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager) : base(new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }) { storage = new LegacySkinResourceStore(beatmap.BeatmapSet, storage); - // todo: sample support - // samples = audioManager.GetSampleManager(storage); + Samples = audioManager.GetSampleManager(storage); Textures = new TextureStore(new RawTextureLoaderStore(storage)); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index bae0d5c997..cfee2cfab2 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -18,13 +18,13 @@ namespace osu.Game.Skinning { protected TextureStore Textures; - private readonly SampleManager samples; + protected SampleManager Samples; public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin) { storage = new LegacySkinResourceStore(skin, storage); - samples = audioManager.GetSampleManager(storage); + Samples = audioManager.GetSampleManager(storage); Textures = new TextureStore(new RawTextureLoaderStore(storage)); Stream stream = storage.GetStream("skin.ini"); @@ -63,7 +63,7 @@ namespace osu.Game.Skinning return new Sprite { Texture = texture }; } - public override SampleChannel GetSample(string sampleName) => samples.Get(sampleName); + public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName); protected class LegacySkinResourceStore : IResourceStore where T : INamedFileInfo From 9ad4e9284a18ba4b30ab8355cb6b64e078156296 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 16:26:36 +0900 Subject: [PATCH 203/537] Add skin source fallback chain --- osu.Game/OsuGameBase.cs | 1 + .../Objects/Drawables/DrawableHitObject.cs | 11 +++- osu.Game/Screens/Play/Player.cs | 7 ++- osu.Game/Skinning/DefaultSkin.cs | 22 ++++---- osu.Game/Skinning/ISkinSource.cs | 25 +++++++++ .../Skinning/LocalSkinOverrideContainer.cs | 53 +++++++++++++++++++ osu.Game/Skinning/Skin.cs | 9 +++- osu.Game/Skinning/SkinManager.cs | 16 +++++- osu.Game/Skinning/SkinReloadableDrawable.cs | 15 +++--- osu.Game/Skinning/SkinnableDrawable.cs | 2 +- osu.Game/Skinning/SkinnableSound.cs | 2 +- osu.Game/osu.Game.csproj | 2 + 12 files changed, 142 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Skinning/ISkinSource.cs create mode 100644 osu.Game/Skinning/LocalSkinOverrideContainer.cs diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f5d7d15a47..54a279e977 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -105,6 +105,7 @@ namespace osu.Game runMigrations(); dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); + dependencies.CacheAs(SkinManager); var api = new APIAccess(LocalConfig); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 02f88d9ee0..945cd928d4 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Game.Audio; using osu.Game.Graphics; @@ -19,7 +18,7 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables { - public abstract class DrawableHitObject : CompositeDrawable, IHasAccentColour + public abstract class DrawableHitObject : SkinReloadableDrawable, IHasAccentColour { public readonly HitObject HitObject; @@ -103,6 +102,14 @@ namespace osu.Game.Rulesets.Objects.Drawables } } + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + if (HitObject is IHasComboIndex combo) + AccentColour = skin.GetComboColour(combo) ?? Color4.White; + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8502812f26..b0472f0e0d 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -26,6 +26,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Ranking; +using osu.Game.Skinning; using osu.Game.Storyboards.Drawables; namespace osu.Game.Screens.Play @@ -163,7 +164,11 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Alpha = 0, }, - RulesetContainer, + new LocalSkinOverrideContainer(working.Skin) + { + RelativeSizeAxes = Axes.Both, + Child = RulesetContainer + }, new SkipOverlay(firstObjectTime) { Clock = Clock, // skip button doesn't want to use the audio clock directly diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index c469e91250..aa891646c8 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using OpenTK.Graphics; namespace osu.Game.Skinning { @@ -11,17 +12,20 @@ namespace osu.Game.Skinning public DefaultSkin() : base(SkinInfo.Default) { - Configuration = new SkinConfiguration(); + Configuration = new SkinConfiguration + { + ComboColours = + { + new Color4(17, 136, 170, 255), + new Color4(102, 136, 0, 255), + new Color4(204, 102, 0, 255), + new Color4(121, 9, 13, 255) + } + }; } - public override Drawable GetDrawableComponent(string componentName) - { - return null; - } + public override Drawable GetDrawableComponent(string componentName) => null; - public override SampleChannel GetSample(string sampleName) - { - return null; - } + public override SampleChannel GetSample(string sampleName) => null; } } diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs new file mode 100644 index 0000000000..ffa520ae6a --- /dev/null +++ b/osu.Game/Skinning/ISkinSource.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Types; +using OpenTK.Graphics; + +namespace osu.Game.Skinning +{ + /// + /// Provides access to skinnable elements. + /// + public interface ISkinSource + { + event Action SourceChanged; + + Drawable GetDrawableComponent(string componentName); + + SampleChannel GetSample(string sampleName); + + Color4? GetComboColour(IHasComboIndex comboObject); + } +} diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs new file mode 100644 index 0000000000..66080bac17 --- /dev/null +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Types; +using OpenTK.Graphics; + +namespace osu.Game.Skinning +{ + public class LocalSkinOverrideContainer : Container, ISkinSource + { + public event Action SourceChanged; + + public Drawable GetDrawableComponent(string componentName) => source.GetDrawableComponent(componentName) ?? fallbackSource?.GetDrawableComponent(componentName); + + public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName); + + public Color4? GetComboColour(IHasComboIndex comboObject) => source.GetComboColour(comboObject) ?? fallbackSource?.GetComboColour(comboObject); + + private readonly ISkinSource source; + private ISkinSource fallbackSource; + + public LocalSkinOverrideContainer(ISkinSource source) + { + this.source = source; + } + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + + fallbackSource = dependencies.Get(); + if (fallbackSource != null) + fallbackSource.SourceChanged += () => SourceChanged?.Invoke(); + + dependencies.CacheAs(this); + + return dependencies; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (fallbackSource != null) + fallbackSource.SourceChanged -= SourceChanged; + } + } +} diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 53bcf30b0e..8f8fe94337 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -4,19 +4,26 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Types; +using OpenTK.Graphics; namespace osu.Game.Skinning { - public abstract class Skin : IDisposable + public abstract class Skin : IDisposable, ISkinSource { public readonly SkinInfo SkinInfo; public virtual SkinConfiguration Configuration { get; protected set; } + public event Action SourceChanged; + public abstract Drawable GetDrawableComponent(string componentName); public abstract SampleChannel GetSample(string sampleName); + public virtual Color4? GetComboColour(IHasComboIndex comboObject) => + Configuration.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[comboObject.ComboIndex % Configuration.ComboColours.Count]; + protected Skin(SkinInfo skin) { SkinInfo = skin; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index fa65b923fb..e4149404cd 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -7,14 +7,18 @@ using System.Linq; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Configuration; +using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Game.Database; using osu.Game.IO.Archives; +using osu.Game.Rulesets.Objects.Types; +using OpenTK.Graphics; namespace osu.Game.Skinning { - public class SkinManager : ArchiveModelManager + public class SkinManager : ArchiveModelManager, ISkinSource { private readonly AudioManager audio; @@ -89,6 +93,8 @@ namespace osu.Game.Skinning { if (skin.SkinInfo != CurrentSkinInfo.Value) throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead."); + + SourceChanged?.Invoke(); }; // migrate older imports which didn't have access to skin.ini @@ -108,5 +114,13 @@ namespace osu.Game.Skinning /// The query. /// The first result for the provided query, or null if no results were found. public SkinInfo Query(Expression> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query); + + public event Action SourceChanged; + + public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName); + + public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName); + + public Color4? GetComboColour(IHasComboIndex comboObject) => CurrentSkin.Value.GetComboColour(comboObject); } } diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index 3e33f952cd..04ba8427b2 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; namespace osu.Game.Skinning @@ -12,7 +11,7 @@ namespace osu.Game.Skinning /// public abstract class SkinReloadableDrawable : CompositeDrawable { - private Bindable skin; + private ISkinSource skin; /// /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. @@ -29,16 +28,18 @@ namespace osu.Game.Skinning } [BackgroundDependencyLoader] - private void load(SkinManager skinManager) + private void load(ISkinSource source) { - skin = skinManager.CurrentSkin.GetBoundCopy(); - skin.ValueChanged += skin => SkinChanged(skin, allowDefaultFallback || skin.SkinInfo == SkinInfo.Default); + skin = source; + skin.SourceChanged += onChange; } + private void onChange() => SkinChanged(skin, allowDefaultFallback); + protected override void LoadAsyncComplete() { base.LoadAsyncComplete(); - skin.TriggerChange(); + onChange(); } /// @@ -46,7 +47,7 @@ namespace osu.Game.Skinning /// /// The new skin. /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. - protected virtual void SkinChanged(Skin skin, bool allowFallback) + protected virtual void SkinChanged(ISkinSource skin, bool allowFallback) { } } diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 81abc9e80c..77af44d5d6 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -40,7 +40,7 @@ namespace osu.Game.Skinning RelativeSizeAxes = Axes.Both; } - protected override void SkinChanged(Skin skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { var drawable = skin.GetDrawableComponent(componentName); if (drawable != null) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index fd52d62d59..07c8fd3735 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -31,7 +31,7 @@ namespace osu.Game.Skinning public void Play() => channels?.ForEach(c => c.Play()); - protected override void SkinChanged(Skin skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { channels = samples.Select(s => { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9a240b6899..1c902a158f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -874,8 +874,10 @@ + + From f03abb3145b53b8e0dd21c4d80fbd063a6a59b28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 16:28:39 +0900 Subject: [PATCH 204/537] Add GetTexture method to ISkinSource Used to shortcut lookup checks without potentially expensive drawable creation overhead. --- osu.Game/Skinning/DefaultSkin.cs | 3 +++ osu.Game/Skinning/ISkinSource.cs | 3 +++ osu.Game/Skinning/LegacySkin.cs | 4 +++- osu.Game/Skinning/LocalSkinOverrideContainer.cs | 3 +++ osu.Game/Skinning/Skin.cs | 3 +++ osu.Game/Skinning/SkinManager.cs | 3 +++ 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index aa891646c8..7422ae2e47 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using OpenTK.Graphics; namespace osu.Game.Skinning @@ -26,6 +27,8 @@ namespace osu.Game.Skinning public override Drawable GetDrawableComponent(string componentName) => null; + public override Texture GetTexture(string componentName) => null; + public override SampleChannel GetSample(string sampleName) => null; } } diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs index ffa520ae6a..924fdbd8c1 100644 --- a/osu.Game/Skinning/ISkinSource.cs +++ b/osu.Game/Skinning/ISkinSource.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; @@ -18,6 +19,8 @@ namespace osu.Game.Skinning Drawable GetDrawableComponent(string componentName); + Texture GetTexture(string componentName); + SampleChannel GetSample(string sampleName); Color4? GetComboColour(IHasComboIndex comboObject); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index cfee2cfab2..c543537f32 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -57,12 +57,14 @@ namespace osu.Game.Skinning break; } - var texture = Textures.Get(componentName); + var texture = GetTexture(componentName); if (texture == null) return null; return new Sprite { Texture = texture }; } + public override Texture GetTexture(string componentName) => Textures.Get(componentName); + public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName); protected class LegacySkinResourceStore : IResourceStore diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index 66080bac17..a0cc11a324 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; @@ -17,6 +18,8 @@ namespace osu.Game.Skinning public Drawable GetDrawableComponent(string componentName) => source.GetDrawableComponent(componentName) ?? fallbackSource?.GetDrawableComponent(componentName); + public Texture GetTexture(string componentName) => source.GetTexture(componentName) ?? fallbackSource.GetTexture(componentName); + public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName); public Color4? GetComboColour(IHasComboIndex comboObject) => source.GetComboColour(comboObject) ?? fallbackSource?.GetComboColour(comboObject); diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 8f8fe94337..5d2c640244 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; @@ -21,6 +22,8 @@ namespace osu.Game.Skinning public abstract SampleChannel GetSample(string sampleName); + public abstract Texture GetTexture(string componentName); + public virtual Color4? GetComboColour(IHasComboIndex comboObject) => Configuration.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[comboObject.ComboIndex % Configuration.ComboColours.Count]; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index e4149404cd..9ae6eef49f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -10,6 +10,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using osu.Framework.Platform; using osu.Game.Database; using osu.Game.IO.Archives; @@ -119,6 +120,8 @@ namespace osu.Game.Skinning public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName); + public Texture GetTexture(string componentName)=> CurrentSkin.Value.GetTexture(componentName); + public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName); public Color4? GetComboColour(IHasComboIndex comboObject) => CurrentSkin.Value.GetComboColour(comboObject); From fb3d319d0e56d015b6c3f5ec8cc37486a684c81d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Mar 2018 10:36:26 +0900 Subject: [PATCH 205/537] Make fallback bool into a function Allows correct handling now that beatmap skins are also a thing. --- .../Objects/Drawables/Pieces/ExplodePiece.cs | 2 +- .../Objects/Drawables/Pieces/FlashPiece.cs | 2 +- .../Objects/Drawables/Pieces/GlowPiece.cs | 2 +- .../Objects/Drawables/Pieces/NumberPiece.cs | 2 +- osu.Game/Skinning/SkinReloadableDrawable.cs | 8 +++++--- osu.Game/Skinning/SkinnableDrawable.cs | 6 +++--- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs index 76ed89be67..28552e6c36 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Blending = BlendingMode.Additive, RelativeSizeAxes = Axes.Both, Alpha = 0.2f, - }, false); + }, s => s.GetTexture("Play/osu/hitcircle") == null); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs index 921d24f69d..50dc473750 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { RelativeSizeAxes = Axes.Both } - }, false); + }, s => s.GetTexture("Play/osu/hitcircle") == null); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs index a4e1916659..211e138b65 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Texture = textures.Get(name), Blending = BlendingMode.Additive, Alpha = 0.5f - }, false); + }, s => s.GetTexture("Play/osu/hitcircle") == null); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs index 4220299c66..0c1fd4c364 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Colour = Color4.White.Opacity(0.5f), }, Child = new Box() - }, false), + }, s => s.GetTexture("Play/osu/hitcircle") == null), number = new OsuSpriteText { Text = @"1", diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index 04ba8427b2..36f33e746a 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; @@ -11,20 +12,21 @@ namespace osu.Game.Skinning /// public abstract class SkinReloadableDrawable : CompositeDrawable { + private readonly Func allowFallback; private ISkinSource skin; /// /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. /// - private readonly bool allowDefaultFallback; + private bool allowDefaultFallback => allowFallback == null || allowFallback.Invoke(skin); /// /// Create a new /// /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. - protected SkinReloadableDrawable(bool fallback = true) + protected SkinReloadableDrawable(Func allowFallback = null) { - allowDefaultFallback = fallback; + this.allowFallback = allowFallback; } [BackgroundDependencyLoader] diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 77af44d5d6..9314d16c39 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -9,8 +9,8 @@ namespace osu.Game.Skinning { public class SkinnableDrawable : SkinnableDrawable { - public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true, bool restrictSize = true) - : base(name, defaultImplementation, fallback, restrictSize) + public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, bool restrictSize = true) + : base(name, defaultImplementation, allowFallback, restrictSize) { } } @@ -31,7 +31,7 @@ namespace osu.Game.Skinning /// A function to create the default skin implementation of this element. /// Whther to fallback to the default implementation when a custom skin is specified but not implementation is present. /// Whether a user-skin drawable should be limited to the size of our parent. - public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true, bool restrictSize = true) : base(fallback) + public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, bool restrictSize = true) : base(allowFallback) { componentName = name; createDefault = defaultImplementation; From 625e561fc8a5b0a35c92908b74eb290fee348efc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 18:01:09 +0900 Subject: [PATCH 206/537] Fix whitespace --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index bdc7c58238..78bf46eb69 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -333,7 +333,7 @@ namespace osu.Game.Database { if (ZipFile.IsZipFile(path)) return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path)); - return new LegacyFilesystemReader(path); + return new LegacyFilesystemReader(path); } } } From 6fd650777ce2709ef4ab0f4ac076c83258ac235d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 20:09:55 +0900 Subject: [PATCH 207/537] Move value change logic to bindable Also add drag support --- .../Visual/TestCaseDrawableBeatDivisor.cs | 4 + .../Screens/Compose/BindableBeatDivisor.cs | 42 ++++++- .../Screens/Compose/DrawableBeatDivisor.cs | 112 ++++++++++-------- 3 files changed, 106 insertions(+), 52 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs b/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs index 238fd09fd8..fa565eaf5d 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Screens.Compose; @@ -10,6 +12,8 @@ namespace osu.Game.Tests.Visual { public class TestCaseDrawableBeatDivisor : OsuTestCase { + public override IReadOnlyList RequiredTypes => new[] { typeof(BindableBeatDivisor) }; + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs index df2521dc10..090f278f39 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs @@ -1,15 +1,55 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Linq; using osu.Framework.Configuration; namespace osu.Game.Screens.Edit.Screens.Compose { - public class BindableBeatDivisor : Bindable + public class BindableBeatDivisor : BindableNumber { + public static readonly int[] VALID_DIVISORS = { 1, 2, 3, 4, 6, 8, 12, 16 }; + public BindableBeatDivisor(int value = 1) : base(value) { } + + public void Next() => Value = VALID_DIVISORS[Math.Min(VALID_DIVISORS.Length - 1, Array.IndexOf(VALID_DIVISORS, Value) + 1)]; + + public void Previous() => Value = VALID_DIVISORS[Math.Max(0, Array.IndexOf(VALID_DIVISORS, Value) - 1)]; + + public override int Value + { + get { return base.Value; } + set + { + int snapped = 1; + + for (int i = 1; i < VALID_DIVISORS.Length; i++) + { + var curr = VALID_DIVISORS[i]; + var prev = VALID_DIVISORS[i - 1]; + if (value < prev + (curr - prev) / 2f) + { + snapped = prev; + break; + } + + snapped = curr; + } + + if (snapped == Value) + // it may be that we are already at the snapped value, but we want bound components to still be made aware that we possibly modified an incoming ValueChanged. + TriggerValueChange(); + else + base.Value = snapped; + } + } + + protected override int DefaultMinValue => VALID_DIVISORS.First(); + protected override int DefaultMaxValue => VALID_DIVISORS.Last(); + protected override int DefaultPrecision => 1; } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs index 08221009cc..35bb93cd4b 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs @@ -1,13 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; 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.UserInterface; using OpenTK; @@ -17,10 +18,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose { public class DrawableBeatDivisor : CompositeDrawable { - private static readonly int[] available_divisors = { 1, 2, 3, 4, 6, 8, 12, 16 }; - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); private int currentDivisorIndex; + private TickSliderBar slider; public DrawableBeatDivisor(BindableBeatDivisor beatDivisor) { @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose { new Drawable[] { - new TickContainer(beatDivisor, 1, 2, 3, 4, 6, 8, 12, 16) + slider = new TickSliderBar(beatDivisor, 1, 2, 3, 4, 6, 8, 12, 16) { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = 5 } @@ -80,13 +80,13 @@ namespace osu.Game.Screens.Edit.Screens.Compose new DivisorButton { Icon = FontAwesome.fa_chevron_left, - Action = selectPrevious + Action = beatDivisor.Previous }, new DivisorText(beatDivisor), new DivisorButton { Icon = FontAwesome.fa_chevron_right, - Action = selectNext + Action = beatDivisor.Next } }, new Drawable[] @@ -118,20 +118,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose } } }; - } - private void selectPrevious() - { - if (currentDivisorIndex == 0) - return; - beatDivisor.Value = available_divisors[--currentDivisorIndex]; - } - - private void selectNext() - { - if (currentDivisorIndex == available_divisors.Length - 1) - return; - beatDivisor.Value = available_divisors[++currentDivisorIndex]; + slider.Current.BindTo(beatDivisor); } private class DivisorText : SpriteText @@ -186,61 +174,46 @@ namespace osu.Game.Screens.Edit.Screens.Compose } } - private class TickContainer : CompositeDrawable + private class TickSliderBar : SliderBar { - private readonly Bindable beatDivisor = new Bindable(); + public new MarginPadding Padding + { + set => base.Padding = value; + } - public new MarginPadding Padding { set => base.Padding = value; } - - private EquilateralTriangle marker; + private Drawable marker; private readonly int[] availableDivisors; - private readonly float tickSpacing; - public TickContainer(BindableBeatDivisor beatDivisor, params int[] divisors) + public TickSliderBar(params int[] divisors) { - this.beatDivisor.BindTo(beatDivisor); - availableDivisors = divisors; - tickSpacing = 1f / (availableDivisors.Length + 1); } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { - InternalChild = marker = new EquilateralTriangle - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomCentre, - RelativePositionAxes = Axes.X, - Height = 7, - EdgeSmoothness = new Vector2(1), - Colour = colours.Gray4, - }; + InternalChild = marker = new Marker(); - for (int i = 0; i < availableDivisors.Length; i++) + foreach (var t in availableDivisors) { - AddInternal(new Tick(availableDivisors[i]) + AddInternal(new Tick(t) { Anchor = Anchor.TopLeft, Origin = Anchor.TopCentre, RelativePositionAxes = Axes.X, - X = getTickPosition(i) + X = getTickPosition(t) }); } + + CurrentNumber.ValueChanged += v => marker.MoveToX(getTickPosition(v), 100, Easing.OutQuint); } - protected override void LoadComplete() + protected override void UpdateValue(float value) { - base.LoadComplete(); - - beatDivisor.ValueChanged += v => updatePosition(); - updatePosition(); } - private void updatePosition() => marker.MoveToX(getTickPosition(Array.IndexOf(availableDivisors, beatDivisor.Value)), 100, Easing.OutQuint); - - private float getTickPosition(int index) => (index + 1) * tickSpacing; + private float getTickPosition(float divisor) => (divisor - 1) / availableDivisors.Last(); private class Tick : Box { @@ -249,7 +222,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose public Tick(int divisor) { this.divisor = divisor; - Size = new Vector2(2, 10); } @@ -264,6 +236,44 @@ namespace osu.Game.Screens.Edit.Screens.Compose Colour = colours.Gray4; } } + + private class Marker : CompositeDrawable + { + private const float size = 7; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Gray4; + Anchor = Anchor.TopLeft; + Origin = Anchor.TopCentre; + + Width = size; + RelativeSizeAxes = Axes.Y; + RelativePositionAxes = Axes.X; + + + InternalChildren = new Drawable[] + { + new Box + { + Width = 1, + RelativeSizeAxes = Axes.Y, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Colour = Color4.White, + }, + new EquilateralTriangle + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Height = size, + EdgeSmoothness = new Vector2(1), + Colour = Color4.White, + } + }; + } + } } } } From c2eb32c0e9337149858613d40b9668fc83163641 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 20:10:27 +0900 Subject: [PATCH 208/537] DrawableBeatDivisor -> BeatDivisorControl --- ...seDrawableBeatDivisor.cs => TestCaseBeatDivisorControl.cs} | 4 ++-- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- .../Compose/{DrawableBeatDivisor.cs => BeatDivisorControl.cs} | 4 ++-- osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game.Tests/Visual/{TestCaseDrawableBeatDivisor.cs => TestCaseBeatDivisorControl.cs} (82%) rename osu.Game/Screens/Edit/Screens/Compose/{DrawableBeatDivisor.cs => BeatDivisorControl.cs} (96%) diff --git a/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs similarity index 82% rename from osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs rename to osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs index fa565eaf5d..598c504ca1 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawableBeatDivisor.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs @@ -10,14 +10,14 @@ using OpenTK; namespace osu.Game.Tests.Visual { - public class TestCaseDrawableBeatDivisor : OsuTestCase + public class TestCaseBeatDivisorControl : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(BindableBeatDivisor) }; [BackgroundDependencyLoader] private void load() { - Child = new DrawableBeatDivisor(new BindableBeatDivisor()) + Child = new BeatDivisorControl(new BindableBeatDivisor()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index b5ab229f8a..e85bbd6f10 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -116,7 +116,7 @@ - + diff --git a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs similarity index 96% rename from osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs rename to osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 35bb93cd4b..98b6a14219 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/DrawableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -16,13 +16,13 @@ using OpenTK.Graphics; namespace osu.Game.Screens.Edit.Screens.Compose { - public class DrawableBeatDivisor : CompositeDrawable + public class BeatDivisorControl : CompositeDrawable { private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); private int currentDivisorIndex; private TickSliderBar slider; - public DrawableBeatDivisor(BindableBeatDivisor beatDivisor) + public BeatDivisorControl(BindableBeatDivisor beatDivisor) { this.beatDivisor.BindTo(beatDivisor); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 2603832437..91adc8324a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose Padding = new MarginPadding { Right = 5 }, Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } }, - new DrawableBeatDivisor(beatDivisor) { RelativeSizeAxes = Axes.Both } + new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } }, }, ColumnDimensions = new[] diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 42f58e9eee..62a26334cc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -381,7 +381,7 @@ - + From 5a10270a2a6257e9d074b0bff91502d112254954 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 20:32:32 +0900 Subject: [PATCH 209/537] return -> break no real reason but whatever works --- osu.Game/Skinning/LegacySkinDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 9a881f9241..853abceddf 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -29,7 +29,7 @@ namespace osu.Game.Skinning break; } - return; + break; } base.ParseLine(output, section, line); From 045610dbfeed0f85928b300506c6285df0558d63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 21:31:17 +0900 Subject: [PATCH 210/537] Adjust styling and colours --- .../Screens/Compose/BeatDivisorControl.cs | 94 ++++++++++++++++--- 1 file changed, 83 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 98b6a14219..61bcc2fb82 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -5,10 +5,12 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; @@ -181,7 +183,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose set => base.Padding = value; } - private Drawable marker; + private Marker marker; private readonly int[] availableDivisors; @@ -193,8 +195,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose [BackgroundDependencyLoader] private void load() { - InternalChild = marker = new Marker(); - foreach (var t in availableDivisors) { AddInternal(new Tick(t) @@ -206,23 +206,69 @@ namespace osu.Game.Screens.Edit.Screens.Compose }); } - CurrentNumber.ValueChanged += v => marker.MoveToX(getTickPosition(v), 100, Easing.OutQuint); + AddInternal(marker = new Marker()); + + CurrentNumber.ValueChanged += v => + { + marker.MoveToX(getTickPosition(v), 100, Easing.OutQuint); + marker.Flash(); + }; } protected override void UpdateValue(float value) { } + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + marker.Active = true; + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + marker.Active = false; + return base.OnMouseUp(state, args); + } + + protected override bool OnClick(InputState state) + { + handleMouseInput(state); + return true; + } + + protected override bool OnDrag(InputState state) + { + handleMouseInput(state); + return true; + } + + private void handleMouseInput(InputState state) + { + // copied from SliderBar so we can do custom spacing logic. + var xPosition = ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding; + + if (!CurrentNumber.Disabled) + CurrentNumber.SetProportional(xPosition / UsableWidth, state != null && state.Keyboard.ShiftPressed ? KeyboardStep : 0); + + OnUserChange(); + } + private float getTickPosition(float divisor) => (divisor - 1) / availableDivisors.Last(); - private class Tick : Box + private class Tick : CompositeDrawable { private readonly int divisor; public Tick(int divisor) { this.divisor = divisor; - Size = new Vector2(2, 10); + Size = new Vector2(2.5f, 10); + + InternalChild = new Box { RelativeSizeAxes = Axes.Both }; + + CornerRadius = 0.5f; + Masking = true; } [BackgroundDependencyLoader] @@ -233,18 +279,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose else if (divisor >= 8) Colour = colours.Yellow; else - Colour = colours.Gray4; + Colour = OsuColour.Gray(2f / divisor); } } private class Marker : CompositeDrawable { + private Color4 defaultColour; + private const float size = 7; [BackgroundDependencyLoader] private void load(OsuColour colours) { - Colour = colours.Gray4; + Colour = defaultColour = colours.Gray4; Anchor = Anchor.TopLeft; Origin = Anchor.TopCentre; @@ -252,16 +300,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose RelativeSizeAxes = Axes.Y; RelativePositionAxes = Axes.X; - InternalChildren = new Drawable[] { new Box { - Width = 1, + Width = 2, RelativeSizeAxes = Axes.Y, Origin = Anchor.BottomCentre, Anchor = Anchor.BottomCentre, - Colour = Color4.White, + Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White), + Blending = BlendingMode.Additive, }, new EquilateralTriangle { @@ -273,6 +321,30 @@ namespace osu.Game.Screens.Edit.Screens.Compose } }; } + + private bool active; + + public bool Active + { + get => active; + set + { + this.FadeColour(value ? Color4.White : defaultColour, 500, Easing.OutQuint); + active = value; + } + } + + public void Flash() + { + bool wasActive = active; + + Active = true; + + if (wasActive) return; + + using (BeginDelayedSequence(50)) + Active = false; + } } } } From dac1ba21db339adfa3a363fefc25c69f9fe2d6bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 21:38:22 +0900 Subject: [PATCH 211/537] Make keyboard control work --- osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 61bcc2fb82..00054c0039 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -212,6 +212,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose { marker.MoveToX(getTickPosition(v), 100, Easing.OutQuint); marker.Flash(); + + KeyboardStep = v / 3f; }; } From aa8b0d017cfd02826974227defafcb85a34ab07a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 21:59:32 +0900 Subject: [PATCH 212/537] Adjust colours and spacing --- .../Screens/Compose/BeatDivisorControl.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 00054c0039..64f77a2dea 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -1,9 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -219,6 +221,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose protected override void UpdateValue(float value) { + } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) @@ -256,7 +259,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose OnUserChange(); } - private float getTickPosition(float divisor) => (divisor - 1) / availableDivisors.Last(); + private float getTickPosition(float divisor) => (float)Math.Pow((divisor - 1) / availableDivisors.Last(), 0.90f); + private class Tick : CompositeDrawable { @@ -276,12 +280,18 @@ namespace osu.Game.Screens.Edit.Screens.Compose [BackgroundDependencyLoader] private void load(OsuColour colours) { + Color4 colour; + if (divisor >= 16) - Colour = colours.Red; - else if (divisor >= 8) - Colour = colours.Yellow; + colour = colours.Red; + else if (divisor >= 12) + colour = colours.YellowDarker; + else if (divisor % 3 == 0) + colour = colours.Yellow; else - Colour = OsuColour.Gray(2f / divisor); + colour = Color4.White; + +s Colour = colour.Opacity((float)Math.Pow(0.98f, divisor * 1.2f)); } } From 8ac660b0c6ef4c6c1fb0906f9a7ed5d731adc2b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Mar 2018 22:25:15 +0900 Subject: [PATCH 213/537] Fixes for keyboard handling and general code quality --- .../Screens/Compose/BeatDivisorControl.cs | 55 +++++++++++-------- .../Screens/Compose/BindableBeatDivisor.cs | 22 +------- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 64f77a2dea..5d2991ca7d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; using OpenTK.Graphics; +using OpenTK.Input; namespace osu.Game.Screens.Edit.Screens.Compose { @@ -24,8 +25,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose { private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); private int currentDivisorIndex; - private TickSliderBar slider; - public BeatDivisorControl(BindableBeatDivisor beatDivisor) { this.beatDivisor.BindTo(beatDivisor); @@ -52,10 +51,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose { new Drawable[] { - slider = new TickSliderBar(beatDivisor, 1, 2, 3, 4, 6, 8, 12, 16) + new TickSliderBar(beatDivisor, 1, 2, 3, 4, 6, 8, 12, 16) { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 5 } } }, new Drawable[] @@ -122,8 +120,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose } } }; - - slider.Current.BindTo(beatDivisor); } private class DivisorText : SpriteText @@ -180,18 +176,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose private class TickSliderBar : SliderBar { - public new MarginPadding Padding - { - set => base.Padding = value; - } - private Marker marker; + private readonly BindableBeatDivisor beatDivisor; private readonly int[] availableDivisors; - public TickSliderBar(params int[] divisors) + public TickSliderBar(BindableBeatDivisor beatDivisor, params int[] divisors) { + CurrentNumber.BindTo(this.beatDivisor = beatDivisor); availableDivisors = divisors; + + Padding = new MarginPadding { Horizontal = 5 }; } [BackgroundDependencyLoader] @@ -204,7 +199,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose Anchor = Anchor.TopLeft, Origin = Anchor.TopCentre, RelativePositionAxes = Axes.X, - X = getTickPosition(t) + X = getMappedPosition(t) }); } @@ -212,16 +207,33 @@ namespace osu.Game.Screens.Edit.Screens.Compose CurrentNumber.ValueChanged += v => { - marker.MoveToX(getTickPosition(v), 100, Easing.OutQuint); + marker.MoveToX(getMappedPosition(v), 100, Easing.OutQuint); marker.Flash(); - - KeyboardStep = v / 3f; }; } protected override void UpdateValue(float value) { + } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!IsHovered || CurrentNumber.Disabled) + return false; + + switch (args.Key) + { + case Key.Right: + beatDivisor.Next(); + OnUserChange(); + return true; + case Key.Left: + beatDivisor.Previous(); + OnUserChange(); + return true; + default: + return false; + } } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) @@ -251,16 +263,13 @@ namespace osu.Game.Screens.Edit.Screens.Compose private void handleMouseInput(InputState state) { // copied from SliderBar so we can do custom spacing logic. - var xPosition = ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding; - - if (!CurrentNumber.Disabled) - CurrentNumber.SetProportional(xPosition / UsableWidth, state != null && state.Keyboard.ShiftPressed ? KeyboardStep : 0); + var xPosition = (ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding) / UsableWidth; + CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First(); OnUserChange(); } - private float getTickPosition(float divisor) => (float)Math.Pow((divisor - 1) / availableDivisors.Last(), 0.90f); - + private float getMappedPosition(float divisor) => (float)Math.Pow((divisor - 1) / (availableDivisors.Last() - 1), 0.90f); private class Tick : CompositeDrawable { @@ -291,7 +300,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose else colour = Color4.White; -s Colour = colour.Opacity((float)Math.Pow(0.98f, divisor * 1.2f)); + Colour = colour.Opacity((float)Math.Pow(0.98f, divisor * 1.2f)); } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs index 090f278f39..8eb3f1347e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs @@ -25,26 +25,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose get { return base.Value; } set { - int snapped = 1; + if (!VALID_DIVISORS.Contains(value)) + throw new ArgumentOutOfRangeException($"Provided divisor is not in {nameof(VALID_DIVISORS)}"); - for (int i = 1; i < VALID_DIVISORS.Length; i++) - { - var curr = VALID_DIVISORS[i]; - var prev = VALID_DIVISORS[i - 1]; - if (value < prev + (curr - prev) / 2f) - { - snapped = prev; - break; - } - - snapped = curr; - } - - if (snapped == Value) - // it may be that we are already at the snapped value, but we want bound components to still be made aware that we possibly modified an incoming ValueChanged. - TriggerValueChange(); - else - base.Value = snapped; + base.Value = value; } } From ccdd11d7b22be419ab34230150c342e4e1550c08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 12:29:44 +0900 Subject: [PATCH 214/537] Add missing licence header --- osu.Game/Graphics/ScreenshotManager.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 8028b744c9..3dc4fec63a 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; using System.Drawing.Imaging; using System.IO; using osu.Framework.Allocation; @@ -43,7 +46,10 @@ namespace osu.Game.Graphics return false; } - public bool OnReleased(GlobalAction action) => false; + public bool OnReleased(GlobalAction action) + { + return false; + } public async void TakeScreenshotAsync() { From 357a3c535e95c37387d08e6936451c0e3c683a9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 12:29:49 +0900 Subject: [PATCH 215/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index d29c8365ba..140f9c4ccf 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d29c8365ba3cf7924b57cf22341f4af55658764c +Subproject commit 140f9c4ccfc8a6ea464ee29ebfb4f6fdc66b0db7 From 63f0419d6a897e51fd5513d8df145a4044cc03f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 15:40:43 +0900 Subject: [PATCH 216/537] Apply minor fixes --- .../Screens/Edit/Screens/Compose/BeatDivisorControl.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 5d2991ca7d..9fc5896ccd 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose { new Drawable[] { - new TickSliderBar(beatDivisor, 1, 2, 3, 4, 6, 8, 12, 16) + new TickSliderBar(beatDivisor, BindableBeatDivisor.VALID_DIVISORS) { RelativeSizeAxes = Axes.Both, } @@ -216,11 +216,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose { } + public override bool HandleKeyboardInput => IsHovered && !CurrentNumber.Disabled; + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - if (!IsHovered || CurrentNumber.Disabled) - return false; - switch (args.Key) { case Key.Right: From 48142602eae923225e8402c1b3cb6e91347b6edc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 16:04:43 +0900 Subject: [PATCH 217/537] Adjust text size to match design --- .../Screens/Compose/BeatDivisorControl.cs | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 9fc5896ccd..adeb6c7789 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -25,6 +25,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose { private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); private int currentDivisorIndex; + public BeatDivisorControl(BindableBeatDivisor beatDivisor) { this.beatDivisor.BindTo(beatDivisor); @@ -40,9 +41,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose { new Box { - Name = "Background", + Name = "Gray Background", RelativeSizeAxes = Axes.Both, - Colour = Color4.Black + Colour = colours.Gray4 }, new GridContainer { @@ -51,9 +52,22 @@ namespace osu.Game.Screens.Edit.Screens.Compose { new Drawable[] { - new TickSliderBar(beatDivisor, BindableBeatDivisor.VALID_DIVISORS) + new Container { RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Name = "Black Background", + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + new TickSliderBar(beatDivisor, BindableBeatDivisor.VALID_DIVISORS) + { + RelativeSizeAxes = Axes.Both, + } + } } }, new Drawable[] @@ -91,16 +105,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose Action = beatDivisor.Next } }, - new Drawable[] - { - null, - new TextFlowContainer(s => s.TextSize = 10) - { - Text = "beat snap divisor", - RelativeSizeAxes = Axes.X, - TextAnchor = Anchor.TopCentre - }, - }, }, ColumnDimensions = new[] { @@ -113,6 +117,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose } } }, + new Drawable[] + { + new TextFlowContainer(s => s.TextSize = 14) + { + Padding = new MarginPadding { Horizontal = 15 }, + Text = "beat snap divisor", + RelativeSizeAxes = Axes.X, + TextAnchor = Anchor.TopCentre + }, + } }, RowDimensions = new[] { From 271c0826062349ae550a6ea3447472484541ae5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 16:17:09 +0900 Subject: [PATCH 218/537] Adjust colour assignment --- .../Screens/Compose/BeatDivisorControl.cs | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index adeb6c7789..796bfd96c4 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -302,18 +302,30 @@ namespace osu.Game.Screens.Edit.Screens.Compose [BackgroundDependencyLoader] private void load(OsuColour colours) { - Color4 colour; + Colour = getColourForDivisor(divisor, colours); + } - if (divisor >= 16) - colour = colours.Red; - else if (divisor >= 12) - colour = colours.YellowDarker; - else if (divisor % 3 == 0) - colour = colours.Yellow; - else - colour = Color4.White; - - Colour = colour.Opacity((float)Math.Pow(0.98f, divisor * 1.2f)); + private ColourInfo getColourForDivisor(int divisor, OsuColour colours) + { + switch (divisor) + { + case 2: + return colours.BlueLight; + case 4: + return colours.Blue; + case 8: + return colours.BlueDarker; + case 16: + return colours.PurpleDark; + case 3: + return colours.YellowLight; + case 6: + return colours.Yellow; + case 12: + return colours.YellowDarker; + default: + return Color4.White; + } } } @@ -342,7 +354,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose RelativeSizeAxes = Axes.Y, Origin = Anchor.BottomCentre, Anchor = Anchor.BottomCentre, - Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White), + Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.2f), Color4.White), Blending = BlendingMode.Additive, }, new EquilateralTriangle From 65b6a2faad7437e4d14a9084b91e90e3611c4b5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 18:40:45 +0900 Subject: [PATCH 219/537] Fix mod button sounds playing when mod overlay is not visible Resolves #2240. --- osu.Game/Overlays/Mods/ModButton.cs | 12 ------------ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 91063bfa38..a4cc79bca6 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -4,9 +4,6 @@ using OpenTK; using OpenTK.Graphics; using OpenTK.Input; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -30,7 +27,6 @@ namespace osu.Game.Overlays.Mods private ModIcon backgroundIcon; private readonly SpriteText text; private readonly Container iconsContainer; - private SampleChannel sampleOn, sampleOff; /// /// Fired when the selection changes. @@ -100,7 +96,6 @@ namespace osu.Game.Overlays.Mods foregroundIcon.Highlighted = Selected; - (selectedIndex == -1 ? sampleOff : sampleOn).Play(); SelectionChanged?.Invoke(SelectedMod); return true; } @@ -152,13 +147,6 @@ namespace osu.Game.Overlays.Mods public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleOn = audio.Sample.Get(@"UI/check-on"); - sampleOff = audio.Sample.Get(@"UI/check-off"); - } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { switch (args.Button) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index fe7b7bae99..d8c95da94f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -15,6 +15,8 @@ using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets; using osu.Game.Graphics.UserInterface; @@ -49,7 +51,7 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets) + private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets, AudioManager audio) { SelectedMods.ValueChanged += selectedModsChanged; @@ -63,6 +65,9 @@ namespace osu.Game.Overlays.Mods Ruleset.ValueChanged += rulesetChanged; Ruleset.TriggerChange(); + + sampleOn = audio.Sample.Get(@"UI/check-on"); + sampleOff = audio.Sample.Get(@"UI/check-off"); } protected override void Dispose(bool isDisposing) @@ -154,10 +159,21 @@ namespace osu.Game.Overlays.Mods section.DeselectTypes(modTypes, immediate); } + + private SampleChannel sampleOn, sampleOff; + private void modButtonPressed(Mod selectedMod) { if (selectedMod != null) + { + if (State == Visibility.Visible) sampleOn?.Play(); DeselectTypes(selectedMod.IncompatibleMods, true); + } + else + { + if (State == Visibility.Visible) sampleOff?.Play(); + } + refreshSelectedMods(); } From d909cce8d6b1f5e47da00d762d795c701b28e4af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 20:06:10 +0900 Subject: [PATCH 220/537] Add the ability to skin the gameplay cursor --- .../UI/Cursor/GameplayCursor.cs | 117 +++++++++--------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 34940a084a..ac81d93309 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Skinning; using OpenTK; using OpenTK.Graphics; @@ -82,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public class OsuCursor : Container { - private Container cursorContainer; + private Drawable cursorContainer; private Bindable cursorScale; private Bindable autoCursorScale; @@ -97,66 +98,66 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [BackgroundDependencyLoader] private void load(OsuConfigManager config, OsuGameBase game) { - Children = new Drawable[] + Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer { - cursorContainer = new CircularContainer + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = Size.X / 6, + BorderColour = Color4.White, + EdgeEffect = new EdgeEffectParameters { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = Size.X / 6, - BorderColour = Color4.White, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Pink.Opacity(0.5f), - Radius = 5, - }, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = Size.X / 3, - BorderColour = Color4.White.Opacity(0.5f), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - }, - }, - new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.1f), - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - }, - }, - }, - } + Type = EdgeEffectType.Shadow, + Colour = Color4.Pink.Opacity(0.5f), + Radius = 5, }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = Size.X / 3, + BorderColour = Color4.White.Opacity(0.5f), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + }, + }, + new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.1f), + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + }, + }, + } + }, restrictSize: false) + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, }; beatmap = game.Beatmap.GetBoundCopy(); From 4a93abcc36c9ec8e108892c0f3f257a66e6ffce0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 20:06:36 +0900 Subject: [PATCH 221/537] Add scale adjusts to bring legacy skins in-line with lazer sizing --- osu.Game/Skinning/LegacySkin.cs | 20 +++++++++++++++++--- osu.Game/Skinning/SkinnableDrawable.cs | 1 + 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index c543537f32..a361fa2b8a 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Database; +using OpenTK; namespace osu.Game.Skinning { @@ -35,7 +36,8 @@ namespace osu.Game.Skinning Configuration = new SkinConfiguration(); } - protected LegacySkin(SkinInfo skin) : base(skin) + protected LegacySkin(SkinInfo skin) + : base(skin) { } @@ -57,10 +59,22 @@ namespace osu.Game.Skinning break; } - var texture = GetTexture(componentName); + float ratio = 0.72f; // brings sizing roughly in-line with stable + + var texture = GetTexture($"{componentName}@2x"); + if (texture == null) + { + ratio *= 2; + GetTexture(componentName); + } + if (texture == null) return null; - return new Sprite { Texture = texture }; + return new Sprite + { + Texture = texture, + Scale = new Vector2(ratio), + }; } public override Texture GetTexture(string componentName) => Textures.Get(componentName); diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 9314d16c39..09d2e6a3ed 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -49,6 +49,7 @@ namespace osu.Game.Skinning { drawable.RelativeSizeAxes = Axes.Both; drawable.Size = Vector2.One; + drawable.Scale = Vector2.One; drawable.FillMode = FillMode.Fit; } } From e67d8e13541c388310389a3057cf0bb134e09994 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Mar 2018 22:31:52 +0900 Subject: [PATCH 222/537] Update nuspec to include humanizer localisations --- osu.Desktop/osu.nuspec | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec index bb7d382cee..316a5443ef 100644 --- a/osu.Desktop/osu.nuspec +++ b/osu.Desktop/osu.nuspec @@ -16,11 +16,9 @@ en-AU - - - - - + + + From 62229300c4b398967ea6df6953bddfe9d9102f46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 00:10:49 +0900 Subject: [PATCH 223/537] Fix DummyRuleset getting selected Resolves #2261. --- osu.Game/Screens/Select/SongSelect.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index ca8a1cae41..f01616ade2 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -259,6 +259,8 @@ namespace osu.Game.Screens.Select private void workingBeatmapChanged(WorkingBeatmap beatmap) { + if (beatmap is DummyWorkingBeatmap) return; + if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false)) // If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value) From 4991f2ad2e28ea4bcb295b8da4518a1ffd6e8355 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Wed, 21 Mar 2018 18:27:08 +0300 Subject: [PATCH 224/537] Change filename format --- osu.Game/Graphics/ScreenshotManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 3dc4fec63a..4c9e9756df 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -76,15 +76,16 @@ namespace osu.Game.Graphics private string getFileName() { + var dt = DateTime.Now; var fileExt = screenshotFormat.ToString().ToLower(); - var withoutIndex = $"Screenshot.{fileExt}"; + var withoutIndex = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}.{fileExt}"; if (!storage.Exists(withoutIndex)) return withoutIndex; for (ulong i = 1; i < ulong.MaxValue; i++) { - var indexedName = $"Screenshot-{i}.{fileExt}"; + var indexedName = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}-{i}.{fileExt}"; if (!storage.Exists(indexedName)) return indexedName; } From 8f71d53c17c696a160a482d64a4f61e59635fda5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 02:10:31 +0900 Subject: [PATCH 225/537] Fix retry not allowing continuing beyond PlayerLoader Resolves #2265. --- osu.Game/Screens/Play/PlayerLoader.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 31e7313c0b..ca36689b76 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -149,6 +149,7 @@ namespace osu.Game.Screens.Play pushDebounce = Scheduler.AddDelayed(() => { + pushDebounce = null; contentOut(); this.Delay(250).Schedule(() => From e0b74a357ab0fcc2c24647338014db2915de4e6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 03:30:14 +0900 Subject: [PATCH 226/537] Fix osu! logo making hover beat sounds when not visible --- osu.Game/Screens/Menu/OsuLogo.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 3fcb885655..b7d2ed2e1f 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -337,12 +337,10 @@ namespace osu.Game.Screens.Menu } } - private bool interactive => Action != null && Alpha > 0.2f; + public override bool HandleMouseInput => base.HandleMouseInput && Action != null && Alpha > 0.2f; protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - if (!interactive) return false; - logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out); return true; } @@ -355,8 +353,6 @@ namespace osu.Game.Screens.Menu protected override bool OnClick(InputState state) { - if (!interactive) return false; - if (Action?.Invoke() ?? true) sampleClick.Play(); @@ -368,8 +364,6 @@ namespace osu.Game.Screens.Menu protected override bool OnHover(InputState state) { - if (!interactive) return false; - logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic); return true; } From 101caf3064de36bf379f59adacdc102e54744935 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 11:28:40 +0900 Subject: [PATCH 227/537] Apply same logic fixes to delete/restore all notifications --- osu.Game/Database/ArchiveModelManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 78bf46eb69..f781beb443 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -220,8 +220,8 @@ namespace osu.Game.Database // user requested abort return; - notification.Text = $"Deleting ({i} of {items.Count})"; - notification.Progress = (float)++i / items.Count; + notification.Progress = (float)i / items.Count; + notification.Text = $"Deleting ({++i} of {items.Count})"; Delete(b); } } @@ -256,8 +256,8 @@ namespace osu.Game.Database // user requested abort return; - notification.Text = $"Restoring ({i} of {items.Count})"; - notification.Progress = (float)++i / items.Count; + notification.Progress = (float)i / items.Count; + notification.Text = $"Restoring ({++i} of {items.Count})"; Undelete(item); } } From 1a782a840cd15d5be3a1192e479d99be111c7692 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 12:34:29 +0900 Subject: [PATCH 228/537] Fix xmldoc --- osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs b/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs index 68474a6e2c..dae10e4552 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Objects.Types int IndexInCurrentCombo { get; set; } /// - /// The offset of this hitobject in the current combo. + /// The offset of this combo in relation to the beatmap. /// int ComboIndex { get; set; } From 78a8f60b39114518f65483e47557da6625e0f22a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 12:35:17 +0900 Subject: [PATCH 229/537] IHasComboIndex -> IHasComboInformation --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- osu.Game/Beatmaps/BeatmapProcessor.cs | 4 ++-- .../Types/{IHasComboIndex.cs => IHasComboInformation.cs} | 2 +- osu.Game/osu.Game.csproj | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game/Rulesets/Objects/Types/{IHasComboIndex.cs => IHasComboInformation.cs} (91%) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index a6ab18bbf7..1a0ccc9b1e 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -8,7 +8,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Catch.Objects { - public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboIndex + public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation { public const double OBJECT_RADIUS = 44; diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 5d1908fa6e..c00c30ced9 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -10,7 +10,7 @@ using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Osu.Objects { - public abstract class OsuHitObject : HitObject, IHasComboIndex, IHasPosition + public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition { public const double OBJECT_RADIUS = 64; diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index 83b2867df7..f2cc419043 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -23,9 +23,9 @@ namespace osu.Game.Beatmaps /// The Beatmap to process. public virtual void PostProcess(Beatmap beatmap) { - IHasComboIndex lastObj = null; + IHasComboInformation lastObj = null; - foreach (var obj in beatmap.HitObjects.OfType()) + foreach (var obj in beatmap.HitObjects.OfType()) { if (obj.NewCombo) { diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs similarity index 91% rename from osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs rename to osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index dae10e4552..1d4f4e0f90 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -6,7 +6,7 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that is part of a combo and has extended information about its position relative to other combo objects. /// - public interface IHasComboIndex : IHasCombo + public interface IHasComboInformation : IHasCombo { /// /// The offset of this hitobject in the current combo. diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5ece1a18ac..19914bb5cb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -374,7 +374,7 @@ - + From c5eecae32fb7da44ccac7a921bb07398bad4f80b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 12:50:23 +0900 Subject: [PATCH 230/537] Improve shared code paths in legacy skins --- osu.Game/Skinning/BeatmapSkin.cs | 19 ++++--------------- osu.Game/Skinning/LegacySkin.cs | 15 +++++++-------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs index 815aac2f64..02278aa31e 100644 --- a/osu.Game/Skinning/BeatmapSkin.cs +++ b/osu.Game/Skinning/BeatmapSkin.cs @@ -1,9 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.IO; using osu.Framework.Audio; -using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Beatmaps; @@ -12,20 +10,11 @@ namespace osu.Game.Skinning public class BeatmapSkin : LegacySkin { public BeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager) - : base(new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }) + : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), audioManager, beatmap.Path) { - storage = new LegacySkinResourceStore(beatmap.BeatmapSet, storage); - - Samples = audioManager.GetSampleManager(storage); - - Textures = new TextureStore(new RawTextureLoaderStore(storage)); - - var decoder = new LegacySkinDecoder(); - - using (StreamReader reader = new StreamReader(storage.GetStream(beatmap.Path))) - { - Configuration = decoder.Decode(reader); - } } + + private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => + new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }; } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index cfee2cfab2..64f65cd08c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -21,22 +21,21 @@ namespace osu.Game.Skinning protected SampleManager Samples; public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) - : this(skin) + : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") { - storage = new LegacySkinResourceStore(skin, storage); - Samples = audioManager.GetSampleManager(storage); - Textures = new TextureStore(new RawTextureLoaderStore(storage)); + } - Stream stream = storage.GetStream("skin.ini"); + protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) : base(skin) + { + Stream stream = storage.GetStream(filename); if (stream != null) using (StreamReader reader = new StreamReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); else Configuration = new SkinConfiguration(); - } - protected LegacySkin(SkinInfo skin) : base(skin) - { + Samples = audioManager.GetSampleManager(storage); + Textures = new TextureStore(new RawTextureLoaderStore(storage)); } public override Drawable GetDrawableComponent(string componentName) From 504c3ff0baa1f71456cdc626289a2510a114ca51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 12:50:48 +0900 Subject: [PATCH 231/537] BeatmapSkin -> LegacyBeatmapSkin --- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 +- osu.Game/Skinning/{BeatmapSkin.cs => LegacyBeatmapSkin.cs} | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Skinning/{BeatmapSkin.cs => LegacyBeatmapSkin.cs} (77%) diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 58b51085a4..5874314f75 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -110,7 +110,7 @@ namespace osu.Game.Beatmaps Skin skin; try { - skin = new BeatmapSkin(BeatmapInfo, store, audioManager); + skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager); } catch (Exception e) { diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs similarity index 77% rename from osu.Game/Skinning/BeatmapSkin.cs rename to osu.Game/Skinning/LegacyBeatmapSkin.cs index 02278aa31e..01beb8db32 100644 --- a/osu.Game/Skinning/BeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -7,9 +7,9 @@ using osu.Game.Beatmaps; namespace osu.Game.Skinning { - public class BeatmapSkin : LegacySkin + public class LegacyBeatmapSkin : LegacySkin { - public BeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager) + public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager) : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), audioManager, beatmap.Path) { } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 50c272e827..3b4f46a918 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -871,7 +871,7 @@ - + From e39b7b8d354463d849de7b9d30feda00eb1a1fd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 12:58:02 +0900 Subject: [PATCH 232/537] Don't hard-crash when an unknown section is encountered in a legacy file --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 67d497ba83..131c010c5c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using osu.Framework.Logging; using OpenTK.Graphics; namespace osu.Game.Beatmaps.Formats @@ -31,7 +32,11 @@ namespace osu.Game.Beatmaps.Formats if (line.StartsWith(@"[") && line.EndsWith(@"]")) { if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - throw new InvalidDataException($@"Unknown osu section {line}"); + { + Logger.Log($"Unknown section \"{line}\" in {beatmap}"); + section = Section.None; + } + continue; } From 917334a39ab3a0eda594e5178470112450e403e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 13:01:40 +0900 Subject: [PATCH 233/537] Move cancel code to exit paths for screen --- osu.Game/Screens/Play/PlayerLoader.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ca36689b76..89082cfbd5 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -139,8 +139,7 @@ namespace osu.Game.Screens.Play { // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce // if we become unready for push during the delay. - pushDebounce?.Cancel(); - pushDebounce = null; + cancelLoad(); return; } @@ -149,7 +148,6 @@ namespace osu.Game.Screens.Play pushDebounce = Scheduler.AddDelayed(() => { - pushDebounce = null; contentOut(); this.Delay(250).Schedule(() => @@ -173,10 +171,23 @@ namespace osu.Game.Screens.Play } } + private void cancelLoad() + { + pushDebounce?.Cancel(); + pushDebounce = null; + } + + protected override void OnSuspending(Screen next) + { + base.OnSuspending(next); + cancelLoad(); + } + protected override bool OnExiting(Screen next) { Content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); + cancelLoad(); return base.OnExiting(next); } From dc9fb84e254550c0069d61a7137f80ba96bb10d8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Mar 2018 14:46:25 +0900 Subject: [PATCH 234/537] Update progress with the current item, not the next item In the case where there is no next item, the progress will not get updated, so we'll essentially skip one element from filling the progress bar further. In the future we may/will want to not hide the notification upon completion, so this will look better in such scenarios. --- osu.Game/Database/ArchiveModelManager.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index f781beb443..ef85f691d6 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -101,7 +101,7 @@ namespace osu.Game.Database using (ArchiveReader reader = getReaderFrom(path)) imported.Add(Import(reader)); - notification.Progress = (float)(current - 1) / paths.Length; + notification.Progress = (float)current / paths.Length; // We may or may not want to delete the file depending on where it is stored. // e.g. reconstructing/repairing database with items from default storage. @@ -220,9 +220,11 @@ namespace osu.Game.Database // user requested abort return; - notification.Progress = (float)i / items.Count; notification.Text = $"Deleting ({++i} of {items.Count})"; + Delete(b); + + notification.Progress = (float)i / items.Count; } } @@ -256,9 +258,11 @@ namespace osu.Game.Database // user requested abort return; - notification.Progress = (float)i / items.Count; notification.Text = $"Restoring ({++i} of {items.Count})"; + Undelete(item); + + notification.Progress = (float)i / items.Count; } } From 3f3f57e51ef6f4506666aaf9b2ca5f839b0c7acc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Mar 2018 15:13:38 +0900 Subject: [PATCH 235/537] Use cached vertexbatch add delegate --- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- osu.Game/Screens/Menu/LogoVisualisation.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 6f9d83473f..89ed8044e6 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -242,7 +242,7 @@ namespace osu.Game.Graphics.Backgrounds triangle, colourInfo, null, - Shared.VertexBatch.Add, + Shared.VertexBatch.AddAction, Vector2.Divide(localInflationAmount, size)); } diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 3a3f3d4650..fc747acbb4 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -211,7 +211,7 @@ namespace osu.Game.Screens.Menu rectangle, colourInfo, null, - Shared.VertexBatch.Add, + Shared.VertexBatch.AddAction, //barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. Vector2.Divide(inflation, barSize.Yx)); } From 799b7caf7e969243b493d1192c2180bcea04eb93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 15:20:58 +0900 Subject: [PATCH 236/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 41e2a0a430..cc39713fbf 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 41e2a0a4304544fb67779c21cad1435c105982d5 +Subproject commit cc39713fbf9427aa53df91e27ecd09d15661089f From 4ccaf143b5408d4c2cf1d01b8c0133e27e368033 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Mar 2018 15:50:19 +0900 Subject: [PATCH 237/537] Give sliders a default accent colour --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 26186a0049..c59c22c771 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public double? SnakedStart { get; private set; } public double? SnakedEnd { get; private set; } - private Color4 accentColour; + private Color4 accentColour = Color4.White; /// /// Used to colour the path. /// From 6e379f0646cb531798fa24243dc3a5e273cfd72c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 15:54:16 +0900 Subject: [PATCH 238/537] Fix FocusedTextBox in line with framework changes --- osu.Game/Graphics/UserInterface/FocusedTextBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 6d9bf231c3..9ff64e5d5f 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -30,6 +30,8 @@ namespace osu.Game.Graphics.UserInterface } } + public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput; + protected override void OnFocus(InputState state) { base.OnFocus(state); From 1b116dd04e6393b523ba4e20896ed2c44d7fd948 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 16:11:56 +0900 Subject: [PATCH 239/537] Adjust testcase sizing to match editor --- osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs index 598c504ca1..04a662426f 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs @@ -21,8 +21,7 @@ namespace osu.Game.Tests.Visual { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Y = -200, - Size = new Vector2(100, 110) + Size = new Vector2(90, 90) }; } } From dade52d15d7a1776b196d9b9e9ee7a68c70d2ce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 16:15:14 +0900 Subject: [PATCH 240/537] Fix vertical padding --- osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 796bfd96c4..8ba3500eea 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -131,6 +131,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, 35), + new Dimension(GridSizeMode.Absolute, 25), } } }; From c4fe6a04c555574f7f844a37432aeabf32e6dd68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 17:32:05 +0900 Subject: [PATCH 241/537] Use string lookups for combo colours --- .../Objects/Drawables/DrawableHitObject.cs | 2 +- osu.Game/Skinning/ISkinSource.cs | 3 +-- .../Skinning/LocalSkinOverrideContainer.cs | 3 +-- osu.Game/Skinning/Skin.cs | 22 ++++++++++++++++--- osu.Game/Skinning/SkinManager.cs | 3 +-- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 6aa7ddf787..b8852bb141 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Objects.Drawables base.SkinChanged(skin, allowFallback); if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetComboColour(combo) ?? Color4.White; + AccentColour = skin.GetColour($"Play/Combo/{combo.ComboIndex}") ?? Color4.White; } protected override void LoadComplete() diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs index 9911ee3a95..5292ff3cb5 100644 --- a/osu.Game/Skinning/ISkinSource.cs +++ b/osu.Game/Skinning/ISkinSource.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; namespace osu.Game.Skinning @@ -23,6 +22,6 @@ namespace osu.Game.Skinning SampleChannel GetSample(string sampleName); - Color4? GetComboColour(IHasComboInformation comboObject); + Color4? GetColour(string colourName); } } diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index f13e5ed353..cebdc85955 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; namespace osu.Game.Skinning @@ -22,7 +21,7 @@ namespace osu.Game.Skinning public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName); - public Color4? GetComboColour(IHasComboInformation comboObject) => source.GetComboColour(comboObject) ?? fallbackSource?.GetComboColour(comboObject); + public Color4? GetColour(string colourName) => source.GetColour(colourName) ?? fallbackSource?.GetColour(colourName); private readonly ISkinSource source; private ISkinSource fallbackSource; diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 641d1d6f8b..6d5196e1e7 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; namespace osu.Game.Skinning @@ -24,8 +23,25 @@ namespace osu.Game.Skinning public abstract Texture GetTexture(string componentName); - public virtual Color4? GetComboColour(IHasComboInformation comboObject) => - Configuration.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[comboObject.ComboIndex % Configuration.ComboColours.Count]; + public virtual Color4? GetColour(string colourName) + { + var namespaces = colourName.Split('/'); + + switch (namespaces[0]) + { + case "Play": + switch (namespaces[1]) + { + case "Combo": + int index = int.Parse(namespaces[2]); + return Configuration.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[index % Configuration.ComboColours.Count]; + } + + break; + } + + return null; + } protected Skin(SkinInfo skin) { diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index ee11c15dbf..654260daca 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Platform; using osu.Game.Database; using osu.Game.IO.Archives; -using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; namespace osu.Game.Skinning @@ -124,6 +123,6 @@ namespace osu.Game.Skinning public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName); - public Color4? GetComboColour(IHasComboInformation comboObject) => CurrentSkin.Value.GetComboColour(comboObject); + public Color4? GetColour(string colourName) => CurrentSkin.Value.GetColour(colourName); } } From 425d4aa7661ec131640fca29545de791ea496b81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 18:10:28 +0900 Subject: [PATCH 242/537] Add ability to lookup arbitrary SkinConfiguration values --- osu.Game/Skinning/ISkinSource.cs | 4 ++++ .../Skinning/LocalSkinOverrideContainer.cs | 20 +++++++++++++++++++ osu.Game/Skinning/Skin.cs | 8 +++++++- osu.Game/Skinning/SkinManager.cs | 6 +++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs index 5292ff3cb5..ae2909327e 100644 --- a/osu.Game/Skinning/ISkinSource.cs +++ b/osu.Game/Skinning/ISkinSource.cs @@ -23,5 +23,9 @@ namespace osu.Game.Skinning SampleChannel GetSample(string sampleName); Color4? GetColour(string colourName); + + TValue GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : class; + + TValue? GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : struct; } } diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index cebdc85955..8181ec0ea1 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -23,6 +23,26 @@ namespace osu.Game.Skinning public Color4? GetColour(string colourName) => source.GetColour(colourName) ?? fallbackSource?.GetColour(colourName); + public TValue? GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : struct + { + TValue? val = null; + var conf = (source as Skin)?.Configuration as TConfiguration; + if (conf != null) + val = query?.Invoke(conf); + + return val ?? fallbackSource?.GetConfiguration(query); + } + + public TValue GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : class + { + TValue val = null; + var conf = (source as Skin)?.Configuration as TConfiguration; + if (conf != null) + val = query?.Invoke(conf); + + return val ?? fallbackSource?.GetConfiguration(query); + } + private readonly ISkinSource source; private ISkinSource fallbackSource; diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 6d5196e1e7..1c175ea4e9 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -34,7 +34,7 @@ namespace osu.Game.Skinning { case "Combo": int index = int.Parse(namespaces[2]); - return Configuration.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[index % Configuration.ComboColours.Count]; + return GetConfiguration(s => s.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[index % Configuration.ComboColours.Count]); } break; @@ -43,6 +43,12 @@ namespace osu.Game.Skinning return null; } + public TValue GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : class + => Configuration is TConfiguration conf ? query?.Invoke(conf) : null; + + public TValue? GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : struct + => Configuration is TConfiguration conf ? query?.Invoke(conf) : null; + protected Skin(SkinInfo skin) { SkinInfo = skin; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 654260daca..ba6f5b4774 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -119,10 +119,14 @@ namespace osu.Game.Skinning public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName); - public Texture GetTexture(string componentName)=> CurrentSkin.Value.GetTexture(componentName); + public Texture GetTexture(string componentName) => CurrentSkin.Value.GetTexture(componentName); public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName); public Color4? GetColour(string colourName) => CurrentSkin.Value.GetColour(colourName); + + public TValue GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : class => CurrentSkin.Value.GetConfiguration(query); + + public TValue? GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : struct => CurrentSkin.Value.GetConfiguration(query); } } From 366b7fca657904d6a14f6f3b6927ed614361ecf9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 18:50:19 +0900 Subject: [PATCH 243/537] Remove GetColour method --- .../Objects/Drawables/DrawableHitObject.cs | 2 +- osu.Game/Skinning/ISkinSource.cs | 7 ++---- .../Skinning/LocalSkinOverrideContainer.cs | 11 +++----- osu.Game/Skinning/Skin.cs | 25 ++----------------- osu.Game/Skinning/SkinManager.cs | 7 ++---- 5 files changed, 11 insertions(+), 41 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index b8852bb141..bbd6c15d0b 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Objects.Drawables base.SkinChanged(skin, allowFallback); if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetColour($"Play/Combo/{combo.ComboIndex}") ?? Color4.White; + AccentColour = skin.GetValue(s => s.ComboColours?.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; } protected override void LoadComplete() diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs index ae2909327e..d8f259b4ea 100644 --- a/osu.Game/Skinning/ISkinSource.cs +++ b/osu.Game/Skinning/ISkinSource.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; -using OpenTK.Graphics; namespace osu.Game.Skinning { @@ -22,10 +21,8 @@ namespace osu.Game.Skinning SampleChannel GetSample(string sampleName); - Color4? GetColour(string colourName); + TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class; - TValue GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : class; - - TValue? GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : struct; + TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct; } } diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index 8181ec0ea1..b7e2bd0daf 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; -using OpenTK.Graphics; namespace osu.Game.Skinning { @@ -21,26 +20,24 @@ namespace osu.Game.Skinning public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName); - public Color4? GetColour(string colourName) => source.GetColour(colourName) ?? fallbackSource?.GetColour(colourName); - - public TValue? GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : struct + public TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct { TValue? val = null; var conf = (source as Skin)?.Configuration as TConfiguration; if (conf != null) val = query?.Invoke(conf); - return val ?? fallbackSource?.GetConfiguration(query); + return val ?? fallbackSource?.GetValue(query); } - public TValue GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : class + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class { TValue val = null; var conf = (source as Skin)?.Configuration as TConfiguration; if (conf != null) val = query?.Invoke(conf); - return val ?? fallbackSource?.GetConfiguration(query); + return val ?? fallbackSource?.GetValue(query); } private readonly ISkinSource source; diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 1c175ea4e9..02fb84a4a2 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; -using OpenTK.Graphics; namespace osu.Game.Skinning { @@ -23,30 +22,10 @@ namespace osu.Game.Skinning public abstract Texture GetTexture(string componentName); - public virtual Color4? GetColour(string colourName) - { - var namespaces = colourName.Split('/'); - - switch (namespaces[0]) - { - case "Play": - switch (namespaces[1]) - { - case "Combo": - int index = int.Parse(namespaces[2]); - return GetConfiguration(s => s.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[index % Configuration.ComboColours.Count]); - } - - break; - } - - return null; - } - - public TValue GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : class + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class => Configuration is TConfiguration conf ? query?.Invoke(conf) : null; - public TValue? GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : struct + public TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct => Configuration is TConfiguration conf ? query?.Invoke(conf) : null; protected Skin(SkinInfo skin) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index ba6f5b4774..f965a77cce 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Platform; using osu.Game.Database; using osu.Game.IO.Archives; -using OpenTK.Graphics; namespace osu.Game.Skinning { @@ -123,10 +122,8 @@ namespace osu.Game.Skinning public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName); - public Color4? GetColour(string colourName) => CurrentSkin.Value.GetColour(colourName); + public TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class => CurrentSkin.Value.GetValue(query); - public TValue GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : class => CurrentSkin.Value.GetConfiguration(query); - - public TValue? GetConfiguration(Func query) where TConfiguration : SkinConfiguration where TValue : struct => CurrentSkin.Value.GetConfiguration(query); + public TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct => CurrentSkin.Value.GetValue(query); } } From 9f220b3b9b2ef28e9cb805cdd87716b962b47862 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Mar 2018 18:51:03 +0900 Subject: [PATCH 244/537] Add explanatory comment --- osu.Game/Graphics/UserInterface/FocusedTextBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 9ff64e5d5f..33786252ab 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -30,6 +30,7 @@ namespace osu.Game.Graphics.UserInterface } } + // We may not be focused yet, but we need to handle keyboard input to be able to request focus public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput; protected override void OnFocus(InputState state) From f87c3765474310dea7ac0addf4f757da0c336bb8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Mar 2018 18:52:32 +0900 Subject: [PATCH 245/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index cc39713fbf..241133f0a6 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit cc39713fbf9427aa53df91e27ecd09d15661089f +Subproject commit 241133f0a65326a563ba23b7166167a882d1d5cb From af65f2dd48273ac95035223d5482ee67878ecafd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 18:53:16 +0900 Subject: [PATCH 246/537] Move AccentColour changes local to rulesets which rely on it --- .../Objects/Drawable/DrawableCatchHitObject.cs | 10 ++++++++++ .../Objects/Drawables/DrawableOsuHitObject.cs | 11 +++++++++++ .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 8 -------- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 8d56fc1081..582946ff00 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -8,6 +8,8 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using OpenTK; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -57,6 +59,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss }); } + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + if (HitObject is IHasComboInformation combo) + AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + } + private const float preempt = 1000; protected override void UpdateState(ArmedState state) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 2e59e2dc60..d4d89c2aa3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -5,6 +5,9 @@ using System.ComponentModel; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics; using System.Linq; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -34,6 +37,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + if (HitObject is IHasComboInformation combo) + AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + } + protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein); protected virtual void UpdateCurrentState(ArmedState state) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index bbd6c15d0b..348364a2bf 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -102,14 +102,6 @@ namespace osu.Game.Rulesets.Objects.Drawables } } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - - if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetValue(s => s.ComboColours?.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; - } - protected override void LoadComplete() { base.LoadComplete(); From e5f83530b8dab9e1c8b4caf67abdc88e9944df14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 19:49:31 +0900 Subject: [PATCH 247/537] Only allow settings or notifications to be visible at once Resolves #2273. --- osu.Game/OsuGame.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e656c7256e..4a40a6b5df 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -302,6 +302,21 @@ namespace osu.Game }; } + var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; + foreach (var overlay in singleDisplaySideOverlays) + { + overlay.StateChanged += state => + { + if (state == Visibility.Hidden) return; + + foreach (var c in singleDisplaySideOverlays) + { + if (c == overlay) continue; + c.State = Visibility.Hidden; + } + }; + } + // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; foreach (var overlay in informationalOverlays) From 88ad3db0220de81229e95b23214a94b4ad138aa5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Mar 2018 20:00:57 +0900 Subject: [PATCH 248/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 241133f0a6..d8d4f55e10 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 241133f0a65326a563ba23b7166167a882d1d5cb +Subproject commit d8d4f55e10ac553223db75874bae6ae4894b739a From 2b7d22c4eadde80cc90bef1f3e82b148095399a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Mar 2018 20:11:55 +0900 Subject: [PATCH 249/537] Very slightly reduce size of slider bar to increase bottom padding --- osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 8ba3500eea..a7be3c1eb5 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose }, RowDimensions = new[] { - new Dimension(GridSizeMode.Absolute, 35), + new Dimension(GridSizeMode.Absolute, 30), new Dimension(GridSizeMode.Absolute, 25), } } From 9e080028ff2d6953fddbeb2a98f52c677b03a8ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 20:35:07 +0900 Subject: [PATCH 250/537] Add shutter sound --- osu-resources | 2 +- osu.Game/Graphics/ScreenshotManager.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu-resources b/osu-resources index 7bb0782200..6e145ed502 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 7bb0782200abadf73b79ed1a3bc1d5b926c6a81e +Subproject commit 6e145ed50274539ee827fdc3d1fda1e130b070fd diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 4c9e9756df..412ec18412 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -5,6 +5,8 @@ using System; using System.Drawing.Imaging; using System.IO; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -24,14 +26,18 @@ namespace osu.Game.Graphics private Storage storage; private NotificationOverlay notificationOverlay; + private SampleChannel shutter; + [BackgroundDependencyLoader] - private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay) + private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio) { this.host = host; this.storage = storage.GetStorageForDirectory(@"screenshots"); this.notificationOverlay = notificationOverlay; screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); + + shutter = audio.Sample.Get("UI/shutter"); } public bool OnPressed(GlobalAction action) @@ -39,6 +45,7 @@ namespace osu.Game.Graphics switch (action) { case GlobalAction.TakeScreenshot: + shutter.Play(); TakeScreenshotAsync(); return true; } From 94847e4a233eee9ddef6cfb97ac4ff1ccc897509 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 20:44:00 +0900 Subject: [PATCH 251/537] Allow clicking notification to open screenshot folder --- osu.Game/Graphics/ScreenshotManager.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 412ec18412..c1df7999ad 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -53,10 +53,7 @@ namespace osu.Game.Graphics return false; } - public bool OnReleased(GlobalAction action) - { - return false; - } + public bool OnReleased(GlobalAction action) => false; public async void TakeScreenshotAsync() { @@ -77,7 +74,15 @@ namespace osu.Game.Graphics throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); } - notificationOverlay.Post(new SimpleNotification { Text = $"{fileName} saved" }); + notificationOverlay.Post(new SimpleNotification + { + Text = $"{fileName} saved!", + Activated = () => + { + storage.OpenInNativeExplorer(); + return true; + } + }); } } From e41993ac44fb88f5174c7a1c5bf323b8cc98fbc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 20:45:26 +0900 Subject: [PATCH 252/537] Don't bother with an exception that will never happen Wasn't being caught anyways --- osu.Game/Graphics/ScreenshotManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index c1df7999ad..b0cd997837 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -60,6 +60,8 @@ namespace osu.Game.Graphics using (var bitmap = await host.TakeScreenshotAsync()) { var fileName = getFileName(); + if (fileName == null) return; + var stream = storage.GetStream(fileName, FileAccess.Write); switch (screenshotFormat.Value) @@ -102,7 +104,7 @@ namespace osu.Game.Graphics return indexedName; } - throw new Exception($"Failed to find suitable file name for saving {fileExt} image"); + return null; } } } From 14a653aa28d14b4d42a5602ef8654524ba596b0b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 20:55:00 +0900 Subject: [PATCH 253/537] Restore old auto-popout notification overlay behaviour --- osu.Game/Overlays/NotificationOverlay.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 48ad507d88..f5b281efc1 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -110,17 +110,7 @@ namespace osu.Game.Overlays private int runningDepth; - private void notificationClosed() - { - Schedule(() => - { - // hide ourselves if all notifications have been dismissed. - if (totalCount == 0) - State = Visibility.Hidden; - }); - - updateCounts(); - } + private void notificationClosed() => updateCounts(); private readonly Scheduler postScheduler = new Scheduler(); @@ -141,6 +131,8 @@ namespace osu.Game.Overlays var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType))); section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth); + State = Visibility.Visible; + updateCounts(); }); From 1d3c9098b8884b889089ebf4337d0008e189c190 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Mar 2018 21:42:57 +0900 Subject: [PATCH 254/537] Load component asynchronously --- osu.Game/OsuGame.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 268cca5957..89447b8ed6 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -239,6 +239,7 @@ namespace osu.Game loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); + loadComponentSingleFile(new ScreenshotManager(), Add); //overlay elements loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); @@ -285,8 +286,6 @@ namespace osu.Game dependencies.Cache(notifications); dependencies.Cache(dialogOverlay); - Add(new ScreenshotManager()); - // ensure only one of these overlays are open at once. var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; foreach (var overlay in singleDisplayOverlays) From a3d8048acab0b1ccf267a85e789dc944496e7ee3 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Thu, 22 Mar 2018 21:46:35 +0100 Subject: [PATCH 255/537] abort loading and exit player for dummy maps --- osu.Game/Screens/Play/Player.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b0472f0e0d..89cf326bbe 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -89,6 +89,13 @@ namespace osu.Game.Screens.Play userAudioOffset = config.GetBindable(OsuSetting.AudioOffset); WorkingBeatmap working = Beatmap.Value; + + if (working is DummyWorkingBeatmap) + { + Exit(); + return; + } + Beatmap beatmap; try From d4aeb3d00bb021dde49b742ed1aad3a0c69ed6f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Mar 2018 01:06:05 +0900 Subject: [PATCH 256/537] Fix BeatmapCarousel's flush not correctly applying selection changes They may have been delayed until the next Update, which is too late in this case. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c2bb155753..aed8fb110f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -328,7 +328,10 @@ namespace osu.Game.Screens.Select public void FlushPendingFilterOperations() { if (FilterTask?.Completed == false) + { applyActiveCriteria(false, false); + Update(); + } } public void Filter(FilterCriteria newCriteria, bool debounce = true) From f77dad280972b7286e58ad45feb4c2a2ed287265 Mon Sep 17 00:00:00 2001 From: Joseph Madamba <35318437+Joehuu@users.noreply.github.com> Date: Thu, 22 Mar 2018 22:49:14 -0700 Subject: [PATCH 257/537] Add perfect mod icon --- osu.Game/Graphics/SpriteIcon.cs | 2 +- osu.Game/Rulesets/Mods/ModPerfect.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/SpriteIcon.cs b/osu.Game/Graphics/SpriteIcon.cs index a93cb4c6cd..4324119481 100644 --- a/osu.Game/Graphics/SpriteIcon.cs +++ b/osu.Game/Graphics/SpriteIcon.cs @@ -988,7 +988,7 @@ namespace osu.Game.Graphics fa_osu_expert_mania = 0xe028, // mod icons - fa_osu_mod_perfect = 0xe02d, + fa_osu_mod_perfect = 0xe049, fa_osu_mod_autopilot = 0xe03a, fa_osu_mod_auto = 0xe03b, fa_osu_mod_cinema = 0xe03c, diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index 08942fbe12..116d13bf0a 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Perfect"; public override string ShortenedName => "PF"; - public override FontAwesome Icon => FontAwesome.fa_question; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_perfect; public override string Description => "SS or quit."; protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1; From b56eee1927f6c80bf777f2529687537fc9699e36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Mar 2018 15:04:40 +0900 Subject: [PATCH 258/537] Fix background loaded player never being disposed if early exit occurs --- osu.Game/Screens/Play/PlayerLoader.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 89082cfbd5..6d55cdb9ca 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -27,6 +28,8 @@ namespace osu.Game.Screens.Play private bool showOverlays = true; public override bool ShowOverlaysOnEnter => showOverlays; + private Task loadTask; + public PlayerLoader(Player player) { this.player = player; @@ -55,7 +58,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding(25) }); - LoadComponentAsync(player); + loadTask = LoadComponentAsync(player); } protected override void OnResuming(Screen last) @@ -65,7 +68,7 @@ namespace osu.Game.Screens.Play contentIn(); //we will only be resumed if the player has requested a re-run (see ValidForResume setting above) - LoadComponentAsync(player = new Player + loadTask = LoadComponentAsync(player = new Player { RestartCount = player.RestartCount + 1, RestartRequested = player.RestartRequested, @@ -154,6 +157,8 @@ namespace osu.Game.Screens.Play { if (!IsCurrentScreen) return; + loadTask = null; + if (!Push(player)) Exit(); else @@ -192,6 +197,14 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + // if the player never got pushed, we should explicitly dispose it. + loadTask?.ContinueWith(_ => player.Dispose()); + } + private class BeatmapMetadataDisplay : Container { private class MetadataLine : Container From eb751fa607d25c187947cb53bd4d7988f8824114 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Mar 2018 15:05:28 +0900 Subject: [PATCH 259/537] Fix event unbind not being unbound correctly --- osu.Game/Skinning/LocalSkinOverrideContainer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index b7e2bd0daf..d000127859 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -48,13 +48,15 @@ namespace osu.Game.Skinning this.source = source; } + private void onSourceChanged() => SourceChanged?.Invoke(); + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); fallbackSource = dependencies.Get(); if (fallbackSource != null) - fallbackSource.SourceChanged += () => SourceChanged?.Invoke(); + fallbackSource.SourceChanged += onSourceChanged; dependencies.CacheAs(this); @@ -66,7 +68,7 @@ namespace osu.Game.Skinning base.Dispose(isDisposing); if (fallbackSource != null) - fallbackSource.SourceChanged -= SourceChanged; + fallbackSource.SourceChanged -= onSourceChanged; } } } From 6c4e719e0f675af3724a45862a9338c4038681e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Mar 2018 15:20:19 +0900 Subject: [PATCH 260/537] Fix API never stopping its thread --- osu.Game/Online/API/APIAccess.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 2cb8424bcc..39ecde55bc 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -44,7 +44,6 @@ namespace osu.Game.Online.API protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); - // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (should dispose of this or at very least keep a reference). private readonly Thread thread; private readonly Logger log; @@ -267,10 +266,7 @@ namespace osu.Game.Online.API public bool IsLoggedIn => LocalUser.Value.Id > 1; - public void Queue(APIRequest request) - { - queue.Enqueue(request); - } + public void Queue(APIRequest request) => queue.Enqueue(request); public event StateChangeDelegate OnStateChange; @@ -312,6 +308,9 @@ namespace osu.Game.Online.API config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? Token : string.Empty); config.Save(); + + flushQueue(); + thread?.Abort(); } } From 52fa837447ba745843331a11e36d3a908115fd03 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Mar 2018 18:17:14 +0900 Subject: [PATCH 261/537] Fix volume glow being cut off Fixes #2285. --- osu.Game/Overlays/Volume/VolumeMeter.cs | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 64b9e513c4..b1951f4d72 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; +using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; @@ -93,19 +94,25 @@ namespace osu.Game.Overlays.Volume Colour = colours.Gray2, Size = new Vector2(0.8f) }, - (volumeCircle = new CircularProgress + new Container { - RelativeSizeAxes = Axes.Both, - InnerRadius = 0.05f, - Rotation = 180, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(0.8f) - }).WithEffect(new GlowEffect - { - Colour = meterColour, - Strength = 2 - }), + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.8f), + Padding = new MarginPadding(-Blur.KernelSize(5)), + Rotation = 180, + Child = (volumeCircle = new CircularProgress + { + RelativeSizeAxes = Axes.Both, + InnerRadius = 0.05f, + }).WithEffect(new GlowEffect + { + Colour = meterColour, + Strength = 2, + PadExtent = true + }), + }, maxGlow = (text = new OsuSpriteText { Anchor = Anchor.Centre, From afdab7895aa3fa3cff46529ef7b164342fda7202 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Mar 2018 20:41:38 +0900 Subject: [PATCH 262/537] Fix beatmap background fade not being updated on retry Fixes #2287 --- osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs index 8e963a94a8..964267cc17 100644 --- a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs +++ b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs @@ -46,6 +46,12 @@ namespace osu.Game.Screens.Play UpdateBackgroundElements(); } + protected override void OnResuming(Screen last) + { + base.OnResuming(last); + UpdateBackgroundElements(); + } + protected virtual void UpdateBackgroundElements() { if (!IsCurrentScreen) return; From f0c0a5110819716e9bfffcaf7f22a681f5e4e88d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Mar 2018 20:57:04 +0900 Subject: [PATCH 263/537] Convert APIAccess to use cancellation tokens --- osu.Game/Online/API/APIAccess.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 39ecde55bc..957aeac3cd 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Logging; @@ -44,7 +45,7 @@ namespace osu.Game.Online.API protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); - private readonly Thread thread; + private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); private readonly Logger log; @@ -58,8 +59,7 @@ namespace osu.Game.Online.API ProvidedUsername = config.Get(OsuSetting.Username); Token = config.Get(OsuSetting.Token); - thread = new Thread(run) { IsBackground = true }; - thread.Start(); + Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } private readonly List components = new List(); @@ -92,7 +92,7 @@ namespace osu.Game.Online.API private void run() { - while (thread.IsAlive) + while (!cancellationToken.IsCancellationRequested) { switch (State) { @@ -310,7 +310,7 @@ namespace osu.Game.Online.API config.Save(); flushQueue(); - thread?.Abort(); + cancellationToken.Cancel(); } } From dd5cc592501115d2584ae032cf97a0b6dd939315 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Fri, 23 Mar 2018 22:53:06 +0300 Subject: [PATCH 264/537] Introduce 'Capture menu cursor' setting --- osu.Game/Configuration/OsuConfigManager.cs | 4 +++- osu.Game/Graphics/ScreenshotManager.cs | 3 +++ .../Overlays/Settings/Sections/Graphics/DetailSettings.cs | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 70260b349e..d55cd6bf2c 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -84,6 +84,7 @@ namespace osu.Game.Configuration Set(OsuSetting.Version, string.Empty); Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); + Set(OsuSetting.ScreenshotCaptureMenuCursor, false); } public OsuConfigManager(Storage storage) : base(storage) @@ -128,6 +129,7 @@ namespace osu.Game.Configuration ShowConvertedBeatmaps, SpeedChangeVisualisation, Skin, - ScreenshotFormat + ScreenshotFormat, + ScreenshotCaptureMenuCursor } } diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index b0cd997837..68ac6e9df4 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -22,6 +22,8 @@ namespace osu.Game.Graphics public class ScreenshotManager : Container, IKeyBindingHandler, IHandleGlobalInput { private Bindable screenshotFormat; + private Bindable captureMenuCursor; + private GameHost host; private Storage storage; private NotificationOverlay notificationOverlay; @@ -36,6 +38,7 @@ namespace osu.Game.Graphics this.notificationOverlay = notificationOverlay; screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); + captureMenuCursor = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor); shutter = audio.Sample.Get("UI/shutter"); } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index fa57a85454..e124d4cf7e 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -30,6 +30,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { LabelText = "Screenshot format", Bindable = config.GetBindable(OsuSetting.ScreenshotFormat) + }, + new SettingsCheckbox + { + LabelText = "Capture menu cursor", + Bindable = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor) } }; } From b593c478096b8919aa7078d5e9607e039c7f72ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Mar 2018 05:07:48 +0900 Subject: [PATCH 265/537] Add setting to toggle performance logging --- .../Settings/Sections/Debug/GeneralSettings.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index 867410b178..ba591b9456 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -18,14 +18,19 @@ namespace osu.Game.Overlays.Settings.Sections.Debug { new SettingsCheckbox { - LabelText = "Bypass caching", - Bindable = config.GetBindable(DebugSetting.BypassCaching) + LabelText = "Show log overlay", + Bindable = frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay) }, new SettingsCheckbox { - LabelText = "Debug logs", - Bindable = frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay) - } + LabelText = "Performance logging", + Bindable = frameworkConfig.GetBindable(FrameworkSetting.PerformanceLogging) + }, + new SettingsCheckbox + { + LabelText = "Bypass caching (slow)", + Bindable = config.GetBindable(DebugSetting.BypassCaching) + }, }; } } From 256baf6d60f10fe2187ba45bfc24d634e9fb2b52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Mar 2018 05:40:26 +0900 Subject: [PATCH 266/537] Move binding to LoadComplete Previously there was a chance that it would still never get disposed, as the event was bound in async load, before it was in a state it can be recursively disposed via the PlayerLoader call. --- osu.Game/Skinning/LocalSkinOverrideContainer.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index d000127859..95cdde2f48 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -55,14 +55,19 @@ namespace osu.Game.Skinning var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); fallbackSource = dependencies.Get(); - if (fallbackSource != null) - fallbackSource.SourceChanged += onSourceChanged; - dependencies.CacheAs(this); return dependencies; } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (fallbackSource != null) + fallbackSource.SourceChanged += onSourceChanged; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 3844e95656a756286f8c31106bea35026b3af904 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Mar 2018 05:55:56 +0900 Subject: [PATCH 267/537] Fix one more instance of the same thing happening --- osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs index b6bc348a52..2b53f77d62 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs @@ -45,6 +45,11 @@ namespace osu.Game.Input.Bindings private void load(KeyBindingStore keyBindings) { store = keyBindings; + } + + protected override void LoadComplete() + { + base.LoadComplete(); store.KeyBindingChanged += ReloadMappings; } From ade97f8c0c2ca3a93b2e3ebec01b6e1847416148 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Mar 2018 05:58:59 +0900 Subject: [PATCH 268/537] Increase deploy delta keep --- osu.Desktop.Deploy/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index d8ff2bfd82..3a716468c9 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -39,7 +39,7 @@ namespace osu.Desktop.Deploy /// /// How many previous build deltas we want to keep when publishing. /// - private const int keep_delta_count = 3; + private const int keep_delta_count = 4; private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\""; From 7115e4390eabb3f408cc7d623c2b85798a0deaae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Mar 2018 11:18:24 +0900 Subject: [PATCH 269/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index d8d4f55e10..99140d9d79 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d8d4f55e10ac553223db75874bae6ae4894b739a +Subproject commit 99140d9d79909d1a5474e01c60e54cbdc27f6b19 From 19966988170f93c3469341cc7ff664994e0a8941 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Mar 2018 11:40:45 +0900 Subject: [PATCH 270/537] Fix @1x skin elements not being used --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 66e8cb8f9f..1b52507688 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -63,7 +63,7 @@ namespace osu.Game.Skinning if (texture == null) { ratio *= 2; - GetTexture(componentName); + texture = GetTexture(componentName); } if (texture == null) return null; From ffa712dccbd5bdef12d69a90d54e7dd723303932 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Mar 2018 15:19:45 +0900 Subject: [PATCH 271/537] Fix post-merge issues --- osu-framework | 2 +- osu-resources | 2 +- osu.Desktop/Program.cs | 6 +- osu.Game/Beatmaps/BeatmapManager.cs | 92 ------------------- osu.Game/Database/ArchiveModelManager.cs | 4 +- .../Screens/Compose/Layers/SelectionLayer.cs | 1 + 6 files changed, 10 insertions(+), 97 deletions(-) diff --git a/osu-framework b/osu-framework index 86079714a5..9b628d7f51 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 86079714a5f5c2678c4cc91df6c0257f33c43bcf +Subproject commit 9b628d7f51104ac94af2a4739480be0113ad551b diff --git a/osu-resources b/osu-resources index 883ff04e79..db406143da 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 883ff04e79a76f5eb1f89c4f59bd7476a803b42b +Subproject commit db406143da9e791efa04cdac4418c85a6831b739 diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index e878167536..5e6b5cc701 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -4,10 +4,12 @@ using System; using System.IO; using System.Linq; -using System.Runtime; using osu.Framework; using osu.Framework.Platform; using osu.Game.IPC; +#if NET_FRAMEWORK +using System.Runtime; +#endif namespace osu.Desktop { @@ -58,9 +60,11 @@ namespace osu.Desktop private static void useMulticoreJit() { +#if NET_FRAMEWORK var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles")); ProfileOptimization.SetProfileRoot(directory.FullName); ProfileOptimization.StartProfile("Startup.Profile"); +#endif } } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 46fef78c4e..1113e38d7a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -20,8 +20,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; -using osu.Game.Utils; -using osu.Game.Storyboards; namespace osu.Game.Beatmaps { @@ -197,96 +195,6 @@ namespace osu.Game.Beatmaps /// The object if it exists, or null. public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID); - /// - /// Delete a beatmap from the manager. - /// Is a no-op for already deleted beatmaps. - /// - /// The beatmap set to delete. - public void Delete(BeatmapSetInfo beatmapSet) - { - lock (importContextLock) - { - var context = importContext.Value; - - using (var transaction = context.BeginTransaction()) - { - context.ChangeTracker.AutoDetectChangesEnabled = false; - - // re-fetch the beatmap set on the import context. - // ReSharper disable once AccessToModifiedClosure - beatmapSet = context.BeatmapSetInfo.Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == beatmapSet.ID); - - // create local stores so we can isolate and thread safely, and share a context/transaction. - var iFiles = new FileStore(() => context, storage); - var iBeatmaps = createBeatmapStore(() => context); - - if (iBeatmaps.Delete(beatmapSet)) - { - if (!beatmapSet.Protected) - iFiles.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); - } - - context.ChangeTracker.AutoDetectChangesEnabled = true; - context.SaveChanges(transaction); - } - } - } - - public void UndeleteAll() - { - var deleteMaps = QueryBeatmapSets(bs => bs.DeletePending).ToList(); - - if (!deleteMaps.Any()) return; - - var notification = new ProgressNotification - { - CompletionText = "Restored all deleted beatmaps!", - Progress = 0, - State = ProgressNotificationState.Active, - }; - - PostNotification?.Invoke(notification); - - int i = 0; - - foreach (var bs in deleteMaps) - { - if (notification.State == ProgressNotificationState.Cancelled) - // user requested abort - return; - - notification.Text = $"Restoring ({i} of {deleteMaps.Count})"; - notification.Progress = (float)++i / deleteMaps.Count; - Undelete(bs); - } - - notification.State = ProgressNotificationState.Completed; - } - - public void Undelete(BeatmapSetInfo beatmapSet) - { - if (beatmapSet.Protected) - return; - - lock (importContextLock) - { - var context = importContext.Value; - - using (var transaction = context.BeginTransaction()) - { - context.ChangeTracker.AutoDetectChangesEnabled = false; - - var iFiles = new FileStore(() => context, storage); - var iBeatmaps = createBeatmapStore(() => context); - - undelete(iBeatmaps, iFiles, beatmapSet); - - context.ChangeTracker.AutoDetectChangesEnabled = true; - context.SaveChanges(transaction); - } - } - } - /// /// Delete a beatmap difficulty. /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index f0e67a7185..7050e34712 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Ionic.Zip; using Microsoft.EntityFrameworkCore; using osu.Framework.Logging; using osu.Framework.Platform; @@ -13,6 +12,7 @@ using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.IPC; using osu.Game.Overlays.Notifications; +using osu.Game.Utils; using SharpCompress.Common; using FileInfo = osu.Game.IO.FileInfo; @@ -336,7 +336,7 @@ namespace osu.Game.Database /// A reader giving access to the archive's content. private ArchiveReader getReaderFrom(string path) { - if (ZipFile.IsZipFile(path)) + if (ZipUtils.IsZipArchive(path)) return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path)); if (Directory.Exists(path)) return new LegacyFilesystemReader(path); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs index 8c66007bb7..ab51385980 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using OpenTK; using OpenTK.Graphics; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; namespace osu.Game.Screens.Edit.Screens.Compose.Layers { From 9d9b9d50bc2a26416a514aec8d14d31bbd9a2c70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Mar 2018 17:48:40 +0900 Subject: [PATCH 272/537] User nuget OpenTK package --- OpenTK.props | 5 +---- osu-framework | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/OpenTK.props b/OpenTK.props index 0876c52b66..6ab847b6cc 100644 --- a/OpenTK.props +++ b/OpenTK.props @@ -1,9 +1,6 @@ - - - PreserveNewest - + \ No newline at end of file diff --git a/osu-framework b/osu-framework index 9b628d7f51..7eb292e9ec 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 9b628d7f51104ac94af2a4739480be0113ad551b +Subproject commit 7eb292e9ec09a18a50c0619e34a4a06ae155b49b From 97be022e229e72e6369f8265f423bfc3b058d61b Mon Sep 17 00:00:00 2001 From: John Neijzen Date: Sat, 24 Mar 2018 16:55:39 +0800 Subject: [PATCH 273/537] Remove duplicate condition --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 7aa4108428..bc51c69ea7 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -99,11 +99,6 @@ namespace osu.Game.Rulesets.Osu.Replays if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) - { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); - } } private void addHitObjectReplay(OsuHitObject h) From 0e669c9a3fb81a292affc5202def3c3dd9688714 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Mar 2018 18:22:55 +0900 Subject: [PATCH 274/537] Fix many warnings --- osu-framework | 2 +- osu.Game/Beatmaps/IBeatmapConverter.cs | 2 +- osu.Game/Database/DatabaseBackedStore.cs | 3 --- osu.Game/Database/DatabaseContextFactory.cs | 2 +- osu.Game/Graphics/DrawableDate.cs | 3 +-- osu.Game/Online/API/APIAccess.cs | 2 +- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 1 + osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs | 1 - osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs | 1 - osu.Game/Screens/Select/BeatmapCarousel.cs | 1 + osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- osu.Game/Screens/Select/PlaySongSelect.cs | 4 +++- osu.Game/Skinning/SkinManager.cs | 2 -- osu.Game/Skinning/SkinReloadableDrawable.cs | 2 +- osu.Game/Skinning/SkinnableDrawable.cs | 2 +- osu.sln.DotSettings | 1 + 16 files changed, 14 insertions(+), 17 deletions(-) diff --git a/osu-framework b/osu-framework index 7eb292e9ec..ae633287dc 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 7eb292e9ec09a18a50c0619e34a4a06ae155b49b +Subproject commit ae633287dc44517f3f5fc66f1d0f5333f76cc261 diff --git a/osu.Game/Beatmaps/IBeatmapConverter.cs b/osu.Game/Beatmaps/IBeatmapConverter.cs index ebd900b97e..096ba345a1 100644 --- a/osu.Game/Beatmaps/IBeatmapConverter.cs +++ b/osu.Game/Beatmaps/IBeatmapConverter.cs @@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps /// /// Converts a Beatmap using this Beatmap Converter. /// - /// The un-converted Beatmap. + /// The un-converted Beatmap. void Convert(Beatmap beatmap); } } diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 0fafb77339..4b582bdfea 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -11,9 +11,6 @@ namespace osu.Game.Database { protected readonly Storage Storage; - /// - /// Create a new instance (separate from the shared context via for performing isolated operations. - /// protected readonly IDatabaseContextFactory ContextFactory; /// diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index 712ed2d0cc..06737e61eb 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -17,7 +17,7 @@ namespace osu.Game.Database private readonly object writeLock = new object(); private bool currentWriteDidWrite; - private volatile int currentWriteUsages; + private int currentWriteUsages; public DatabaseContextFactory(GameHost host) { diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index a912f989e0..d2a7250aaa 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -6,7 +6,6 @@ using Humanizer; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; -using osu.Framework.Threading; using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics @@ -14,7 +13,6 @@ namespace osu.Game.Graphics public class DrawableDate : OsuSpriteText, IHasTooltip { private readonly DateTimeOffset date; - private ScheduledDelegate updateTask; public DrawableDate(DateTimeOffset date) { @@ -61,6 +59,7 @@ namespace osu.Game.Graphics public override bool HandleMouseInput => true; private void updateTime() => Text = date.Humanize(); + public string TooltipText => date.ToString(); } } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 957aeac3cd..798ed1b9b3 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -64,7 +64,7 @@ namespace osu.Game.Online.API private readonly List components = new List(); - internal void Schedule(Action action) => base.Schedule(action); + internal new void Schedule(Action action) => base.Schedule(action); public void Register(IOnlineComponent component) { diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 0f0c43bc88..ca203e1cdb 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Judgements /// Creates a drawable which visualises a . /// /// The judgement to visualise. + /// The object which was judged. public DrawableJudgement(Judgement judgement, DrawableHitObject judgedObject) { Judgement = judgement; diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs index ac1e5e29ec..892f5ca2fe 100644 --- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs @@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Replays.Types /// Populates this using values from a . /// /// The to extract values from. - /// The score. /// The beatmap. void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index a7be3c1eb5..0b30aeef8d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -24,7 +24,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose public class BeatDivisorControl : CompositeDrawable { private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - private int currentDivisorIndex; public BeatDivisorControl(BindableBeatDivisor beatDivisor) { diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index aed8fb110f..15f4d5cf96 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -547,6 +547,7 @@ namespace osu.Game.Screens.Select float? setY = null; if (!d.IsLoaded || beatmap.Alpha == 0) // can't use IsPresent due to DrawableCarouselItem override. + // ReSharper disable once PossibleNullReferenceException (resharper broken?) setY = lastSet.Y + lastSet.DrawHeight + 5; if (d.IsLoaded) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 7476df4955..5cde0d2751 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -254,7 +254,7 @@ namespace osu.Game.Screens.Select private OsuSpriteText[] getMapper(BeatmapMetadata metadata) { - if (string.IsNullOrEmpty(metadata.Author?.Username)) + if (metadata.Author == null || string.IsNullOrEmpty(metadata.Author.Username)) return Array.Empty(); return new[] diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 09524d2eac..d5a91e1a6b 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -75,7 +76,8 @@ namespace osu.Game.Screens.Select { // if we have no beatmaps but osu-stable is found, let's prompt the user to import. if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable) - dialogOverlay.Push(new ImportFromStablePopup(() => beatmaps.ImportFromStable())); + dialogOverlay.Push(new ImportFromStablePopup(() => + Task.Factory.StartNew(beatmaps.ImportFromStable, TaskCreationOptions.LongRunning))); }); } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index f965a77cce..24d7155b53 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -80,8 +80,6 @@ namespace osu.Game.Skinning return new LegacySkin(skinInfo, Files.Store, audio); } - private SkinStore store; - public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost, AudioManager audio) : base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost) { diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index 36f33e746a..5007a9d325 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -23,7 +23,7 @@ namespace osu.Game.Skinning /// /// Create a new /// - /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. + /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. protected SkinReloadableDrawable(Func allowFallback = null) { this.allowFallback = allowFallback; diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 09d2e6a3ed..7ed796d433 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -29,7 +29,7 @@ namespace osu.Game.Skinning /// /// The namespace-complete resource name for this skinnable element. /// A function to create the default skin implementation of this element. - /// Whther to fallback to the default implementation when a custom skin is specified but not implementation is present. + /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. /// Whether a user-skin drawable should be limited to the size of our parent. public SkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, bool restrictSize = true) : base(allowFallback) { diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 3b62dbe579..0b631d008b 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -43,6 +43,7 @@ DO_NOT_SHOW WARNING WARNING + HINT ERROR HINT HINT From 12f785bf40a49371a270fa72f0945ea73d2d4672 Mon Sep 17 00:00:00 2001 From: John Neijzen Date: Sat, 24 Mar 2018 17:48:43 +0800 Subject: [PATCH 275/537] Change duplicate condition to HitResult.Good instead of HitResult.Meh --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index bc51c69ea7..f83aca8043 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -99,6 +99,11 @@ namespace osu.Game.Rulesets.Osu.Replays if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } + else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) + { + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + } } private void addHitObjectReplay(OsuHitObject h) From e91d24f31a9f1059b98650a2a0ccdce5e11fe476 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Sat, 24 Mar 2018 12:53:01 +0300 Subject: [PATCH 276/537] Use ScreenshotCaptureMenuCursor in ScreenshotManager --- osu.Game/Graphics/ScreenshotManager.cs | 23 ++++++++++++++++++++++- osu.Game/OsuGameBase.cs | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 68ac6e9df4..7d50a298ec 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -4,15 +4,19 @@ using System; using System.Drawing.Imaging; using System.IO; +using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Platform; using osu.Game.Configuration; +using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; @@ -29,13 +33,15 @@ namespace osu.Game.Graphics private NotificationOverlay notificationOverlay; private SampleChannel shutter; + private CursorContainer menuCursorContainer; [BackgroundDependencyLoader] - private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio) + private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio, CursorOverrideContainer cursorOverrideContainer) { this.host = host; this.storage = storage.GetStorageForDirectory(@"screenshots"); this.notificationOverlay = notificationOverlay; + this.menuCursorContainer = cursorOverrideContainer.Cursor; screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); captureMenuCursor = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor); @@ -60,6 +66,18 @@ namespace osu.Game.Graphics public async void TakeScreenshotAsync() { + var menuCursorWasHidden = false; + if (!captureMenuCursor.Value && menuCursorContainer.State == Visibility.Visible) + { + menuCursorContainer.ToggleVisibility(); + await Task.Run(() => + { + while (menuCursorContainer.ActiveCursor.Alpha > 0) + Thread.Sleep(1); + }); + menuCursorWasHidden = true; + } + using (var bitmap = await host.TakeScreenshotAsync()) { var fileName = getFileName(); @@ -89,6 +107,9 @@ namespace osu.Game.Graphics } }); } + + if (menuCursorWasHidden) + menuCursorContainer.ToggleVisibility(); } private string getFileName() diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 54a279e977..ac5e300f16 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -214,7 +214,7 @@ namespace osu.Game GlobalActionContainer globalBinding; - CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; + dependencies.Cache(CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }); CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) { RelativeSizeAxes = Axes.Both, From 9d96aeb3478436cd5ebf17c48c44f5374b17afba Mon Sep 17 00:00:00 2001 From: John Neijzen Date: Sat, 24 Mar 2018 17:53:50 +0800 Subject: [PATCH 277/537] Update OsuAutoGenerator.cs --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index f83aca8043..be696fdc55 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Replays if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) + else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Good) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); From bee9af8500b14823f213c6eb718dd89cdc71bd8d Mon Sep 17 00:00:00 2001 From: John Neijzen Date: Sat, 24 Mar 2018 18:30:36 +0800 Subject: [PATCH 278/537] Update OsuAutoGenerator.cs --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index be696fdc55..7e89fee2f6 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -101,8 +101,8 @@ namespace osu.Game.Rulesets.Osu.Replays } else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Good) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } } From 7b39447213581938cab9b287479deccea48f3459 Mon Sep 17 00:00:00 2001 From: John Neijzen Date: Sat, 24 Mar 2018 18:34:56 +0800 Subject: [PATCH 279/537] Update OsuAutoGenerator.cs --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 7e89fee2f6..699cdf75a2 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Replays if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) { From d28de89e5059299e228cbd92069fc9c80df0fb4e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 25 Mar 2018 01:34:59 +0900 Subject: [PATCH 280/537] Add net461 target to test projects for nunit discovery Nunit "by design" will not run tests for NETStandard projects (see: https://github.com/nunit/dotnet-test-nunit/issues/122#issuecomment-329531632). --- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 2 +- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 2 +- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 2 +- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 6bd96b5d87..79f5b76bf0 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + net461;netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 8092af65ce..75cc9d1583 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + net461;netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 53d444d0d9..6ba4902d0a 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + net461;netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index bcceb03042..ecd6d05e13 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + net461;netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 1c9b0503c0..adedbd6f8a 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + net461;netstandard2.0 Library AnyCPU true diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f763d2527e..d7dfac2afc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + net461;netstandard2.0 Library AnyCPU true From 097ab66182b79d2bb8ec51534e2d792f622207b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 25 Mar 2018 01:54:25 +0900 Subject: [PATCH 281/537] Fix resharper error --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 5cde0d2751..8e045b9fb7 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -254,7 +254,7 @@ namespace osu.Game.Screens.Select private OsuSpriteText[] getMapper(BeatmapMetadata metadata) { - if (metadata.Author == null || string.IsNullOrEmpty(metadata.Author.Username)) + if (string.IsNullOrEmpty(metadata.Author?.Username)) return Array.Empty(); return new[] @@ -268,6 +268,7 @@ namespace osu.Game.Screens.Select new OsuSpriteText { Font = @"Exo2.0-Bold", + // ReSharper disable once PossibleNullReferenceException (resharper broken?) Text = metadata.Author.Username, TextSize = 15, } From 31920f1866a5632432649404c3d6779753f92dd3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 25 Mar 2018 02:12:01 +0900 Subject: [PATCH 282/537] Fix ruleset resources not getting included in build --- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 3 +++ osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 3 +++ osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 3 +++ osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 3 +++ 4 files changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 79f5b76bf0..335638f10b 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -20,4 +20,7 @@ + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 75cc9d1583..c44ae0416d 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -20,4 +20,7 @@ + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 6ba4902d0a..2b055fb047 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -20,4 +20,7 @@ + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index ecd6d05e13..e9a4a4e79a 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -20,4 +20,7 @@ + + + \ No newline at end of file From 0b2c1930bdc796e2a019790644f0317dab8b8c5b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 25 Mar 2018 02:28:10 +0900 Subject: [PATCH 283/537] Reference osu.Game.Tests in the desktop project --- osu.Desktop/osu.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 6822b7be51..201ba47cae 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -27,6 +27,7 @@ + From c21e820a45169762de7a655d72ebca848d813f44 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 25 Mar 2018 02:43:45 +0900 Subject: [PATCH 284/537] One more level of nesting (we can do this better in the future) --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 6428881b54..4f5eae85c8 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Beatmaps.IO [TestFixture] public class ImportBeatmapTest { - private const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; + private const string osz_path = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; [Test] public void TestImportWhenClosed() From 4dc317d4df095acd2dad531581f2d1d708698c27 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 25 Mar 2018 03:01:48 +0900 Subject: [PATCH 285/537] Fix msbuild warnings --- osu.Desktop/osu.Desktop.csproj | 3 +++ osu.Game.Tests/osu.Game.Tests.csproj | 2 ++ osu.Game/osu.Game.csproj | 1 + 3 files changed, 6 insertions(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 201ba47cae..9c2a1713cb 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -15,6 +15,9 @@ lazer.ico 0.0.0.0 0.0.0.0 + + NU1701 $(DefineConstants);NET_FRAMEWORK diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index adedbd6f8a..3a351a63a8 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -11,6 +11,8 @@ ppy Pty Ltd 2007-2017 osu.Game.Tests osu.Game.Tests + + NU1701 diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d7dfac2afc..893456034d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -12,6 +12,7 @@ osu.Game click the circles. to the beat. osu.Game + 0 From 46d859a6607b6bdf3e63a3d1e94dcf97266a45ac Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Sun, 25 Mar 2018 13:25:48 +0200 Subject: [PATCH 286/537] exit player earlier if a dummy map is loaded --- osu.Game/Screens/Play/Player.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 89cf326bbe..93b36e48b9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -83,19 +83,19 @@ namespace osu.Game.Screens.Play private void load(AudioManager audio, APIAccess api, OsuConfigManager config) { this.api = api; - sampleRestart = audio.Sample.Get(@"Gameplay/restart"); - - mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); - userAudioOffset = config.GetBindable(OsuSetting.AudioOffset); WorkingBeatmap working = Beatmap.Value; - if (working is DummyWorkingBeatmap) { Exit(); return; } + sampleRestart = audio.Sample.Get(@"Gameplay/restart"); + + mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); + userAudioOffset = config.GetBindable(OsuSetting.AudioOffset); + Beatmap beatmap; try From 96901b649826e293ee99caf5029c50c99760fbe4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 10:52:54 +0900 Subject: [PATCH 287/537] Add msbuild/dotnet build + launch targets --- .vscode/launch.json | 131 ++++++++++-------- .vscode/tasks.json | 94 +++++++------ osu-framework | 2 +- osu-resources | 2 +- .../osu.Game.Rulesets.Catch.csproj | 2 +- .../osu.Game.Rulesets.Mania.csproj | 2 +- .../osu.Game.Rulesets.Osu.csproj | 2 +- .../osu.Game.Rulesets.Taiko.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 2 +- 10 files changed, 135 insertions(+), 106 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 6c0a8929f7..8c8255db71 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,102 +1,119 @@ { "version": "0.2.0", - "configurations": [{ - "name": "osu! VisualTests (Debug, .NETFramework)", + "configurations": [ + { + "name": "VisualTests (Debug, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", "request": "launch", - "type": "clr", "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net461/osu!.exe", "args": [ "--tests" ], "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Debug)", + "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, "env": {}, "console": "internalConsole" }, { - "name": "osu! VisualTests (Debug, .NETCore)", + "name": "VisualTests (Release, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", "request": "launch", - "type": "coreclr", - "program": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll", - "args": [ - "--tests" - ], - "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Debug)", - "runtimeExecutable": null, - "env": {}, - "console": "internalConsole" - }, - { - "name": "osu! VisualTests (Release, .NETFramework)", - "request": "launch", - "type": "clr", "program": "${workspaceRoot}/osu.Desktop/bin/Release/net461/osu!.exe", "args": [ "--tests" ], "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Release)", + "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, "env": {}, "console": "internalConsole" }, { - "name": "osu! VisualTests (Release, .NETCore)", + "name": "osu! (Debug, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", "request": "launch", - "type": "coreclr", - "program": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll", - "args": [ - "--tests" - ], - "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Release)", - "runtimeExecutable": null, - "env": {}, - "console": "internalConsole" - }, - { - "name": "osu! (Debug, .NETFramework)", - "request": "launch", - "type": "clr", "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net461/osu!.exe", "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Debug)", + "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, "env": {}, "console": "internalConsole" }, { - "name": "osu! (Debug, .NETCore)", + "name": "osu! (Release, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", "request": "launch", - "type": "coreclr", - "program": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll", - "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Debug)", - "runtimeExecutable": null, - "env": {}, - "console": "internalConsole" - }, - { - "name": "osu! (Release, .NETFramework)", - "request": "launch", - "type": "clr", "program": "${workspaceRoot}/osu.Desktop/bin/Release/net461/osu!.exe", "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Release)", + "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, "env": {}, "console": "internalConsole" }, { - "name": "osu! (Release, .NETCore)", - "request": "launch", + "name": "VisualTests (Debug, netcoreapp2.0)", "type": "coreclr", - "program": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll", + "--tests" + ], "cwd": "${workspaceRoot}", - "preLaunchTask": "Build (Release)", - "runtimeExecutable": null, + "preLaunchTask": "Build (Debug, dotnet)", + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll", + "--tests" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, dotnet)", + "env": {}, + "console": "internalConsole" + }, + { + "name": "osu! (Debug, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll", + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, dotnet)", + "env": {}, + "console": "internalConsole" + }, + { + "name": "osu! (Release, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll", + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, dotnet)", "env": {}, "console": "internalConsole" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f67d7a8c4e..3378a83616 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,70 +2,82 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", - "tasks": [{ - "label": "Build (Debug)", - "type": "shell", - "command": "msbuild", - "args": [ - "/p:GenerateFullPaths=true", - "/p:DebugType=portable", - "/m", - "/v:m" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": "$msCompile" - }, + "tasks": [ { - "label": "Build (Release)", + "label": "Build (Debug, msbuild)", "type": "shell", "command": "msbuild", "args": [ - "/p:Configuration=Release", - "/p:DebugType=portable", + "/p:TargetFramework=net461", "/p:GenerateFullPaths=true", "/m", - "/v:m" + "/verbosity:m" ], "group": "build", "problemMatcher": "$msCompile" }, { - "label": "Clean (Debug)", - "type": "shell", - "command": "msbuild", - "args": [ - "/p:DebugType=portable", - "/p:GenerateFullPaths=true", - "/m", - "/t:Clean", - "/v:m" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "Clean (Release)", + "label": "Build (Release, msbuild)", "type": "shell", "command": "msbuild", "args": [ "/p:Configuration=Release", + "/p:TargetFramework=net461", "/p:GenerateFullPaths=true", - "/p:DebugType=portable", "/m", - "/t:Clean", - "/v:m" + "/verbosity:m" ], + "group": "build", "problemMatcher": "$msCompile" }, { - "label": "Clean All", - "dependsOn": [ - "Clean (Debug)", - "Clean (Release)" + "label": "Build (Debug, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "/p:TargetFramework=netcoreapp2.0", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" ], + "group": "build", "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "/p:TargetFramework=netcoreapp2.0", + "/p:Configuration=Release", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Restore (net461)", + "type": "shell", + "command": "nuget", + "args": [ + "restore" + ], + "problemMatcher": [] + }, + { + "label": "Restore (netcoreapp2.0)", + "type": "shell", + "command": "dotnet", + "args": [ + "restore" + ], + "problemMatcher": [] } ] } \ No newline at end of file diff --git a/osu-framework b/osu-framework index ae633287dc..1ba94a8f0b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit ae633287dc44517f3f5fc66f1d0f5333f76cc261 +Subproject commit 1ba94a8f0b0f1c43488356bf4a5cd5892f01f7da diff --git a/osu-resources b/osu-resources index db406143da..dca651b26f 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit db406143da9e791efa04cdac4418c85a6831b739 +Subproject commit dca651b26fbdfa92fad9bc537497fd47ebcd4130 diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 335638f10b..4db4904fb7 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -2,7 +2,7 @@ - net461;netstandard2.0 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index c44ae0416d..0b4dca31c0 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -2,7 +2,7 @@ - net461;netstandard2.0 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 2b055fb047..d966a2f8ee 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -2,7 +2,7 @@ - net461;netstandard2.0 + net461;netstandard2.0;;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index e9a4a4e79a..9fbfe7f604 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -2,7 +2,7 @@ - net461;netstandard2.0 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 3a351a63a8..e92b2d6a0a 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -2,7 +2,7 @@ - net461;netstandard2.0 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 893456034d..bf5c26fbff 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -2,7 +2,7 @@ - net461;netstandard2.0 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true From 3f9b14704e4c5fed9427b386280195b5f1b715b9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 13:30:43 +0900 Subject: [PATCH 288/537] Fix extra semicolon --- osu-framework | 2 +- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 1ba94a8f0b..08ea2cf894 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 1ba94a8f0b0f1c43488356bf4a5cd5892f01f7da +Subproject commit 08ea2cf8943a22c168856f70ecdf172b43194af0 diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index d966a2f8ee..6162fe93d0 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -2,7 +2,7 @@ - net461;netstandard2.0;;netcoreapp2.0 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true From 76588a1029547b852d7a763ccf93deaaa1253a57 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 15:12:13 +0900 Subject: [PATCH 289/537] Target WinExe to remove console window --- osu-framework | 2 +- osu.Desktop/osu.Desktop.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 08ea2cf894..9a2679b406 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 08ea2cf8943a22c168856f70ecdf172b43194af0 +Subproject commit 9a2679b406daa4a605b5d8e1c4a9cde1cd9aa891 diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 9c2a1713cb..eae61da304 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -3,7 +3,7 @@ net461;netcoreapp2.0 - Exe + WinExe AnyCPU true ppy Pty Ltd From 35ef9d99c679eb05b40cbd3a59dccf2313252bff Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 15:44:55 +0900 Subject: [PATCH 290/537] Only compile with net461 in visual studio --- osu.Desktop/osu.Desktop.csproj | 3 ++- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 3 ++- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 3 ++- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 3 ++- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 3 ++- osu.Game.Tests/osu.Game.Tests.csproj | 3 ++- osu.Game/osu.Game.csproj | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index eae61da304..c7ffbd78de 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -2,7 +2,8 @@ - net461;netcoreapp2.0 + net461 + net461;netcoreapp2.0 WinExe AnyCPU true diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 4db4904fb7..62d3fc008e 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -2,7 +2,8 @@ - net461;netstandard2.0;netcoreapp2.0 + net461 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 0b4dca31c0..09bbce1893 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -2,7 +2,8 @@ - net461;netstandard2.0;netcoreapp2.0 + net461 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 6162fe93d0..6f16197856 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -2,7 +2,8 @@ - net461;netstandard2.0;netcoreapp2.0 + net461 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 9fbfe7f604..90efa4d615 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -2,7 +2,8 @@ - net461;netstandard2.0;netcoreapp2.0 + net461 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index e92b2d6a0a..52a41c23f2 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -2,7 +2,8 @@ - net461;netstandard2.0;netcoreapp2.0 + net461 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bf5c26fbff..64176ab706 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -2,7 +2,8 @@ - net461;netstandard2.0;netcoreapp2.0 + net461 + net461;netstandard2.0;netcoreapp2.0 Library AnyCPU true From e5df2946aac1160b1c6e6c4cada882f55a946e2b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 15:49:16 +0900 Subject: [PATCH 291/537] Bump OpenTK version + update submodules --- OpenTK.props | 2 +- osu-framework | 2 +- osu-resources | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenTK.props b/OpenTK.props index 6ab847b6cc..8828f76cb1 100644 --- a/OpenTK.props +++ b/OpenTK.props @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/osu-framework b/osu-framework index 9a2679b406..dffaee9d66 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 9a2679b406daa4a605b5d8e1c4a9cde1cd9aa891 +Subproject commit dffaee9d66ec15d1e0a6d8561867ff3853624bd9 diff --git a/osu-resources b/osu-resources index dca651b26f..3958127d95 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit dca651b26fbdfa92fad9bc537497fd47ebcd4130 +Subproject commit 3958127d95ee230021e9946c989400e71c9dbb3c From d2c5fa72dcb949e021d975cbdc2c7cb8d4610b53 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 16:03:25 +0900 Subject: [PATCH 292/537] Make appveyor only compile net461 --- appveyor.yml | 2 ++ osu-framework | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index e63f6ea55c..6388b54db5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,8 @@ install: before_build: - cmd: CodeFileSanity.exe - cmd: nuget restore -verbosity quiet +environment: + TargetFramework: net461 build: project: osu.sln parallel: true diff --git a/osu-framework b/osu-framework index dffaee9d66..f0ed38258b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit dffaee9d66ec15d1e0a6d8561867ff3853624bd9 +Subproject commit f0ed38258b61622fa2e500f010a8a6ff1200995a From 5159127bcea889b394295efd1d9c6debbaac2565 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 18:07:09 +0900 Subject: [PATCH 293/537] Fix SQLite failing to initialize on test projects --- osu.Desktop/Program.cs | 1 - osu.Desktop/osu.Desktop.csproj | 1 - osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 1 + osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 1 + osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 1 + osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 1 + osu.Game.Tests/osu.Game.Tests.csproj | 1 + osu.Game/Database/OsuDbContext.cs | 6 ++++++ osu.Game/osu.Game.csproj | 1 + 9 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 5e6b5cc701..25beb9a7d4 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -19,7 +19,6 @@ namespace osu.Desktop public static int Main(string[] args) { // required to initialise native SQLite libraries on some platforms. - SQLitePCL.Batteries_V2.Init(); if (!RuntimeInfo.IsMono) useMulticoreJit(); diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index c7ffbd78de..cca7e70632 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -35,7 +35,6 @@ - diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 62d3fc008e..37702fad56 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -19,6 +19,7 @@ + diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 09bbce1893..ebe78a039c 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -19,6 +19,7 @@ + diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 6f16197856..6b741ef3c9 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -19,6 +19,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 90efa4d615..b4e7ab0658 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -19,6 +19,7 @@ + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 52a41c23f2..30104dee7f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -25,6 +25,7 @@ + diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index aa1f523a80..a4b0c30478 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -33,6 +33,12 @@ namespace osu.Game.Database private static readonly Lazy logger = new Lazy(() => new OsuDbLoggerFactory()); + static OsuDbContext() + { + // required to initialise native SQLite libraries on some platforms. + SQLitePCL.Batteries_V2.Init(); + } + /// /// Create a new in-memory OsuDbContext instance. /// diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 64176ab706..dd4b0c0e02 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,6 +21,7 @@ + From c6b9c4b58857eb07be49826d4ff1f8d2e18df56e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 18:40:59 +0900 Subject: [PATCH 294/537] Fix startup failures --- osu.Desktop/osu.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index cca7e70632..c7ffbd78de 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -35,6 +35,7 @@ + From b9f456402addea12ac0a35659636ed5a02c6f5fa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 18:55:55 +0900 Subject: [PATCH 295/537] Re-implement OsuGameDesktop icon --- osu-framework | 2 +- osu.Desktop/OsuGameDesktop.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index f0ed38258b..8be3928da8 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit f0ed38258b61622fa2e500f010a8a6ff1200995a +Subproject commit 8be3928da807b4bddb0557308f8e223cc0a12663 diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index d8c1d47efb..fddc6af2f4 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -2,8 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Drawing; using System.IO; using System.Linq; +using System.Reflection; using System.Threading.Tasks; using osu.Desktop.Overlays; using osu.Framework.Graphics.Containers; @@ -98,7 +100,7 @@ namespace osu.Desktop { desktopWindow.CursorState |= CursorState.Hidden; - // desktopWindow.Icon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); + desktopWindow.Icon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); desktopWindow.Title = Name; desktopWindow.FileDrop += fileDrop; From 0b38c8ec4fc25dac94bce0945ae8433df0f7107a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Mar 2018 18:58:19 +0900 Subject: [PATCH 296/537] Fix some VS specific issues --- osu.Desktop/Properties/launchSettings.json | 9 +++++++++ osu.Desktop/osu.Desktop.csproj | 3 +++ 2 files changed, 12 insertions(+) create mode 100644 osu.Desktop/Properties/launchSettings.json diff --git a/osu.Desktop/Properties/launchSettings.json b/osu.Desktop/Properties/launchSettings.json new file mode 100644 index 0000000000..3842c10a4e --- /dev/null +++ b/osu.Desktop/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "osu.Desktop": { + // this avoids an ugly command window appearing when running without debugger attached from VS2017 + "commandName": "Executable", + "executablePath": ".\\osu!.exe" + } + } +} \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index cca7e70632..7dfb2aa6cb 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -23,6 +23,9 @@ $(DefineConstants);NET_FRAMEWORK + + osu.Desktop.Program + From dd5bbbbd9fe1db66a86c7c20cbbe39755fabf795 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 26 Mar 2018 19:11:23 +0900 Subject: [PATCH 297/537] Define testable projects --- TestableProject.props | 8 ++++++++ osu-framework | 2 +- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 2 +- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 2 +- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 2 +- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 1 + 8 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 TestableProject.props diff --git a/TestableProject.props b/TestableProject.props new file mode 100644 index 0000000000..40b3fde99e --- /dev/null +++ b/TestableProject.props @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/osu-framework b/osu-framework index 8be3928da8..0b0f756c73 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 8be3928da807b4bddb0557308f8e223cc0a12663 +Subproject commit 0b0f756c7383685cf7a279b38a6eb92131739f03 diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 37702fad56..9d011e2de2 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -1,6 +1,7 @@  + net461 net461;netstandard2.0;netcoreapp2.0 @@ -20,7 +21,6 @@ - diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index ebe78a039c..cc6c770bbf 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -1,6 +1,7 @@  + net461 net461;netstandard2.0;netcoreapp2.0 @@ -20,7 +21,6 @@ - diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 6b741ef3c9..3d512ad53e 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -1,6 +1,7 @@  + net461 net461;netstandard2.0;netcoreapp2.0 @@ -20,7 +21,6 @@ - diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index b4e7ab0658..dc08e71446 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -1,6 +1,7 @@  + net461 net461;netstandard2.0;netcoreapp2.0 @@ -20,7 +21,6 @@ - diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 30104dee7f..5717066f45 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,6 +1,7 @@  + net461 net461;netstandard2.0;netcoreapp2.0 @@ -26,7 +27,6 @@ - diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index dd4b0c0e02..643ada16aa 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,6 +1,7 @@  + net461 net461;netstandard2.0;netcoreapp2.0 From 868a221b6c31fc18a2c5e5434a19c014d4aab5b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Mar 2018 19:42:33 +0900 Subject: [PATCH 298/537] Fix VisualTests build configuration --- osu.Desktop/Properties/launchSettings.json | 9 --------- osu.Desktop/osu.Desktop.csproj | 8 ++++++-- osu.sln | 1 - 3 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 osu.Desktop/Properties/launchSettings.json diff --git a/osu.Desktop/Properties/launchSettings.json b/osu.Desktop/Properties/launchSettings.json deleted file mode 100644 index 3842c10a4e..0000000000 --- a/osu.Desktop/Properties/launchSettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "profiles": { - "osu.Desktop": { - // this avoids an ugly command window appearing when running without debugger attached from VS2017 - "commandName": "Executable", - "executablePath": ".\\osu!.exe" - } - } -} \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 1e0d98620c..5d346603db 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -14,6 +14,7 @@ osu!lazer osu!lazer lazer.ico + $(CONFIGURATIONS);VisualTests 0.0.0.0 0.0.0.0 - + \ No newline at end of file diff --git a/osu-framework b/osu-framework index 0b0f756c73..857b1c4e6d 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 0b0f756c7383685cf7a279b38a6eb92131739f03 +Subproject commit 857b1c4e6d8e8fa4c674cd5bff34df2d16978f06 From 28c737ad7290d65b3c411458e8cee95574f36866 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Mar 2018 14:36:54 +0900 Subject: [PATCH 301/537] Update opentk + framework --- OpenTK.props | 2 +- osu-framework | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenTK.props b/OpenTK.props index f4456cd6eb..8708670f2c 100644 --- a/OpenTK.props +++ b/OpenTK.props @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/osu-framework b/osu-framework index 857b1c4e6d..b32a4ad740 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 857b1c4e6d8e8fa4c674cd5bff34df2d16978f06 +Subproject commit b32a4ad740e8f03c5069c246a1269774f90fe972 From 9674e8d197565d1c9721220ada73eba0ed23ab7b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 15:05:41 +0900 Subject: [PATCH 302/537] Add back mania to VisualTests configuration --- osu.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln b/osu.sln index 5e99e838eb..43a7fdf6c3 100644 --- a/osu.sln +++ b/osu.sln @@ -71,6 +71,7 @@ Global {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Release|Any CPU.ActiveCfg = Release|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU From b290d2d03911d080905120753a81396f9ba113d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 15:06:02 +0900 Subject: [PATCH 303/537] Move NUnit includes to individual projects --- TestableProject.props | 8 -------- osu-framework | 2 +- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 5 ++++- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 5 ++++- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 5 ++++- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 5 ++++- osu.Game.Tests/osu.Game.Tests.csproj | 5 ++++- osu.Game/osu.Game.csproj | 5 ++++- 8 files changed, 25 insertions(+), 15 deletions(-) delete mode 100644 TestableProject.props diff --git a/TestableProject.props b/TestableProject.props deleted file mode 100644 index 40b3fde99e..0000000000 --- a/TestableProject.props +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/osu-framework b/osu-framework index b32a4ad740..5ab18a48f9 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b32a4ad740e8f03c5069c246a1269774f90fe972 +Subproject commit 5ab18a48f9a83d2316526f87db3158ca638d42c5 diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 9d011e2de2..00933445c9 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -1,7 +1,6 @@  - net461 net461;netstandard2.0;netcoreapp2.0 @@ -15,12 +14,16 @@ catch the fruit. to the beat. osu.Game.Rulesets.Catch + + + + diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index cc6c770bbf..155dd8b4f6 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -1,7 +1,6 @@  - net461 net461;netstandard2.0;netcoreapp2.0 @@ -15,12 +14,16 @@ smash the keys. to the beat. osu.Game.Rulests.Mania + + + + diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 3d512ad53e..af32f48b36 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -1,7 +1,6 @@  - net461 net461;netstandard2.0;netcoreapp2.0 @@ -15,12 +14,16 @@ click the circles. to the beat. osu.Game.Rulesets.Osu + + + + diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index dc08e71446..9c5353c4fe 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -1,7 +1,6 @@  - net461 net461;netstandard2.0;netcoreapp2.0 @@ -15,12 +14,16 @@ bash the drum. to the beat. osu.Game.Rulesets.Taiko + + + + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 5717066f45..0f09d97f0f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,7 +1,6 @@  - net461 net461;netstandard2.0;netcoreapp2.0 @@ -16,6 +15,9 @@ NU1701 + + + @@ -28,6 +30,7 @@ + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 643ada16aa..014694ffef 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,7 +1,6 @@  - net461 net461;netstandard2.0;netcoreapp2.0 @@ -16,6 +15,9 @@ osu.Game 0 + + + @@ -26,6 +28,7 @@ + \ No newline at end of file From 988141408f9f3879efca39bbab623f82a7d942a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 15:25:56 +0900 Subject: [PATCH 304/537] Update copyright year --- osu-framework | 2 +- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 5ab18a48f9..a6cea54d05 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 5ab18a48f9a83d2316526f87db3158ca638d42c5 +Subproject commit a6cea54d056ef6f565752f45e8a0a28a00e99030 diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 014694ffef..34a5944e7f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -9,7 +9,7 @@ true ppy Pty Ltd 1.0.0.0 - ppy Pty Ltd 2007-2017 + ppy Pty Ltd 2007-2018 osu.Game click the circles. to the beat. osu.Game From 1a8aa8746972f55a1f9247304538a3fdc963cb66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 18:55:27 +0900 Subject: [PATCH 305/537] Make everything better --- .vscode/tasks.json | 2 ++ OpenTK.props | 6 ------ osu-framework | 2 +- osu-resources | 2 +- osu.Desktop/OsuGameDesktop.cs | 3 +-- osu.Desktop/osu.Desktop.csproj | 4 +--- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 4 +--- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 4 +--- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 4 +--- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 4 +--- osu.Game.Tests/osu.Game.Tests.csproj | 4 +--- osu.Game/osu.Game.csproj | 4 +--- 12 files changed, 12 insertions(+), 31 deletions(-) delete mode 100644 OpenTK.props diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 3378a83616..b1d2c6b57d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -37,6 +37,7 @@ "args": [ "build", "--no-restore", + "osu.Desktop", "/p:TargetFramework=netcoreapp2.0", "/p:GenerateFullPaths=true", "/m", @@ -52,6 +53,7 @@ "args": [ "build", "--no-restore", + "osu.Desktop", "/p:TargetFramework=netcoreapp2.0", "/p:Configuration=Release", "/p:GenerateFullPaths=true", diff --git a/OpenTK.props b/OpenTK.props deleted file mode 100644 index 8708670f2c..0000000000 --- a/OpenTK.props +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/osu-framework b/osu-framework index a6cea54d05..ced746ac27 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit a6cea54d056ef6f565752f45e8a0a28a00e99030 +Subproject commit ced746ac27b30019ef2cdc3a15e6df4a18ac9585 diff --git a/osu-resources b/osu-resources index 3958127d95..b335a7195d 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 3958127d95ee230021e9946c989400e71c9dbb3c +Subproject commit b335a7195d60ab8d83ae9eba1150ee022163f3ba diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index fddc6af2f4..3704d2d5d8 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Drawing; using System.IO; using System.Linq; using System.Reflection; @@ -100,7 +99,7 @@ namespace osu.Desktop { desktopWindow.CursorState |= CursorState.Hidden; - desktopWindow.Icon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); + desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); desktopWindow.Title = Name; desktopWindow.FileDrop += fileDrop; diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 5d346603db..2262bbc515 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -1,9 +1,7 @@  - - net461 - net461;netcoreapp2.0 + net461;netcoreapp2.0 WinExe AnyCPU true diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 00933445c9..aa4d06cf64 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -1,9 +1,7 @@  - - net461 - net461;netstandard2.0;netcoreapp2.0 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 155dd8b4f6..a1f08e9299 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -1,9 +1,7 @@  - - net461 - net461;netstandard2.0;netcoreapp2.0 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index af32f48b36..a58d3b6795 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -1,9 +1,7 @@  - - net461 - net461;netstandard2.0;netcoreapp2.0 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 9c5353c4fe..d7794aaafd 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -1,9 +1,7 @@  - - net461 - net461;netstandard2.0;netcoreapp2.0 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 0f09d97f0f..5b9a1f11e4 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,9 +1,7 @@  - - net461 - net461;netstandard2.0;netcoreapp2.0 + netstandard2.0 Library AnyCPU true diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 34a5944e7f..b42d76e331 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,9 +1,7 @@  - - net461 - net461;netstandard2.0;netcoreapp2.0 + netstandard2.0 Library AnyCPU true From 04a7754049f9a2635aa35dfc7a917aa405be5398 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 18:57:20 +0900 Subject: [PATCH 306/537] Make squirrel local so ifdef is nicer --- osu.Desktop/Overlays/SquirrelUpdateManager.cs | 158 ++++++++++++++++++ osu.Desktop/Overlays/VersionManager.cs | 143 +--------------- 2 files changed, 160 insertions(+), 141 deletions(-) create mode 100644 osu.Desktop/Overlays/SquirrelUpdateManager.cs diff --git a/osu.Desktop/Overlays/SquirrelUpdateManager.cs b/osu.Desktop/Overlays/SquirrelUpdateManager.cs new file mode 100644 index 0000000000..1c1ba6a3ad --- /dev/null +++ b/osu.Desktop/Overlays/SquirrelUpdateManager.cs @@ -0,0 +1,158 @@ +#if NET_FRAMEWORK +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; +using osu.Game; +using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; +using OpenTK; +using OpenTK.Graphics; +using Squirrel; + +namespace osu.Desktop.Overlays +{ + public class SquirrelUpdateManager : Component + { + private UpdateManager updateManager; + private NotificationOverlay notificationOverlay; + + public void PrepareUpdate() + { + // Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here + UpdateManager.RestartAppWhenExited().Wait(); + } + + [BackgroundDependencyLoader] + private void load(NotificationOverlay notification, OsuGameBase game) + { + notificationOverlay = notification; + + if (game.IsDeployedBuild) + Schedule(() => checkForUpdateAsync()); + } + + private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) + { + //should we schedule a retry on completion of this check? + bool scheduleRetry = true; + + try + { + if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); + + var info = await updateManager.CheckForUpdate(!useDeltaPatching); + if (info.ReleasesToApply.Count == 0) + //no updates available. bail and retry later. + return; + + if (notification == null) + { + notification = new UpdateProgressNotification(this) { State = ProgressNotificationState.Active }; + Schedule(() => notificationOverlay.Post(notification)); + } + + notification.Progress = 0; + notification.Text = @"Downloading update..."; + + try + { + await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f); + + notification.Progress = 0; + notification.Text = @"Installing update..."; + + await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f); + + notification.State = ProgressNotificationState.Completed; + } + catch (Exception e) + { + if (useDeltaPatching) + { + Logger.Error(e, @"delta patching failed!"); + + //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) + //try again without deltas. + checkForUpdateAsync(false, notification); + scheduleRetry = false; + } + else + { + Logger.Error(e, @"update failed!"); + } + } + } + catch (Exception) + { + // we'll ignore this and retry later. can be triggered by no internet connection or thread abortion. + } + finally + { + if (scheduleRetry) + { + if (notification != null) + notification.State = ProgressNotificationState.Cancelled; + + //check again in 30 minutes. + Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30); + } + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + updateManager?.Dispose(); + } + + private class UpdateProgressNotification : ProgressNotification + { + private readonly SquirrelUpdateManager updateManager; + private OsuGame game; + + public UpdateProgressNotification(SquirrelUpdateManager updateManager) + { + this.updateManager = updateManager; + } + + protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification + { + Text = @"Update ready to install. Click to restart!", + Activated = () => + { + updateManager.PrepareUpdate(); + game.GracefullyExit(); + return true; + } + }; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, OsuGame game) + { + this.game = game; + + IconContent.AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow) + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_upload, + Colour = Color4.White, + Size = new Vector2(20), + } + }); + } + } + } +} +#endif diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 357cd49a5c..b61603dcc5 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -5,9 +5,7 @@ using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Development; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game; @@ -19,23 +17,13 @@ using osu.Game.Overlays.Notifications; using OpenTK; using OpenTK.Graphics; -#if NET_FRAMEWORK -using System; -using osu.Framework.Logging; -using Squirrel; -#endif - namespace osu.Desktop.Overlays { public class VersionManager : OverlayContainer { -#if NET_FRAMEWORK - private UpdateManager updateManager; -#endif - - private NotificationOverlay notificationOverlay; private OsuConfigManager config; private OsuGameBase game; + private NotificationOverlay notificationOverlay; public override bool HandleKeyboardInput => false; public override bool HandleMouseInput => false; @@ -102,8 +90,7 @@ namespace osu.Desktop.Overlays }; #if NET_FRAMEWORK - if (game.IsDeployedBuild) - checkForUpdateAsync(); + Add(new SquirrelUpdateManager()); #endif } @@ -143,90 +130,6 @@ namespace osu.Desktop.Overlays } } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - -#if NET_FRAMEWORK - updateManager?.Dispose(); -#endif - } - -#if NET_FRAMEWORK - private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) - { - //should we schedule a retry on completion of this check? - bool scheduleRetry = true; - - try - { - if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); - - var info = await updateManager.CheckForUpdate(!useDeltaPatching); - if (info.ReleasesToApply.Count == 0) - //no updates available. bail and retry later. - return; - - if (notification == null) - { - notification = new UpdateProgressNotification { State = ProgressNotificationState.Active }; - Schedule(() => notificationOverlay.Post(notification)); - } - - Schedule(() => - { - notification.Progress = 0; - notification.Text = @"Downloading update..."; - }); - - try - { - await updateManager.DownloadReleases(info.ReleasesToApply, p => Schedule(() => notification.Progress = p / 100f)); - - Schedule(() => - { - notification.Progress = 0; - notification.Text = @"Installing update..."; - }); - - await updateManager.ApplyReleases(info, p => Schedule(() => notification.Progress = p / 100f)); - - Schedule(() => notification.State = ProgressNotificationState.Completed); - } - catch (Exception e) - { - if (useDeltaPatching) - { - Logger.Error(e, @"delta patching failed!"); - - //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) - //try again without deltas. - checkForUpdateAsync(false, notification); - scheduleRetry = false; - } - else - { - Logger.Error(e, @"update failed!"); - } - } - } - catch (Exception) - { - // we'll ignore this and retry later. can be triggered by no internet connection or thread abortion. - } - finally - { - if (scheduleRetry) - { - //check again in 30 minutes. - Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30); - if (notification != null) - notification.State = ProgressNotificationState.Cancelled; - } - } - } -#endif - protected override void PopIn() { this.FadeIn(1000); @@ -235,47 +138,5 @@ namespace osu.Desktop.Overlays protected override void PopOut() { } - - private class UpdateProgressNotification : ProgressNotification - { - private OsuGame game; - - protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification - { - Text = @"Update ready to install. Click to restart!", - Activated = () => - { - // Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here -#if NET_FRAMEWORK - UpdateManager.RestartAppWhenExited().Wait(); -#endif - game.GracefullyExit(); - return true; - } - }; - - [BackgroundDependencyLoader] - private void load(OsuColour colours, OsuGame game) - { - this.game = game; - - IconContent.AddRange(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow) - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.fa_upload, - Colour = Color4.White, - Size = new Vector2(20), - } - }); - } - } } } From 75a413a40e705defa1c0cc719869fd57fbecaaf0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 19:19:09 +0900 Subject: [PATCH 307/537] Move NoWarns --- osu-framework | 2 +- osu.Desktop/osu.Desktop.csproj | 3 --- osu.Game.Tests/osu.Game.Tests.csproj | 6 +++--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/osu-framework b/osu-framework index ced746ac27..bef78377e7 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit ced746ac27b30019ef2cdc3a15e6df4a18ac9585 +Subproject commit bef78377e74056b2b36611b74ece21d18935fcbf diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 2262bbc515..6125ec82c3 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -15,9 +15,6 @@ $(CONFIGURATIONS);VisualTests 0.0.0.0 0.0.0.0 - - NU1701 $(DefineConstants);NET_FRAMEWORK diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 5b9a1f11e4..0a3bd4b417 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -10,8 +10,6 @@ ppy Pty Ltd 2007-2017 osu.Game.Tests osu.Game.Tests - - NU1701 @@ -27,7 +25,9 @@ - + + NU1701 + From 524044d4876e1a39b151988457fd97b7f5c22b0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 19:19:28 +0900 Subject: [PATCH 308/537] Add licence header to SquirrelUpdateManager --- osu.Desktop/Overlays/SquirrelUpdateManager.cs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Desktop/Overlays/SquirrelUpdateManager.cs b/osu.Desktop/Overlays/SquirrelUpdateManager.cs index 1c1ba6a3ad..61d8a75ae3 100644 --- a/osu.Desktop/Overlays/SquirrelUpdateManager.cs +++ b/osu.Desktop/Overlays/SquirrelUpdateManager.cs @@ -1,4 +1,7 @@ -#if NET_FRAMEWORK +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +#if NET_FRAMEWORK using System; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -119,16 +122,19 @@ namespace osu.Desktop.Overlays this.updateManager = updateManager; } - protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification + protected override Notification CreateCompletionNotification() { - Text = @"Update ready to install. Click to restart!", - Activated = () => + return new ProgressCompletionNotification { - updateManager.PrepareUpdate(); - game.GracefullyExit(); - return true; - } - }; + Text = @"Update ready to install. Click to restart!", + Activated = () => + { + updateManager.PrepareUpdate(); + game.GracefullyExit(); + return true; + } + }; + } [BackgroundDependencyLoader] private void load(OsuColour colours, OsuGame game) From d76bc1a8ad01bbf266f0790b97e1c829683185b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 19:19:09 +0900 Subject: [PATCH 309/537] Revert "Move NoWarns" This reverts commit 75a413a40e705defa1c0cc719869fd57fbecaaf0. --- osu-framework | 2 +- osu.Desktop/osu.Desktop.csproj | 3 +++ osu.Game.Tests/osu.Game.Tests.csproj | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu-framework b/osu-framework index bef78377e7..5bad477858 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit bef78377e74056b2b36611b74ece21d18935fcbf +Subproject commit 5bad47785877250f0589f02396f5dc7c9bd2b8f0 diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 6125ec82c3..2262bbc515 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -15,6 +15,9 @@ $(CONFIGURATIONS);VisualTests 0.0.0.0 0.0.0.0 + + NU1701 $(DefineConstants);NET_FRAMEWORK diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 0a3bd4b417..5b9a1f11e4 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -10,6 +10,8 @@ ppy Pty Ltd 2007-2017 osu.Game.Tests osu.Game.Tests + + NU1701 @@ -25,9 +27,7 @@ - - NU1701 - + From efac68a8f5073a55de1e50f890ab15c3af6b0348 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 22:36:43 +0900 Subject: [PATCH 310/537] Update deploy script --- osu.Desktop.Deploy/Program.cs | 12 +++++------- osu.Desktop.Deploy/osu.Desktop.Deploy.csproj | 11 ++++++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index e5e0702e6d..3c1451d555 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -16,8 +16,9 @@ namespace osu.Desktop.Deploy { internal static class Program { - private const string nuget_path = @"packages\NuGet.CommandLine.4.3.0\tools\NuGet.exe"; - private const string squirrel_path = @"packages\squirrel.windows.1.7.8\tools\Squirrel.exe"; + private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); + private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe"); + private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.7.8\tools\Squirrel.exe"); private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"; public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"]; @@ -92,9 +93,6 @@ namespace osu.Desktop.Deploy codeSigningPassword = readLineMasked(); } - write("Restoring NuGet packages..."); - runCommand(nuget_path, "restore " + solutionPath); - write("Updating AssemblyInfo..."); updateCsprojVersion(version); @@ -103,7 +101,7 @@ namespace osu.Desktop.Deploy runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln"); write("Creating NuGet deployment package..."); - runCommand(nuget_path, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}"); + runCommand(nugetPath, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}"); //prune once before checking for files so we can avoid erroring on files which aren't even needed for this build. pruneReleases(); @@ -111,7 +109,7 @@ namespace osu.Desktop.Deploy checkReleaseFiles(); write("Running squirrel build..."); - runCommand(squirrel_path, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi"); + runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi"); //prune again to clean up before upload. pruneReleases(); diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index ccbf351bda..eab54bd10b 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -1,5 +1,5 @@ - + net461 Exe @@ -11,11 +11,12 @@ osu.Desktop.Deploy - + - - - + + + + \ No newline at end of file From d47d7a3dd461f5a7ff0e64b881c5abd7ad50b36a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Mar 2018 22:47:01 +0900 Subject: [PATCH 311/537] Update submodules --- osu-framework | 2 +- osu-resources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 5bad477858..b59149e1ce 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 5bad47785877250f0589f02396f5dc7c9bd2b8f0 +Subproject commit b59149e1cebe28675dcd2ebd014e5793d9626c09 diff --git a/osu-resources b/osu-resources index b335a7195d..c3848d8b1c 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit b335a7195d60ab8d83ae9eba1150ee022163f3ba +Subproject commit c3848d8b1c84966abe851d915bcca878415614b4 From 458594d24d19511882960d2105b930cd09801659 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Tue, 27 Mar 2018 18:03:57 +0300 Subject: [PATCH 312/537] Qualifier 'this.' is redundant --- osu.Game/Graphics/ScreenshotManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 7d50a298ec..434a9d0a72 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -41,7 +41,7 @@ namespace osu.Game.Graphics this.host = host; this.storage = storage.GetStorageForDirectory(@"screenshots"); this.notificationOverlay = notificationOverlay; - this.menuCursorContainer = cursorOverrideContainer.Cursor; + menuCursorContainer = cursorOverrideContainer.Cursor; screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); captureMenuCursor = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor); From 506e27a30e65e5ecf18124bf9f7b188c4228d992 Mon Sep 17 00:00:00 2001 From: DrabWeb Date: Tue, 27 Mar 2018 17:59:58 -0300 Subject: [PATCH 313/537] Cleanup. --- osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs | 2 +- osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs index 712c7a34d6..c7f767d3b2 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs @@ -5,7 +5,7 @@ namespace osu.Game.Beatmaps { public enum BeatmapSetOnlineStatus { - None, + None = -3, Graveyard = -2, WIP = -1, Pending = 0, diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 3c98ddc094..8ea7a538f9 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -20,6 +20,7 @@ namespace osu.Game.Beatmaps.Drawables get { return status; } set { + if (value == status) return; status = value; statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpper(); From 84bb96740f36294ac2098f769f2f40a1ebc88059 Mon Sep 17 00:00:00 2001 From: DrabWeb Date: Tue, 27 Mar 2018 18:27:30 -0300 Subject: [PATCH 314/537] Fix merge error. --- osu.Game/osu.Game.csproj | 62 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0827768afe..b42d76e331 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,32 +1,32 @@ - - - - netstandard2.0 - Library - AnyCPU - true - ppy Pty Ltd - 1.0.0.0 - ppy Pty Ltd 2007-2018 - osu.Game - click the circles. to the beat. - osu.Game - 0 - - - - - - - - - - - - - - - - - + + + + netstandard2.0 + Library + AnyCPU + true + ppy Pty Ltd + 1.0.0.0 + ppy Pty Ltd 2007-2018 + osu.Game + click the circles. to the beat. + osu.Game + 0 + + + + + + + + + + + + + + + + + \ No newline at end of file From 94b1d3e4af91f9b34f7f32158c31da849cb24c06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 12:33:01 +0900 Subject: [PATCH 315/537] Make VisualTests/nUnit work again --- osu-framework | 2 +- osu.Desktop/Program.cs | 3 -- osu.Game.Tests/osu.Game.Tests.csproj | 38 ++++++++----------- .../Tests}/OsuTestBrowser.cs | 5 +-- osu.Game/Tests/VisualTestRunner.cs | 22 +++++++++++ osu.TestProject.props | 26 +++++++++++++ 6 files changed, 66 insertions(+), 30 deletions(-) rename {osu.Desktop => osu.Game/Tests}/OsuTestBrowser.cs (87%) create mode 100644 osu.Game/Tests/VisualTestRunner.cs create mode 100644 osu.TestProject.props diff --git a/osu-framework b/osu-framework index b59149e1ce..71fe258bf2 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b59149e1cebe28675dcd2ebd014e5793d9626c09 +Subproject commit 71fe258bf26dd2b8407729fd09f837ca26060d62 diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 25beb9a7d4..7258610f90 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -44,9 +44,6 @@ namespace osu.Desktop { switch (args.FirstOrDefault() ?? string.Empty) { - case "--tests": - host.Run(new OsuTestBrowser()); - break; default: host.Run(new OsuGameDesktop(args)); break; diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 5b9a1f11e4..a5a78f6fc7 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,39 +1,31 @@  - - - netstandard2.0 - Library - AnyCPU - true - ppy Pty Ltd - 1.0.0.0 - ppy Pty Ltd 2007-2017 - osu.Game.Tests - osu.Game.Tests - - NU1701 - + + + net461;netcoreapp2.0 + WinExe + AnyCPU + true + ppy Pty Ltd + click the circles. to the beat. + ppy Pty Ltd 2007-2017 + + NU1701 + - - - + - - - - - - + \ No newline at end of file diff --git a/osu.Desktop/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs similarity index 87% rename from osu.Desktop/OsuTestBrowser.cs rename to osu.Game/Tests/OsuTestBrowser.cs index 7aae2604af..6d9b28b5a7 100644 --- a/osu.Desktop/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -3,12 +3,11 @@ using osu.Framework.Platform; using osu.Framework.Testing; -using osu.Game; using osu.Game.Screens.Backgrounds; -namespace osu.Desktop +namespace osu.Game.Tests { - internal class OsuTestBrowser : OsuGameBase + public class OsuTestBrowser : OsuGameBase { protected override void LoadComplete() { diff --git a/osu.Game/Tests/VisualTestRunner.cs b/osu.Game/Tests/VisualTestRunner.cs new file mode 100644 index 0000000000..d8a4143368 --- /dev/null +++ b/osu.Game/Tests/VisualTestRunner.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework; +using osu.Framework.Platform; + +namespace osu.Game.Tests +{ + public static class VisualTestRunner + { + [STAThread] + public static int Main(string[] args) + { + using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) + { + host.Run(new OsuTestBrowser()); + return 0; + } + } + } +} diff --git a/osu.TestProject.props b/osu.TestProject.props new file mode 100644 index 0000000000..50ee5848fc --- /dev/null +++ b/osu.TestProject.props @@ -0,0 +1,26 @@ + + + + $(DefineConstants);NET_FRAMEWORK + + + osu.Game.Tests.VisualTestRunner + + + + + + + + + + + + + VisualTestRunner.cs + + + + false + + \ No newline at end of file From 4907f14dbedb0a34c201840bfca155e85313b6ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 13:00:18 +0900 Subject: [PATCH 316/537] Add back resources --- osu.Game.Tests/osu.Game.Tests.csproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index a5a78f6fc7..1c88cc49d4 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -21,6 +21,12 @@ + + + + + + From c355a0794c92b5d802f2a7e516250979cebf16b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 13:12:32 +0900 Subject: [PATCH 317/537] Let appveyor do its own discovery --- appveyor.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 6388b54db5..4c4b70827f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,10 +22,6 @@ build: project: osu.sln parallel: true verbosity: minimal -test: - assemblies: - only: - - 'osu.Desktop\**\*.dll' after_build: - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file From 0026b94cd31dfd533e83ade1585b9994920b4b89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 14:13:37 +0900 Subject: [PATCH 318/537] Consolidate and move ruleset test projects --- osu-framework | 2 +- .../CatchBeatmapConversionTest.cs | 0 .../TestCaseBananaShower.cs | 0 .../TestCaseCatchPlayer.cs | 0 .../TestCaseCatchStacker.cs | 0 .../TestCaseCatcherArea.cs | 0 .../TestCaseFruitObjects.cs | 0 .../TestCaseHyperdash.cs | 0 .../TestCasePerformancePoints.cs | 0 .../osu.Game.Rulesets.Catch.Tests.csproj | 6 ++++ .../Properties/AssemblyInfo.cs | 12 +++++++ .../osu.Game.Rulesets.Catch.csproj | 11 ------- .../ManiaBeatmapConversionTest.cs | 0 .../TestCaseAutoGeneration.cs | 0 .../TestCaseManiaHitObjects.cs | 0 .../TestCaseManiaPlayfield.cs | 0 .../TestCasePerformancePoints.cs | 0 .../osu.Game.Rulesets.Mania.Tests.csproj | 6 ++++ .../Properties/AssemblyInfo.cs | 12 +++++++ .../osu.Game.Rulesets.Mania.csproj | 11 ------- .../OsuBeatmapConversionTest.cs | 0 .../TestCaseEditor.cs | 0 .../TestCaseGameplayCursor.cs | 0 .../TestCaseHitCircle.cs | 0 .../TestCaseHitCircleHidden.cs | 0 .../TestCasePerformancePoints.cs | 0 .../TestCaseSlider.cs | 0 .../TestCaseSliderHidden.cs | 0 .../TestCaseSpinner.cs | 0 .../TestCaseSpinnerHidden.cs | 0 .../osu.Game.Rulesets.Osu.Tests.csproj | 6 ++++ .../Properties/AssemblyInfo.cs | 12 +++++++ .../osu.Game.Rulesets.Osu.csproj | 11 ------- .../TaikoBeatmapConversionTest.cs | 0 .../TestCaseInputDrum.cs | 0 .../TestCasePerformancePoints.cs | 0 .../TestCaseTaikoPlayfield.cs | 0 .../osu.Game.Rulesets.Taiko.Tests.csproj | 6 ++++ .../Properties/AssemblyInfo.cs | 12 +++++++ .../osu.Game.Rulesets.Taiko.csproj | 11 ------- osu.Game.Tests/osu.Game.Tests.csproj | 28 ---------------- osu.Game.props | 3 ++ osu.TestProject.props | 20 +++++++++--- osu.sln | 32 +++++++++++++++++++ 44 files changed, 124 insertions(+), 77 deletions(-) rename {osu.Game.Rulesets.Catch/Tests => osu.Game.Rulesets.Catch.Tests}/CatchBeatmapConversionTest.cs (100%) rename {osu.Game.Rulesets.Catch/Tests => osu.Game.Rulesets.Catch.Tests}/TestCaseBananaShower.cs (100%) rename {osu.Game.Rulesets.Catch/Tests => osu.Game.Rulesets.Catch.Tests}/TestCaseCatchPlayer.cs (100%) rename {osu.Game.Rulesets.Catch/Tests => osu.Game.Rulesets.Catch.Tests}/TestCaseCatchStacker.cs (100%) rename {osu.Game.Rulesets.Catch/Tests => osu.Game.Rulesets.Catch.Tests}/TestCaseCatcherArea.cs (100%) rename {osu.Game.Rulesets.Catch/Tests => osu.Game.Rulesets.Catch.Tests}/TestCaseFruitObjects.cs (100%) rename {osu.Game.Rulesets.Catch/Tests => osu.Game.Rulesets.Catch.Tests}/TestCaseHyperdash.cs (100%) rename {osu.Game.Rulesets.Catch/Tests => osu.Game.Rulesets.Catch.Tests}/TestCasePerformancePoints.cs (100%) create mode 100644 osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj create mode 100644 osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs rename {osu.Game.Rulesets.Mania/Tests => osu.Game.Rulesets.Mania.Tests}/ManiaBeatmapConversionTest.cs (100%) rename {osu.Game.Rulesets.Mania/Tests => osu.Game.Rulesets.Mania.Tests}/TestCaseAutoGeneration.cs (100%) rename {osu.Game.Rulesets.Mania/Tests => osu.Game.Rulesets.Mania.Tests}/TestCaseManiaHitObjects.cs (100%) rename {osu.Game.Rulesets.Mania/Tests => osu.Game.Rulesets.Mania.Tests}/TestCaseManiaPlayfield.cs (100%) rename {osu.Game.Rulesets.Mania/Tests => osu.Game.Rulesets.Mania.Tests}/TestCasePerformancePoints.cs (100%) create mode 100644 osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj create mode 100644 osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/OsuBeatmapConversionTest.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCaseEditor.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCaseGameplayCursor.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCaseHitCircle.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCaseHitCircleHidden.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCasePerformancePoints.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCaseSlider.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCaseSliderHidden.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCaseSpinner.cs (100%) rename {osu.Game.Rulesets.Osu/Tests => osu.Game.Rulesets.Osu.Tests}/TestCaseSpinnerHidden.cs (100%) create mode 100644 osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj create mode 100644 osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs rename {osu.Game.Rulesets.Taiko/Tests => osu.Game.Rulesets.Taiko.Tests}/TaikoBeatmapConversionTest.cs (100%) rename {osu.Game.Rulesets.Taiko/Tests => osu.Game.Rulesets.Taiko.Tests}/TestCaseInputDrum.cs (100%) rename {osu.Game.Rulesets.Taiko/Tests => osu.Game.Rulesets.Taiko.Tests}/TestCasePerformancePoints.cs (100%) rename {osu.Game.Rulesets.Taiko/Tests => osu.Game.Rulesets.Taiko.Tests}/TestCaseTaikoPlayfield.cs (100%) create mode 100644 osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj create mode 100644 osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs diff --git a/osu-framework b/osu-framework index 71fe258bf2..c69330f652 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 71fe258bf26dd2b8407729fd09f837ca26060d62 +Subproject commit c69330f652d34350a7bf3786016a3ce5f968c25f diff --git a/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs similarity index 100% rename from osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs rename to osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs similarity index 100% rename from osu.Game.Rulesets.Catch/Tests/TestCaseBananaShower.cs rename to osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchPlayer.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs similarity index 100% rename from osu.Game.Rulesets.Catch/Tests/TestCaseCatchPlayer.cs rename to osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs similarity index 100% rename from osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs rename to osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs similarity index 100% rename from osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs rename to osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs similarity index 100% rename from osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs rename to osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs similarity index 100% rename from osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs rename to osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Catch.Tests/TestCasePerformancePoints.cs similarity index 100% rename from osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs rename to osu.Game.Rulesets.Catch.Tests/TestCasePerformancePoints.cs 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 new file mode 100644 index 0000000000..561613a2e8 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0470b31824 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using System.Runtime.CompilerServices; +using osu.Framework.Testing; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")] +[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index aa4d06cf64..1337617578 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -12,18 +12,7 @@ catch the fruit. to the beat. osu.Game.Rulesets.Catch - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs similarity index 100% rename from osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs rename to osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseAutoGeneration.cs similarity index 100% rename from osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs rename to osu.Game.Rulesets.Mania.Tests/TestCaseAutoGeneration.cs diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseManiaHitObjects.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseManiaHitObjects.cs similarity index 100% rename from osu.Game.Rulesets.Mania/Tests/TestCaseManiaHitObjects.cs rename to osu.Game.Rulesets.Mania.Tests/TestCaseManiaHitObjects.cs diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseManiaPlayfield.cs similarity index 100% rename from osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs rename to osu.Game.Rulesets.Mania.Tests/TestCaseManiaPlayfield.cs diff --git a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Mania.Tests/TestCasePerformancePoints.cs similarity index 100% rename from osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs rename to osu.Game.Rulesets.Mania.Tests/TestCasePerformancePoints.cs 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 new file mode 100644 index 0000000000..c7ee19e14f --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e4f30e2679 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using System.Runtime.CompilerServices; +using osu.Framework.Testing; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")] +[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index a1f08e9299..b1d0baa03b 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -12,18 +12,7 @@ smash the keys. to the beat. osu.Game.Rulests.Mania - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/OsuBeatmapConversionTest.cs rename to osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseEditor.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCaseEditor.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseEditor.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Osu.Tests/TestCasePerformancePoints.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs rename to osu.Game.Rulesets.Osu.Tests/TestCasePerformancePoints.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs similarity index 100% rename from osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs rename to osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj new file mode 100644 index 0000000000..c84eff2da1 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..435bfd6512 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using System.Runtime.CompilerServices; +using osu.Framework.Testing; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")] +[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index a58d3b6795..1d29be1557 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -12,18 +12,7 @@ click the circles. to the beat. osu.Game.Rulesets.Osu - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs similarity index 100% rename from osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs rename to osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseInputDrum.cs similarity index 100% rename from osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs rename to osu.Game.Rulesets.Taiko.Tests/TestCaseInputDrum.cs diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Taiko.Tests/TestCasePerformancePoints.cs similarity index 100% rename from osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs rename to osu.Game.Rulesets.Taiko.Tests/TestCasePerformancePoints.cs diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs similarity index 100% rename from osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs rename to osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs 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 new file mode 100644 index 0000000000..918dd90814 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..c2ced73872 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using System.Runtime.CompilerServices; +using osu.Framework.Testing; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")] +[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index d7794aaafd..75e64012d0 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -12,18 +12,7 @@ bash the drum. to the beat. osu.Game.Rulesets.Taiko - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 1c88cc49d4..4a0b89817f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,37 +1,9 @@  - - - - - net461;netcoreapp2.0 - WinExe - AnyCPU - true - ppy Pty Ltd - click the circles. to the beat. - ppy Pty Ltd 2007-2017 - - NU1701 - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Game.props b/osu.Game.props index 07abeb5539..b8a340f1eb 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -8,4 +8,7 @@ osu.licenseheader + + + \ No newline at end of file diff --git a/osu.TestProject.props b/osu.TestProject.props index 50ee5848fc..c4b4a52a80 100644 --- a/osu.TestProject.props +++ b/osu.TestProject.props @@ -1,19 +1,31 @@ - - - $(DefineConstants);NET_FRAMEWORK - + osu.Game.Tests.VisualTestRunner + + + + + WinExe + AnyCPU + true + + NU1701 + netcoreapp2.0;net461 + + + + diff --git a/osu.sln b/osu.sln index 43a7fdf6c3..a440f456f5 100644 --- a/osu.sln +++ b/osu.sln @@ -23,6 +23,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Tests", "osu.Game. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Desktop", "osu.Desktop\osu.Desktop.csproj", "{419659FD-72EA-4678-9EB8-B22A746CED70}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Catch.Tests", "osu.Game.Rulesets.Catch.Tests\osu.Game.Rulesets.Catch.Tests.csproj", "{3AD63355-D6B1-4365-8D31-5652C989BEF1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Mania.Tests", "osu.Game.Rulesets.Mania.Tests\osu.Game.Rulesets.Mania.Tests.csproj", "{7E9E9C34-B204-406B-82E2-E01E900699CD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko.Tests", "osu.Game.Rulesets.Taiko.Tests\osu.Game.Rulesets.Taiko.Tests.csproj", "{B698561F-FB28-46B1-857E-3CA7B92F9D70}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu.Tests", "osu.Game.Rulesets.Osu.Tests\osu.Game.Rulesets.Osu.Tests.csproj", "{6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,6 +95,30 @@ Global {419659FD-72EA-4678-9EB8-B22A746CED70}.Release|Any CPU.Build.0 = Release|Any CPU {419659FD-72EA-4678-9EB8-B22A746CED70}.VisualTests|Any CPU.ActiveCfg = VisualTests|Any CPU {419659FD-72EA-4678-9EB8-B22A746CED70}.VisualTests|Any CPU.Build.0 = VisualTests|Any CPU + {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Release|Any CPU.Build.0 = Release|Any CPU + {3AD63355-D6B1-4365-8D31-5652C989BEF1}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {3AD63355-D6B1-4365-8D31-5652C989BEF1}.VisualTests|Any CPU.Build.0 = Debug|Any CPU + {7E9E9C34-B204-406B-82E2-E01E900699CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E9E9C34-B204-406B-82E2-E01E900699CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E9E9C34-B204-406B-82E2-E01E900699CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E9E9C34-B204-406B-82E2-E01E900699CD}.Release|Any CPU.Build.0 = Release|Any CPU + {7E9E9C34-B204-406B-82E2-E01E900699CD}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {7E9E9C34-B204-406B-82E2-E01E900699CD}.VisualTests|Any CPU.Build.0 = Debug|Any CPU + {B698561F-FB28-46B1-857E-3CA7B92F9D70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B698561F-FB28-46B1-857E-3CA7B92F9D70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B698561F-FB28-46B1-857E-3CA7B92F9D70}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B698561F-FB28-46B1-857E-3CA7B92F9D70}.Release|Any CPU.Build.0 = Release|Any CPU + {B698561F-FB28-46B1-857E-3CA7B92F9D70}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {B698561F-FB28-46B1-857E-3CA7B92F9D70}.VisualTests|Any CPU.Build.0 = Debug|Any CPU + {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Release|Any CPU.Build.0 = Release|Any CPU + {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU + {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.VisualTests|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 093bd7db3c0620b4b6cc2ed1be30c85e5a4ac357 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 14:41:37 +0900 Subject: [PATCH 319/537] Update vscode build configurations --- .vscode/launch.json | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 8c8255db71..624e584f10 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,10 +8,7 @@ }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net461/osu!.exe", - "args": [ - "--tests" - ], + "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -25,10 +22,7 @@ }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Release/net461/osu!.exe", - "args": [ - "--tests" - ], + "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, @@ -69,8 +63,7 @@ "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll", - "--tests" + "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.0/osu.Game.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, dotnet)", @@ -83,8 +76,7 @@ "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll", - "--tests" + "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.0/osu.Game.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, dotnet)", From 9667d54ecbe542e18634b49e66d3348b296995d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 14:48:08 +0900 Subject: [PATCH 320/537] Add missing licence headers --- osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs | 2 +- osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs | 2 +- osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs | 2 +- osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs index 0470b31824..00fd8247d8 100644 --- a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; using osu.Framework.Testing; diff --git a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs index e4f30e2679..c2c65433ec 100644 --- a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; using osu.Framework.Testing; diff --git a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs index 435bfd6512..7532646a32 100644 --- a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; using osu.Framework.Testing; diff --git a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs index c2ced73872..b7ed9f86b0 100644 --- a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; using osu.Framework.Testing; From 81a8385c8c36bc6e72c91939159db888287a861a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 15:17:09 +0900 Subject: [PATCH 321/537] Remove lingering reference to tests --- osu.Desktop/osu.Desktop.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 2262bbc515..2f74157c6e 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -36,7 +36,6 @@ - From 9aae568bbefee8bb807d6d7b8c123cfc4060c7a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 15:19:56 +0900 Subject: [PATCH 322/537] Tidy up csproj further --- osu.Desktop.Deploy/osu.Desktop.Deploy.csproj | 4 ---- osu.Desktop/osu.Desktop.csproj | 14 +------------- .../osu.Game.Rulesets.Catch.csproj | 5 ----- .../osu.Game.Rulesets.Mania.csproj | 5 ----- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 5 ----- .../osu.Game.Rulesets.Taiko.csproj | 5 ----- osu.Game.props | 7 +++++++ osu.Game/osu.Game.csproj | 6 ------ osu.TestProject.props | 3 --- 9 files changed, 8 insertions(+), 46 deletions(-) diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index eab54bd10b..a18db9477a 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -5,10 +5,6 @@ Exe AnyCPU true - ppy Pty Ltd - ppy Pty Ltd 2007-2017 - osu.Desktop.Deploy - osu.Desktop.Deploy diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 2f74157c6e..1f17ca9696 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -5,19 +5,11 @@ WinExe AnyCPU true - ppy Pty Ltd click the circles. to the beat. - ppy Pty Ltd 2007-2017 osu! osu!lazer osu!lazer lazer.ico - $(CONFIGURATIONS);VisualTests - 0.0.0.0 - 0.0.0.0 - - NU1701 $(DefineConstants);NET_FRAMEWORK @@ -25,22 +17,18 @@ osu.Desktop.Program - - --tests - + - - diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 1337617578..883cac67d1 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -5,12 +5,7 @@ Library AnyCPU true - ppy Pty Ltd - 1.0.0.0 - ppy Pty Ltd 2007-2017 - osu.Game.Rulesets.Catch catch the fruit. to the beat. - osu.Game.Rulesets.Catch diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index b1d0baa03b..a086da0565 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -5,12 +5,7 @@ Library AnyCPU true - ppy Pty Ltd - 1.0.0.0 - ppy Pty Ltd 2007-2017 - osu.Game.Rulests.Mania smash the keys. to the beat. - osu.Game.Rulests.Mania diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 1d29be1557..b0ca314551 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -5,12 +5,7 @@ Library AnyCPU true - ppy Pty Ltd - 1.0.0.0 - ppy Pty Ltd 2007-2017 - osu.Game.Rulesets.Osu click the circles. to the beat. - osu.Game.Rulesets.Osu diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 75e64012d0..002d6a7e8d 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -5,12 +5,7 @@ Library AnyCPU true - ppy Pty Ltd - 1.0.0.0 - ppy Pty Ltd 2007-2017 - osu.Game.Rulesets.Taiko bash the drum. to the beat. - osu.Game.Rulesets.Taiko diff --git a/osu.Game.props b/osu.Game.props index b8a340f1eb..87edafb97f 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -11,4 +11,11 @@ + + ppy Pty Ltd + ppy Pty Ltd 2007-2018 + + NU1701 + \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b42d76e331..367c3490d6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -5,12 +5,6 @@ Library AnyCPU true - ppy Pty Ltd - 1.0.0.0 - ppy Pty Ltd 2007-2018 - osu.Game - click the circles. to the beat. - osu.Game 0 diff --git a/osu.TestProject.props b/osu.TestProject.props index c4b4a52a80..9ffb2094a4 100644 --- a/osu.TestProject.props +++ b/osu.TestProject.props @@ -10,9 +10,6 @@ WinExe AnyCPU true - - NU1701 netcoreapp2.0;net461 From 59df02da0003cf5c99604bb236e8cf35b0706820 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 15:59:41 +0900 Subject: [PATCH 323/537] Update test projects to actually load in visual studio --- .../osu.Game.Rulesets.Catch.Tests.csproj | 4 ++++ .../osu.Game.Rulesets.Mania.Tests.csproj | 4 ++++ .../osu.Game.Rulesets.Osu.Tests.csproj | 4 ++++ .../osu.Game.Rulesets.Taiko.Tests.csproj | 4 ++++ osu.Game.Tests/osu.Game.Tests.csproj | 4 ++++ osu.TestProject.props | 6 ------ osu.sln | 4 ++-- 7 files changed, 22 insertions(+), 8 deletions(-) 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 561613a2e8..51c6d18f1f 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -1,5 +1,9 @@  + + WinExe + netcoreapp2.0;net461 + 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 c7ee19e14f..187d5e47b9 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -1,5 +1,9 @@  + + WinExe + netcoreapp2.0;net461 + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index c84eff2da1..b2b524a71c 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -1,5 +1,9 @@  + + WinExe + netcoreapp2.0;net461 + 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 918dd90814..df73fa61cb 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -1,5 +1,9 @@  + + WinExe + netcoreapp2.0;net461 + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 4a0b89817f..2646e953cf 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,5 +1,9 @@  + + WinExe + netcoreapp2.0;net461 + diff --git a/osu.TestProject.props b/osu.TestProject.props index 9ffb2094a4..cb841f1940 100644 --- a/osu.TestProject.props +++ b/osu.TestProject.props @@ -6,12 +6,6 @@ - - WinExe - AnyCPU - true - netcoreapp2.0;net461 - diff --git a/osu.sln b/osu.sln index a440f456f5..835a2f8169 100644 --- a/osu.sln +++ b/osu.sln @@ -93,8 +93,8 @@ Global {419659FD-72EA-4678-9EB8-B22A746CED70}.Debug|Any CPU.Build.0 = Debug|Any CPU {419659FD-72EA-4678-9EB8-B22A746CED70}.Release|Any CPU.ActiveCfg = Release|Any CPU {419659FD-72EA-4678-9EB8-B22A746CED70}.Release|Any CPU.Build.0 = Release|Any CPU - {419659FD-72EA-4678-9EB8-B22A746CED70}.VisualTests|Any CPU.ActiveCfg = VisualTests|Any CPU - {419659FD-72EA-4678-9EB8-B22A746CED70}.VisualTests|Any CPU.Build.0 = VisualTests|Any CPU + {419659FD-72EA-4678-9EB8-B22A746CED70}.VisualTests|Any CPU.ActiveCfg = Release|Any CPU + {419659FD-72EA-4678-9EB8-B22A746CED70}.VisualTests|Any CPU.Build.0 = Release|Any CPU {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Release|Any CPU.ActiveCfg = Release|Any CPU From e47276c6b00b52437499bc3e46bafd74b650d261 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 16:17:04 +0900 Subject: [PATCH 324/537] Remove VisualTests solution configuration --- osu.sln | 36 ++++-------------------------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/osu.sln b/osu.sln index 835a2f8169..f017bf9eab 100644 --- a/osu.sln +++ b/osu.sln @@ -23,102 +23,74 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Tests", "osu.Game. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Desktop", "osu.Desktop\osu.Desktop.csproj", "{419659FD-72EA-4678-9EB8-B22A746CED70}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Catch.Tests", "osu.Game.Rulesets.Catch.Tests\osu.Game.Rulesets.Catch.Tests.csproj", "{3AD63355-D6B1-4365-8D31-5652C989BEF1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Catch.Tests", "osu.Game.Rulesets.Catch.Tests\osu.Game.Rulesets.Catch.Tests.csproj", "{3AD63355-D6B1-4365-8D31-5652C989BEF1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Mania.Tests", "osu.Game.Rulesets.Mania.Tests\osu.Game.Rulesets.Mania.Tests.csproj", "{7E9E9C34-B204-406B-82E2-E01E900699CD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Mania.Tests", "osu.Game.Rulesets.Mania.Tests\osu.Game.Rulesets.Mania.Tests.csproj", "{7E9E9C34-B204-406B-82E2-E01E900699CD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko.Tests", "osu.Game.Rulesets.Taiko.Tests\osu.Game.Rulesets.Taiko.Tests.csproj", "{B698561F-FB28-46B1-857E-3CA7B92F9D70}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko.Tests", "osu.Game.Rulesets.Taiko.Tests\osu.Game.Rulesets.Taiko.Tests.csproj", "{B698561F-FB28-46B1-857E-3CA7B92F9D70}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu.Tests", "osu.Game.Rulesets.Osu.Tests\osu.Game.Rulesets.Osu.Tests.csproj", "{6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu.Tests", "osu.Game.Rulesets.Osu.Tests\osu.Game.Rulesets.Osu.Tests.csproj", "{6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU - VisualTests|Any CPU = VisualTests|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.Build.0 = Release|Any CPU - {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.Build.0 = Release|Any CPU - {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.Build.0 = Debug|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.Build.0 = Release|Any CPU - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.Build.0 = Debug|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.ActiveCfg = Release|Any CPU {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.Build.0 = Release|Any CPU - {C92A607B-1FDD-4954-9F92-03FF547D9080}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {C92A607B-1FDD-4954-9F92-03FF547D9080}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Release|Any CPU.Build.0 = Release|Any CPU - {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {F167E17A-7DE6-4AF5-B920-A5112296C695}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F167E17A-7DE6-4AF5-B920-A5112296C695}.Debug|Any CPU.Build.0 = Debug|Any CPU {F167E17A-7DE6-4AF5-B920-A5112296C695}.Release|Any CPU.ActiveCfg = Release|Any CPU {F167E17A-7DE6-4AF5-B920-A5112296C695}.Release|Any CPU.Build.0 = Release|Any CPU - {F167E17A-7DE6-4AF5-B920-A5112296C695}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {F167E17A-7DE6-4AF5-B920-A5112296C695}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.Build.0 = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU - {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {48F4582B-7687-4621-9CBE-5C24197CB536}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.Build.0 = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.ActiveCfg = Release|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.Build.0 = Release|Any CPU - {54377672-20B1-40AF-8087-5CF73BF3953A}.VisualTests|Any CPU.ActiveCfg = Release|Any CPU - {54377672-20B1-40AF-8087-5CF73BF3953A}.VisualTests|Any CPU.Build.0 = Release|Any CPU {419659FD-72EA-4678-9EB8-B22A746CED70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {419659FD-72EA-4678-9EB8-B22A746CED70}.Debug|Any CPU.Build.0 = Debug|Any CPU {419659FD-72EA-4678-9EB8-B22A746CED70}.Release|Any CPU.ActiveCfg = Release|Any CPU {419659FD-72EA-4678-9EB8-B22A746CED70}.Release|Any CPU.Build.0 = Release|Any CPU - {419659FD-72EA-4678-9EB8-B22A746CED70}.VisualTests|Any CPU.ActiveCfg = Release|Any CPU - {419659FD-72EA-4678-9EB8-B22A746CED70}.VisualTests|Any CPU.Build.0 = Release|Any CPU {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {3AD63355-D6B1-4365-8D31-5652C989BEF1}.Release|Any CPU.Build.0 = Release|Any CPU - {3AD63355-D6B1-4365-8D31-5652C989BEF1}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {3AD63355-D6B1-4365-8D31-5652C989BEF1}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {7E9E9C34-B204-406B-82E2-E01E900699CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7E9E9C34-B204-406B-82E2-E01E900699CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {7E9E9C34-B204-406B-82E2-E01E900699CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {7E9E9C34-B204-406B-82E2-E01E900699CD}.Release|Any CPU.Build.0 = Release|Any CPU - {7E9E9C34-B204-406B-82E2-E01E900699CD}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {7E9E9C34-B204-406B-82E2-E01E900699CD}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {B698561F-FB28-46B1-857E-3CA7B92F9D70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B698561F-FB28-46B1-857E-3CA7B92F9D70}.Debug|Any CPU.Build.0 = Debug|Any CPU {B698561F-FB28-46B1-857E-3CA7B92F9D70}.Release|Any CPU.ActiveCfg = Release|Any CPU {B698561F-FB28-46B1-857E-3CA7B92F9D70}.Release|Any CPU.Build.0 = Release|Any CPU - {B698561F-FB28-46B1-857E-3CA7B92F9D70}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {B698561F-FB28-46B1-857E-3CA7B92F9D70}.VisualTests|Any CPU.Build.0 = Debug|Any CPU {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.Release|Any CPU.Build.0 = Release|Any CPU - {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU - {6A2D5D58-0261-4A75-BE84-2BE8B076B7C2}.VisualTests|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 01116a73a3e898ed3eb70e2b6e9c113e911bd965 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Mar 2018 18:09:18 +0900 Subject: [PATCH 325/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index c69330f652..2ac5cb0464 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit c69330f652d34350a7bf3786016a3ce5f968c25f +Subproject commit 2ac5cb0464133b570adb1f3408e564691f08d12e From d83e4dfd24a34843f8ba81f5987bc166904edc84 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Mar 2018 12:26:32 +0900 Subject: [PATCH 326/537] Add .vscode configurations for ruleset test projects --- .vscode/settings.json | 3 - .../.vscode/launch.json | 59 +++++++++++++ .../.vscode/tasks.json | 87 +++++++++++++++++++ .../.vscode/launch.json | 59 +++++++++++++ .../.vscode/tasks.json | 87 +++++++++++++++++++ .../.vscode/launch.json | 59 +++++++++++++ .../.vscode/tasks.json | 87 +++++++++++++++++++ .../.vscode/launch.json | 59 +++++++++++++ .../.vscode/tasks.json | 87 +++++++++++++++++++ 9 files changed, 584 insertions(+), 3 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 osu.Game.Rulesets.Catch.Tests/.vscode/launch.json create mode 100644 osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json create mode 100644 osu.Game.Rulesets.Mania.Tests/.vscode/launch.json create mode 100644 osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json create mode 100644 osu.Game.Rulesets.Osu.Tests/.vscode/launch.json create mode 100644 osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json create mode 100644 osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json create mode 100644 osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 20af2f68a6..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -// Place your settings in this file to overwrite default and user settings. -{ -} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json new file mode 100644 index 0000000000..5098b78a42 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json @@ -0,0 +1,59 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "VisualTests (Debug, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Catch.Tests.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, msbuild)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Catch.Tests.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, msbuild)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Debug, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Catch.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, dotnet)", + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Catch.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, dotnet)", + "env": {}, + "console": "internalConsole" + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json new file mode 100644 index 0000000000..d21bb8a69a --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json @@ -0,0 +1,87 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build (Debug, msbuild)", + "type": "shell", + "command": "msbuild", + "args": [ + "osu.Game.Rulesets.Catch.Tests.csproj", + "/p:TargetFramework=net461", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, msbuild)", + "type": "shell", + "command": "msbuild", + "args": [ + "osu.Game.Rulesets.Catch.Tests.csproj", + "/p:Configuration=Release", + "/p:TargetFramework=net461", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Debug, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Rulesets.Catch.Tests.csproj", + "/p:TargetFramework=netcoreapp2.0", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Rulesets.Catch.Tests.csproj", + "/p:TargetFramework=netcoreapp2.0", + "/p:Configuration=Release", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Restore (net461)", + "type": "shell", + "command": "nuget", + "args": [ + "restore" + ], + "problemMatcher": [] + }, + { + "label": "Restore (netcoreapp2.0)", + "type": "shell", + "command": "dotnet", + "args": [ + "restore" + ], + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json new file mode 100644 index 0000000000..c71178059b --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json @@ -0,0 +1,59 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "VisualTests (Debug, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Mania.Tests.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, msbuild)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Mania.Tests.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, msbuild)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Debug, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Mania.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, dotnet)", + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Mania.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, dotnet)", + "env": {}, + "console": "internalConsole" + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json new file mode 100644 index 0000000000..781e89598f --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json @@ -0,0 +1,87 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build (Debug, msbuild)", + "type": "shell", + "command": "msbuild", + "args": [ + "osu.Game.Rulesets.Mania.Tests.csproj", + "/p:TargetFramework=net461", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, msbuild)", + "type": "shell", + "command": "msbuild", + "args": [ + "osu.Game.Rulesets.Mania.Tests.csproj", + "/p:Configuration=Release", + "/p:TargetFramework=net461", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Debug, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Rulesets.Mania.Tests.csproj", + "/p:TargetFramework=netcoreapp2.0", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Rulesets.Mania.Tests.csproj", + "/p:TargetFramework=netcoreapp2.0", + "/p:Configuration=Release", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Restore (net461)", + "type": "shell", + "command": "nuget", + "args": [ + "restore" + ], + "problemMatcher": [] + }, + { + "label": "Restore (netcoreapp2.0)", + "type": "shell", + "command": "dotnet", + "args": [ + "restore" + ], + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json new file mode 100644 index 0000000000..24431eb8de --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json @@ -0,0 +1,59 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "VisualTests (Debug, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Osu.Tests.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, msbuild)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Osu.Tests.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, msbuild)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Debug, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Osu.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, dotnet)", + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Osu.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, dotnet)", + "env": {}, + "console": "internalConsole" + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json new file mode 100644 index 0000000000..734e15353b --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json @@ -0,0 +1,87 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build (Debug, msbuild)", + "type": "shell", + "command": "msbuild", + "args": [ + "osu.Game.Rulesets.Osu.Tests.csproj", + "/p:TargetFramework=net461", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, msbuild)", + "type": "shell", + "command": "msbuild", + "args": [ + "osu.Game.Rulesets.Osu.Tests.csproj", + "/p:Configuration=Release", + "/p:TargetFramework=net461", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Debug, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Rulesets.Osu.Tests.csproj", + "/p:TargetFramework=netcoreapp2.0", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Rulesets.Osu.Tests.csproj", + "/p:TargetFramework=netcoreapp2.0", + "/p:Configuration=Release", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Restore (net461)", + "type": "shell", + "command": "nuget", + "args": [ + "restore" + ], + "problemMatcher": [] + }, + { + "label": "Restore (netcoreapp2.0)", + "type": "shell", + "command": "dotnet", + "args": [ + "restore" + ], + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json new file mode 100644 index 0000000000..caa90c32ce --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json @@ -0,0 +1,59 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "VisualTests (Debug, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Taiko.Tests.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, msbuild)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, net461)", + "windows": { + "type": "clr" + }, + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Taiko.Tests.exe", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, msbuild)", + "runtimeExecutable": null, + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Debug, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Taiko.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Debug, dotnet)", + "env": {}, + "console": "internalConsole" + }, + { + "name": "VisualTests (Release, netcoreapp2.0)", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Taiko.Tests.dll" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build (Release, dotnet)", + "env": {}, + "console": "internalConsole" + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json new file mode 100644 index 0000000000..13044e1ccb --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json @@ -0,0 +1,87 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build (Debug, msbuild)", + "type": "shell", + "command": "msbuild", + "args": [ + "osu.Game.Rulesets.Taiko.Tests.csproj", + "/p:TargetFramework=net461", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, msbuild)", + "type": "shell", + "command": "msbuild", + "args": [ + "osu.Game.Rulesets.Taiko.Tests.csproj", + "/p:Configuration=Release", + "/p:TargetFramework=net461", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Debug, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Rulesets.Taiko.Tests.csproj", + "/p:TargetFramework=netcoreapp2.0", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Build (Release, dotnet)", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Rulesets.Taiko.Tests.csproj", + "/p:TargetFramework=netcoreapp2.0", + "/p:Configuration=Release", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, + { + "label": "Restore (net461)", + "type": "shell", + "command": "nuget", + "args": [ + "restore" + ], + "problemMatcher": [] + }, + { + "label": "Restore (netcoreapp2.0)", + "type": "shell", + "command": "dotnet", + "args": [ + "restore" + ], + "problemMatcher": [] + } + ] +} \ No newline at end of file From 687c889345a286d2e03701b8f4069e2f5045d75c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Mar 2018 12:47:17 +0900 Subject: [PATCH 327/537] Add back osu.Desktop versions (used in deploy) --- osu.Desktop/osu.Desktop.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 1f17ca9696..2ad7b67842 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -10,6 +10,8 @@ osu!lazer osu!lazer lazer.ico + 0.0.0.0 + 0.0.0.0 $(DefineConstants);NET_FRAMEWORK From cc35b1ab64bbd21356662c5a2659808a5c4489f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Mar 2018 12:47:24 +0900 Subject: [PATCH 328/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 2ac5cb0464..f60cd46471 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 2ac5cb0464133b570adb1f3408e564691f08d12e +Subproject commit f60cd4647165d1dadb91aa045f41cef42c11bad9 From 187a025d36a70aa649b84b6573770d5412f12dfd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Mar 2018 16:07:20 +0900 Subject: [PATCH 329/537] Make hitobjects sorted by their start times --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 1 - osu.Game.Rulesets.Mania/UI/Column.cs | 1 - osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 2 -- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 -- osu.Game/Rulesets/UI/HitObjectContainer.cs | 11 +++++++++++ 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 39b7ffb387..8bb206543b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -57,7 +57,6 @@ namespace osu.Game.Rulesets.Catch.UI public override void Add(DrawableHitObject h) { - h.Depth = (float)h.HitObject.StartTime; h.OnJudgement += onJudgement; base.Add(h); diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 21f00c003b..109ffd0d25 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -205,7 +205,6 @@ namespace osu.Game.Rulesets.Mania.UI /// The DrawableHitObject to add. public override void Add(DrawableHitObject hitObject) { - hitObject.Depth = (float)hitObject.HitObject.StartTime; hitObject.AccentColour = AccentColour; hitObject.OnJudgement += OnJudgement; diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 98a8096678..0c5d757474 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -55,8 +55,6 @@ namespace osu.Game.Rulesets.Osu.UI public override void Add(DrawableHitObject h) { - h.Depth = (float)h.HitObject.StartTime; - h.OnJudgement += onJudgement; var c = h as IDrawableHitObjectWithProxiedApproach; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 75aaceaecb..effb9eb54f 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -208,8 +208,6 @@ namespace osu.Game.Rulesets.Taiko.UI public override void Add(DrawableHitObject h) { - h.Depth = (float)h.HitObject.StartTime; - h.OnJudgement += OnJudgement; base.Add(h); diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index c26a6cdff0..ecb10dfba2 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; @@ -15,5 +16,15 @@ namespace osu.Game.Rulesets.UI public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject); public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject); + + protected override int Compare(Drawable x, Drawable y) + { + if (!(x is DrawableHitObject xObj) || !(y is DrawableHitObject yObj)) + return base.Compare(x, y); + + // Put earlier hitobjects towards the end of the list, so they handle input first + int i = yObj.HitObject.StartTime.CompareTo(xObj.HitObject.StartTime); + return i == 0 ? CompareReverseChildID(x, y) : i; + } } } From bb7618eb0c0b4dc38c87786a7e88381718147620 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 29 Mar 2018 16:13:05 +0900 Subject: [PATCH 330/537] FIx mania playfield playing the wrong/duplicated sounds Fixes #2266. --- osu.Game.Rulesets.Mania/UI/Column.cs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 109ffd0d25..15c9a83b78 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -262,21 +262,13 @@ namespace osu.Game.Rulesets.Mania.UI public bool OnPressed(ManiaAction action) { - // Play the sounds of the next hitobject - if (HitObjects.AliveObjects.Any()) - { - // If there are alive hitobjects, we can abuse the fact that AliveObjects are sorted by time (see: Add()) - HitObjects.AliveObjects.First().PlaySamples(); - } - else - { - // If not, we do a slow search - we might want to do a BinarySearch here if this becomes problematic - // We fallback to LastOrDefault() if we're beyond the last note in the map - var hitObject = HitObjects.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.LastOrDefault(); - hitObject?.PlaySamples(); - } + if (action != Action) + return false; - return false; + var hitObject = HitObjects.Objects.LastOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.FirstOrDefault(); + hitObject?.PlaySamples(); + + return true; } public bool OnReleased(ManiaAction action) => false; From 5c4b0c2b8d22c2a567d0afbfe6558ec038496c37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Mar 2018 16:55:07 +0900 Subject: [PATCH 331/537] Add rider run/debug configurations --- .../VisualTests__net461_.xml | 24 +++++++++++++++++++ .../VisualTests__netcoreapp2_0_.xml | 24 +++++++++++++++++++ .../.idea/runConfigurations/osu___net461_.xml | 24 +++++++++++++++++++ .../osu___netcoreapp2_0_.xml | 24 +++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 .idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml create mode 100644 .idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml create mode 100644 .idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml create mode 100644 .idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml new file mode 100644 index 0000000000..5122f08aaa --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml new file mode 100644 index 0000000000..a5c78f0a7a --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml b/.idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml new file mode 100644 index 0000000000..bacfd6c576 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml b/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml new file mode 100644 index 0000000000..b02c7977b6 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file From 49c8bc44ad2934fa6e522a09f581994336d963af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Mar 2018 17:08:06 +0900 Subject: [PATCH 332/537] Remove silly stuff --- .../.idea/runConfigurations/VisualTests__net461_.xml | 5 ----- .../.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml | 5 ----- .idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml | 5 ----- .../.idea/runConfigurations/osu___netcoreapp2_0_.xml | 5 ----- 4 files changed, 20 deletions(-) diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml index 5122f08aaa..0615aa755f 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml @@ -4,10 +4,6 @@ /// The overlays. - public virtual SelectionBox CreateSelectionOverlay(IReadOnlyList overlays) => new SelectionBox(overlays); + public virtual SelectionBox CreateSelectionBox(IReadOnlyList overlays) => new SelectionBox(overlays); /// /// Creates a which provides a layer above or below the . diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 46b09e2c23..1412d98f31 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -2,31 +2,43 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; namespace osu.Game.Screens.Edit.Screens.Compose.Layers { public class HitObjectMaskLayer : CompositeDrawable { + private readonly Playfield playfield; private readonly HitObjectComposer composer; private readonly Container overlayContainer; - public HitObjectMaskLayer(HitObjectComposer composer) + public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer) { + this.playfield = playfield; this.composer = composer; + RelativeSizeAxes = Axes.Both; InternalChild = overlayContainer = new Container { RelativeSizeAxes = Axes.Both }; } + [BackgroundDependencyLoader] + private void load() + { + foreach (var obj in playfield.HitObjects.Objects) + addOverlay(obj); + } + /// /// Adds an overlay for a which adds movement support. /// /// The to create an overlay for. - public void AddOverlay(DrawableHitObject hitObject) + private void addOverlay(DrawableHitObject hitObject) { var overlay = composer.CreateMaskFor(hitObject); if (overlay == null) @@ -39,7 +51,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// Removes the overlay for a . /// /// The to remove the overlay for. - public void RemoveOverlay(DrawableHitObject hitObject) + private void removeOverlay(DrawableHitObject hitObject) { var existing = overlayContainer.FirstOrDefault(h => h.HitObject == hitObject); if (existing == null) @@ -51,13 +63,13 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private SelectionBox currentSelectionBox; - public void AddSelectionOverlay() + private void addSelectionBox() { if (overlayContainer.Count > 0) - AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer)); + AddInternal(currentSelectionBox = composer.CreateSelectionBox(overlayContainer)); } - public void RemoveSelectionOverlay() + private void removeSelectionBox() { currentSelectionBox?.Hide(); currentSelectionBox?.Expire(); From 173f79f7b618a9eb21a27c88587a67d40c51369f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Mar 2018 17:17:33 +0900 Subject: [PATCH 334/537] Force build before run --- .../.idea/runConfigurations/VisualTests__net461_.xml | 5 ++--- .../.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml | 5 ++--- .idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml | 5 ++--- .../.idea/runConfigurations/osu___netcoreapp2_0_.xml | 5 ++--- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml index 0615aa755f..cf4bccfe60 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml @@ -4,6 +4,7 @@ public class CarouselGroupEagerSelect : CarouselGroup { - public CarouselGroupEagerSelect() + private readonly bool isRootSelector; + + public CarouselGroupEagerSelect(bool isRootSelector = false) { + this.isRootSelector = isRootSelector; State.ValueChanged += v => { if (v == CarouselItemState.Selected) @@ -83,9 +87,20 @@ namespace osu.Game.Screens.Select.Carousel // we only perform eager selection if none of our children are in a selected state already. if (Children.Any(i => i.State == CarouselItemState.Selected)) return; - CarouselItem nextToSelect = - Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered) ?? - Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered); + CarouselItem nextToSelect = null; + if (isRootSelector && lastSelected == null) + { + var selectables = Children.Where(i => !i.Filtered).ToList(); + if (selectables.Any()) + nextToSelect = selectables[RNG.Next(selectables.Count)]; + } + else + { + nextToSelect = + Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered) ?? + Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered); + } + if (nextToSelect != null) nextToSelect.State.Value = CarouselItemState.Selected; From 0887dafa2c38ef83b08c301ec33db86f8c554abd Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 29 Mar 2018 20:36:52 -0700 Subject: [PATCH 352/537] Revert changes on hard rock mod --- osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs | 1 + osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 1 + osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 1 + osu.Game/Rulesets/Mods/ModHardRock.cs | 1 - 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index 9479c9d9b0..ed33bf7124 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Catch.Mods public class CatchModHardRock : ModHardRock { public override double ScoreMultiplier => 1.12; + public override bool Ranked => true; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 74c3585d3d..29bf3e248d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModHardRock : ModHardRock, IApplicableToHitObject { public override double ScoreMultiplier => 1.06; + public override bool Ranked => true; public void ApplyToHitObject(OsuHitObject hitObject) { diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index 435a0c1613..ba304c41d8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModHardRock : ModHardRock { public override double ScoreMultiplier => 1.06; + public override bool Ranked => true; } } diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index c998bc123f..c4c0f38faf 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Mods public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Everything just got a bit harder..."; - public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; public void ApplyToDifficulty(BeatmapDifficulty difficulty) From 4ad776bfde4c88da610e388c4394a836af444772 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Mar 2018 14:14:04 +0900 Subject: [PATCH 353/537] Make slider circle masks not handle mouse input at all --- .../Edit/Layers/Selection/Overlays/SliderCircleMask.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs index fa9896ff7b..4e22b4f693 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects; @@ -58,6 +57,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays } // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input. - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => false; + public override bool HandleMouseInput => false; } } From 31ade986a707fd41880ccea73a3ab5bf932cb0b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Mar 2018 14:57:13 +0900 Subject: [PATCH 354/537] Scren async changes in line with framework changes Makes editor not stutter on load, amongst other screens. --- osu-framework | 2 +- osu.Game/Screens/BackgroundScreen.cs | 6 ++---- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 13 +++++-------- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/osu-framework b/osu-framework index 85b3494117..d4cb1117fb 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 85b3494117ccef1b396b70957e1cffaba06e2b54 +Subproject commit d4cb1117fb23453c20e7a8116f1c1f99d9a13611 diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index c5e5883b99..b232cc25cf 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -26,14 +26,14 @@ namespace osu.Game.Screens return false; } - public override bool Push(Screen screen) + public override void Push(Screen screen) { // When trying to push a non-loaded screen, load it asynchronously and re-invoke Push // once it's done. if (screen.LoadState == LoadState.NotLoaded) { LoadComponentAsync(screen, d => Push((BackgroundScreen)d)); - return true; + return; } // Make sure the in-progress loading is complete before pushing the screen. @@ -41,8 +41,6 @@ namespace osu.Game.Screens Thread.Sleep(1); base.Push(screen); - - return true; } protected override void Update() diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 8b651000fd..0fcdd79916 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -173,7 +173,7 @@ namespace osu.Game.Screens.Edit } currentScreen.Beatmap.BindTo(Beatmap); - screenContainer.Add(currentScreen); + LoadComponentAsync(currentScreen, screenContainer.Add); } protected override void OnResuming(Screen last) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6d55cdb9ca..9ec8f0a52e 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -159,14 +159,11 @@ namespace osu.Game.Screens.Play loadTask = null; - if (!Push(player)) - Exit(); - else - { - //By default, we want to load the player and never be returned to. - //Note that this may change if the player we load requested a re-run. - ValidForResume = false; - } + //By default, we want to load the player and never be returned to. + //Note that this may change if the player we load requested a re-run. + ValidForResume = false; + + Push(player); }); }, 500); } From 082e5e4949f91267389a7a639667fa823db87a11 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Mar 2018 15:06:52 +0900 Subject: [PATCH 355/537] Reduce iterations of DragBox --- .../Screens/Edit/Screens/Compose/Layers/DragBox.cs | 12 ++++++------ .../Screens/Compose/Layers/HitObjectMaskLayer.cs | 12 +++++++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs index 0817541a21..1de78d19ce 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs @@ -24,17 +24,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// public event Action DragEnd; - private readonly IEnumerable hitObjectMasks; + private readonly IEnumerable selectableMasks; private Drawable box; /// /// Creates a new . /// - /// The selectable s. - public DragBox(IEnumerable hitObjectMasks) + /// The selectable s. + public DragBox(IEnumerable selectableMasks) { - this.hitObjectMasks = hitObjectMasks; + this.selectableMasks = selectableMasks; RelativeSizeAxes = Axes.Both; AlwaysPresent = true; @@ -79,9 +79,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers box.Position = topLeft; box.Size = bottomRight - topLeft; - foreach (var mask in hitObjectMasks) + foreach (var mask in selectableMasks) { - if (mask.IsAlive && mask.IsPresent && dragRectangle.Contains(mask.SelectionPoint)) + if (mask.IsPresent && dragRectangle.Contains(mask.SelectionPoint)) mask.Select(); else mask.Deselect(); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index ca161a6f37..2c8a308d5b 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private readonly Playfield playfield; private readonly HitObjectComposer composer; - private Container maskContainer; + private MaskContainer maskContainer; private SelectionBox selectionBox; private readonly HashSet selectedMasks = new HashSet(); @@ -34,10 +34,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers [BackgroundDependencyLoader] private void load() { - maskContainer = new Container(); + maskContainer = new MaskContainer(); + selectionBox = composer.CreateSelectionBox(); - var dragBox = new DragBox(maskContainer); + var dragBox = new DragBox(maskContainer.AliveChildren); dragBox.DragEnd += () => selectionBox.UpdateVisibility(); InternalChildren = new Drawable[] @@ -104,5 +105,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// Deselects all selected s. /// public void DeselectAll() => selectedMasks.ToList().ForEach(m => m.Deselect()); + + private class MaskContainer : Container + { + public new IEnumerable AliveChildren => AliveInternalChildren.Cast(); + } } } From 1dca1663c32988040fa47667e8dc52b7f0994d7b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Mar 2018 15:50:55 +0900 Subject: [PATCH 356/537] Handle all selection events within SelectionBox (incl. single-mask) --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 +- osu.Game/Rulesets/Edit/HitObjectMask.cs | 31 ----- .../Edit/Screens/Compose/Layers/DragBox.cs | 11 +- .../Compose/Layers/HitObjectMaskLayer.cs | 35 +----- .../Screens/Compose/Layers/MaskContainer.cs | 50 ++++++++ .../Screens/Compose/Layers/SelectionBox.cs | 109 ++++++++++-------- 6 files changed, 120 insertions(+), 120 deletions(-) create mode 100644 osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1a587bf8f5..7d5702ccbf 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -253,8 +253,8 @@ namespace osu.Game.Rulesets.Edit /// Creates a which outlines s /// and handles hitobject pattern adjustments. /// - /// The overlays. - public virtual SelectionBox CreateSelectionBox() => new SelectionBox(); + /// The container. + public virtual SelectionBox CreateSelectionBox(MaskContainer maskContainer) => new SelectionBox(maskContainer); /// /// Creates a which provides a layer above or below the . diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index c55e34f548..981e109747 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -4,8 +4,6 @@ using System; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; -using osu.Framework.Input; -using osu.Game.Rulesets.Edit.Types; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; @@ -26,12 +24,6 @@ namespace osu.Game.Rulesets.Edit /// public event Action Deselected; - /// - /// Invoked when this is requesting to be the single selection. - /// This has not been selected at this point, but will be selected immediately afterwards. - /// - public event Action SingleSelectionRequested; - /// /// The which this applies to. /// @@ -77,29 +69,6 @@ namespace osu.Game.Rulesets.Edit return true; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - SingleSelectionRequested?.Invoke(this); - Select(); - return true; - } - - protected override bool OnDragStart(InputState state) => true; - - protected override bool OnDrag(InputState state) - { - // Todo: Various forms of snapping - switch (HitObject.HitObject) - { - case IHasEditablePosition editablePosition: - editablePosition.OffsetPosition(state.Mouse.Delta); - break; - } - return true; - } - - protected override bool OnDragEnd(InputState state) => true; - protected override void PopIn() => Alpha = 1; protected override void PopOut() => Alpha = 0; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs index 1de78d19ce..ea170a0326 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -24,17 +23,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// public event Action DragEnd; - private readonly IEnumerable selectableMasks; + private readonly MaskContainer maskContainer; private Drawable box; /// /// Creates a new . /// - /// The selectable s. - public DragBox(IEnumerable selectableMasks) + /// The selectable s. + public DragBox(MaskContainer maskContainer) { - this.selectableMasks = selectableMasks; + this.maskContainer = maskContainer; RelativeSizeAxes = Axes.Both; AlwaysPresent = true; @@ -79,7 +78,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers box.Position = topLeft; box.Size = bottomRight - topLeft; - foreach (var mask in selectableMasks) + foreach (var mask in maskContainer.AliveMasks) { if (mask.IsPresent && dragRectangle.Contains(mask.SelectionPoint)) mask.Select(); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 2c8a308d5b..f972f9ac81 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -21,8 +20,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private MaskContainer maskContainer; private SelectionBox selectionBox; - private readonly HashSet selectedMasks = new HashSet(); - public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer) { this.playfield = playfield; @@ -36,9 +33,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { maskContainer = new MaskContainer(); - selectionBox = composer.CreateSelectionBox(); + selectionBox = composer.CreateSelectionBox(maskContainer); - var dragBox = new DragBox(maskContainer.AliveChildren); + var dragBox = new DragBox(maskContainer); dragBox.DragEnd += () => selectionBox.UpdateVisibility(); InternalChildren = new Drawable[] @@ -63,12 +60,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers if (mask == null) return; - mask.Selected += onSelected; - mask.Deselected += onDeselected; - mask.SingleSelectionRequested += onSingleSelectionRequested; - maskContainer.Add(mask); - selectionBox.AddMask(mask); } /// @@ -81,34 +73,13 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers if (mask == null) return; - mask.Selected -= onSelected; - mask.Deselected -= onDeselected; - mask.SingleSelectionRequested -= onSingleSelectionRequested; - maskContainer.Remove(mask); - selectionBox.RemoveMask(mask); } - private void onSelected(HitObjectMask mask) => selectedMasks.Add(mask); - - private void onDeselected(HitObjectMask mask) => selectedMasks.Remove(mask); - - private void onSingleSelectionRequested(HitObjectMask mask) => DeselectAll(); - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - DeselectAll(); + selectionBox.DeselectAll(); return true; } - - /// - /// Deselects all selected s. - /// - public void DeselectAll() => selectedMasks.ToList().ForEach(m => m.Deselect()); - - private class MaskContainer : Container - { - public new IEnumerable AliveChildren => AliveInternalChildren.Cast(); - } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs new file mode 100644 index 0000000000..4b3ea077bc --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Edit; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + public class MaskContainer : Container + { + /// + /// Invoked when any is selected. + /// + public event Action MaskSelected; + + /// + /// Invoked when any is deselected. + /// + public event Action MaskDeselected; + + /// + /// All the s with == true. + /// + public IEnumerable AliveMasks => AliveInternalChildren.Cast(); + + public override void Add(HitObjectMask drawable) + { + base.Add(drawable); + + drawable.Selected += onMaskSelected; + drawable.Deselected += onMaskDeselected; + } + + public override bool Remove(HitObjectMask drawable) + { + var result = base.Remove(drawable); + + if (result) + { + drawable.Selected -= onMaskSelected; + drawable.Deselected += onMaskDeselected; + } + + return result; + } + + private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask); + private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask); + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs index ad8c846bbf..833c94d3f4 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -23,15 +22,23 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { public const float BORDER_RADIUS = 2; - private readonly HashSet selectedMasks = new HashSet(); + private readonly MaskContainer maskContainer; + + private readonly List selectedMasks = new List(); + private IEnumerable selectableMasks => maskContainer.AliveMasks; private Drawable box; - public SelectionBox() + public SelectionBox(MaskContainer maskContainer) { + this.maskContainer = maskContainer; + RelativeSizeAxes = Axes.Both; AlwaysPresent = true; Alpha = 0; + + maskContainer.MaskSelected += onSelected; + maskContainer.MaskDeselected += onDeselected; } [BackgroundDependencyLoader] @@ -51,58 +58,34 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers }; } - /// - /// Tracks a selectable . - /// - /// The to track. - public void AddMask(HitObjectMask mask) + #region User Input Handling + + // Only handle input on selectable or selected masks + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => selectableMasks.Concat(selectedMasks).Any(m => m.ReceiveMouseInputAt(screenSpacePos)); + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - mask.Selected += onSelected; - mask.Deselected += onDeselected; - mask.SingleSelectionRequested += onSingleSelectionRequested; - } + if (selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position))) + return true; - /// - /// Stops tracking a . - /// - /// The to stop tracking. - public void RemoveMask(HitObjectMask mask) - { - mask.Selected -= onSelected; - mask.Deselected -= onDeselected; - mask.SingleSelectionRequested -= onSingleSelectionRequested; - } + DeselectAll(); + selectableMasks.First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)).Select(); - private void onSelected(HitObjectMask mask) => selectedMasks.Add(mask); - - private void onDeselected(HitObjectMask mask) - { - selectedMasks.Remove(mask); - - // We don't want to update visibility if > 0, since we may be deselecting masks during drag-selection - if (selectedMasks.Count == 0) - UpdateVisibility(); - } - - private void onSingleSelectionRequested(HitObjectMask mask) - { - selectedMasks.Add(mask); UpdateVisibility(); + return true; } - // Only handle input on the selected masks - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => selectedMasks.Any(m => m.ReceiveMouseInputAt(screenSpacePos)); - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; - protected override bool OnClick(InputState state) { - if (state.Mouse.NativeState.PositionMouseDown == null) - throw new InvalidOperationException("Click event received without a mouse down position."); + if (selectedMasks.Count == 1) + return true; - // We handled mousedown, but if the mouse has been clicked and not dragged, select the mask which would've handled the mouse down - // A mousedown event is triggered such that a single selection is requested - selectedMasks.First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.PositionMouseDown.Value)).TriggerOnMouseDown(state); + var toSelect = selectedMasks.First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); + + DeselectAll(); + toSelect.Select(); + + UpdateVisibility(); return true; } @@ -127,10 +110,32 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers protected override bool OnDragEnd(InputState state) => true; + #endregion + + #region Selection Handling + + private void onSelected(HitObjectMask mask) => selectedMasks.Add(mask); + + private void onDeselected(HitObjectMask mask) + { + selectedMasks.Remove(mask); + + // We don't want to update visibility if > 0, since we may be deselecting masks during drag-selection + if (selectedMasks.Count == 0) + UpdateVisibility(); + } + + /// + /// Deselects all selected s. + /// + public void DeselectAll() => selectedMasks.ToList().ForEach(m => m.Deselect()); + + #endregion + /// /// Updates whether this is visible. /// - public void UpdateVisibility() + internal void UpdateVisibility() { if (selectedMasks.Count > 0) Show(); @@ -145,8 +150,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers if (selectedMasks.Count == 0) return; - // Todo: We might need to optimise this - // Move the rectangle to cover the hitobjects var topLeft = new Vector2(float.MaxValue, float.MaxValue); var bottomRight = new Vector2(float.MinValue, float.MinValue); @@ -165,5 +168,13 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers box.Size = bottomRight - topLeft; box.Position = topLeft; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + maskContainer.MaskSelected -= onSelected; + maskContainer.MaskDeselected -= onDeselected; + } } } From 5d0a636cc4fc24ccaa3f868007a97d3334a81d87 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Mar 2018 15:51:38 +0900 Subject: [PATCH 357/537] Rename SelectionBox -> Selection --- .../Compose/Layers/{SelectionBox.cs => Selection.cs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Screens/Edit/Screens/Compose/Layers/{SelectionBox.cs => Selection.cs} (93%) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs similarity index 93% rename from osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs rename to osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs index 833c94d3f4..661929c5b4 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private readonly List selectedMasks = new List(); private IEnumerable selectableMasks => maskContainer.AliveMasks; - private Drawable box; + private Drawable outline; public SelectionBox(MaskContainer maskContainer) { @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers [BackgroundDependencyLoader] private void load(OsuColour colours) { - InternalChild = box = new Container + InternalChild = outline = new Container { Masking = true, BorderThickness = BORDER_RADIUS, @@ -165,8 +165,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers topLeft -= new Vector2(5); bottomRight += new Vector2(5); - box.Size = bottomRight - topLeft; - box.Position = topLeft; + outline.Size = bottomRight - topLeft; + outline.Position = topLeft; } protected override void Dispose(bool isDisposing) From 53541a5c8da1a154c9a0d823704db500f9d59d0f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Mar 2018 15:53:31 +0900 Subject: [PATCH 358/537] Add license header --- .../Screens/Edit/Screens/Compose/Layers/MaskContainer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 4b3ea077bc..0662070857 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics.Containers; From 69a7ddbf1e8ff160fcdd3fcc5a43e41e7b87e939 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Mar 2018 16:23:17 +0900 Subject: [PATCH 359/537] Fix ordering of display/input of HitObjectMasks --- .../Edit/Screens/Compose/Layers/MaskContainer.cs | 15 +++++++++++++++ .../Edit/Screens/Compose/Layers/Selection.cs | 11 +++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 0662070857..89bae004b5 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; @@ -49,5 +50,19 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask); private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask); + + protected override int Compare(Drawable x, Drawable y) + { + if (!(x is HitObjectMask xMask) || !(y is HitObjectMask yMask)) + return base.Compare(x, y); + return Compare(xMask, yMask); + } + + public int Compare(HitObjectMask x, HitObjectMask y) + { + // Put earlier hitobjects towards the end of the list, so they handle input first + int i = y.HitObject.HitObject.StartTime.CompareTo(x.HitObject.HitObject.StartTime); + return i == 0 ? CompareReverseChildID(x, y) : i; + } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs index 661929c5b4..4ff1284f8a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; +using osu.Framework.Lists; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Types; @@ -24,7 +25,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private readonly MaskContainer maskContainer; - private readonly List selectedMasks = new List(); + private readonly SortedList selectedMasks; private IEnumerable selectableMasks => maskContainer.AliveMasks; private Drawable outline; @@ -33,6 +34,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { this.maskContainer = maskContainer; + selectedMasks = new SortedList(maskContainer.Compare); + RelativeSizeAxes = Axes.Both; AlwaysPresent = true; Alpha = 0; @@ -61,7 +64,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers #region User Input Handling // Only handle input on selectable or selected masks - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => selectableMasks.Concat(selectedMasks).Any(m => m.ReceiveMouseInputAt(screenSpacePos)); + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => selectableMasks.Reverse().Concat(selectedMasks).Any(m => m.ReceiveMouseInputAt(screenSpacePos)); protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { @@ -69,7 +72,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers return true; DeselectAll(); - selectableMasks.First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)).Select(); + selectableMasks.Reverse().First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)).Select(); UpdateVisibility(); return true; @@ -80,7 +83,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers if (selectedMasks.Count == 1) return true; - var toSelect = selectedMasks.First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); + var toSelect = selectedMasks.Reverse().First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); DeselectAll(); toSelect.Select(); From f1f7d978ec92be92183755cd9a1fa646e1d3e1b3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 30 Mar 2018 16:28:59 +0900 Subject: [PATCH 360/537] Add some comments --- osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs index 4ff1284f8a..427acbef5a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs @@ -68,6 +68,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { + // If masks are overlapping, make sure we don't change the selection if the overlapped portion is pressed if (selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position))) return true; @@ -80,6 +81,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers protected override bool OnClick(InputState state) { + // If there's only mask, this isn't going to change anything, so we can save on doing some processing here if (selectedMasks.Count == 1) return true; From 7e303754431c565581d9ac1637a93e05e52c3078 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 30 Mar 2018 10:46:46 +0300 Subject: [PATCH 361/537] Use selectNextRandom instead --- osu.Game/Screens/Select/BeatmapCarousel.cs | 5 ++-- .../Carousel/CarouselGroupEagerSelect.cs | 24 +++++++------------ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index f80b1dc9fd..12791eb193 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Select get { return beatmapSets.Select(g => g.BeatmapSet); } set { - CarouselGroup newRoot = new CarouselGroupEagerSelect(true); + CarouselGroup newRoot = new CarouselGroupEagerSelect(this); Task.Run(() => { @@ -102,10 +102,11 @@ namespace osu.Game.Screens.Select private readonly Stack randomSelectedBeatmaps = new Stack(); protected List Items = new List(); - private CarouselGroup root = new CarouselGroupEagerSelect(true); + private CarouselGroup root; public BeatmapCarousel() { + root = new CarouselGroupEagerSelect(this); Child = new OsuContextMenuContainer { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index eb75483e50..7f64af8216 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.MathUtils; using System; using System.Linq; @@ -12,11 +11,11 @@ namespace osu.Game.Screens.Select.Carousel /// public class CarouselGroupEagerSelect : CarouselGroup { - private readonly bool isRootSelector; + private readonly BeatmapCarousel parent; - public CarouselGroupEagerSelect(bool isRootSelector = false) + public CarouselGroupEagerSelect(BeatmapCarousel parent = null) { - this.isRootSelector = isRootSelector; + this.parent = parent; State.ValueChanged += v => { if (v == CarouselItemState.Selected) @@ -87,20 +86,15 @@ namespace osu.Game.Screens.Select.Carousel // we only perform eager selection if none of our children are in a selected state already. if (Children.Any(i => i.State == CarouselItemState.Selected)) return; - CarouselItem nextToSelect = null; - if (isRootSelector && lastSelected == null) + if (parent != null && lastSelected == null) { - var selectables = Children.Where(i => !i.Filtered).ToList(); - if (selectables.Any()) - nextToSelect = selectables[RNG.Next(selectables.Count)]; - } - else - { - nextToSelect = - Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered) ?? - Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered); + parent.SelectNextRandom(); + return; } + CarouselItem nextToSelect = + Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered) ?? + Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered); if (nextToSelect != null) nextToSelect.State.Value = CarouselItemState.Selected; From c39f056b3b6a497281751c21d99ba52809fa0cfd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Mar 2018 17:53:13 +0900 Subject: [PATCH 362/537] Fix skin path being ignored in extension agnostic lookups Closes #2295. --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1b52507688..63d2543bc3 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -92,7 +92,7 @@ namespace osu.Game.Skinning string lastPiece = filename.Split('/').Last(); var file = source.Files.FirstOrDefault(f => - string.Equals(hasExtension ? f.Filename : Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase)); + string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), lastPiece, StringComparison.InvariantCultureIgnoreCase)); return file?.FileInfo.StoragePath; } From 7fed8d64de9ce135ccc6fd40115d321f72af4e4c Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Sat, 31 Mar 2018 04:59:08 +0900 Subject: [PATCH 363/537] Implement Judgement Colours --- .../Rulesets/Judgements/DrawableJudgement.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index ca203e1cdb..4b01cc5ccd 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Judgements { private const float judgement_size = 80; + private OsuColour colours; + protected readonly Judgement Judgement; public readonly DrawableHitObject JudgedObject; @@ -45,11 +47,12 @@ namespace osu.Game.Rulesets.Judgements [BackgroundDependencyLoader] private void load(OsuColour colours) { + this.colours = colours; Child = new SkinnableDrawable($"Play/{Judgement.Result}", _ => JudgementText = new OsuSpriteText { Text = Judgement.Result.GetDescription().ToUpper(), Font = @"Venera", - Colour = Judgement.Result == HitResult.Miss ? colours.Red : Color4.White, + Colour = judgementColour(Judgement.Result), Scale = new Vector2(0.85f, 1), TextSize = 12 }, restrictSize: false); @@ -84,5 +87,27 @@ namespace osu.Game.Rulesets.Judgements Expire(true); } + + private Color4 judgementColour(HitResult judgement) + { + switch (judgement) + { + case HitResult.Perfect: + case HitResult.Great: + return colours.Blue; + + case HitResult.Ok: + case HitResult.Good: + return colours.Green; + + case HitResult.Meh: + return colours.Yellow; + + case HitResult.Miss: + return colours.Red; + } + + return Color4.White; + } } } From 551ba6ac4bc77c7f3097a369ab250339a2730064 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Apr 2018 13:04:47 +0900 Subject: [PATCH 364/537] Fix ScalableContainer irrepairably altering content size --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 6 +-- .../Replays/OsuReplayInputHandler.cs | 2 +- .../Visual/TestCaseScrollingHitObjects.cs | 2 +- osu.Game/Input/Handlers/ReplayInputHandler.cs | 4 +- osu.Game/Rulesets/UI/RulesetContainer.cs | 2 +- osu.Game/Rulesets/UI/ScalableContainer.cs | 47 ++++++++++++------- 6 files changed, 38 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 8bb206543b..2b6a7c41f4 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -30,10 +30,10 @@ namespace osu.Game.Rulesets.Catch.UI Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; - ScaledContent.Anchor = Anchor.BottomLeft; - ScaledContent.Origin = Anchor.BottomLeft; + base.Content.Anchor = Anchor.BottomLeft; + base.Content.Origin = Anchor.BottomLeft; - ScaledContent.AddRange(new Drawable[] + base.Content.AddRange(new Drawable[] { explodingFruitContainer = new Container { diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs index 0a61b0f199..69154a1d0c 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Replays { new ReplayState { - Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)), + Mouse = new ReplayMouseState(GamefieldToScreenSpace(Position ?? Vector2.Zero)), PressedActions = CurrentFrame.Actions } }; diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs index 745ae9ad9d..0742dd68eb 100644 --- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs @@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual Direction = direction; Padding = new MarginPadding(2); - ScaledContent.Masking = true; + Content.Masking = true; AddInternal(new Box { diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs index 8aa3a53cc2..c431af0219 100644 --- a/osu.Game/Input/Handlers/ReplayInputHandler.cs +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -13,9 +13,9 @@ namespace osu.Game.Input.Handlers public abstract class ReplayInputHandler : InputHandler { /// - /// A function provided to convert replay coordinates from gamefield to screen space. + /// A function that converts coordinates from gamefield to screen space. /// - public Func ToScreenSpace { protected get; set; } + public Func GamefieldToScreenSpace { protected get; set; } /// /// Update the current frame based on an incoming time value. diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 2201b6963f..81418fecd4 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -290,7 +290,7 @@ namespace osu.Game.Rulesets.UI base.SetReplay(replay); if (ReplayInputManager?.ReplayInputHandler != null) - ReplayInputManager.ReplayInputHandler.ToScreenSpace = input => Playfield.ScaledContent.ToScreenSpace(input); + ReplayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace; } /// diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs index 9762828e7d..04e6db9578 100644 --- a/osu.Game/Rulesets/UI/ScalableContainer.cs +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; @@ -12,13 +13,16 @@ namespace osu.Game.Rulesets.UI /// public class ScalableContainer : Container { + /// + /// A function that converts coordinates from gamefield to screen space. + /// + public Func GamefieldToScreenSpace => scaledContent.GamefieldToScreenSpace; + /// /// The scaled content. /// - public readonly Container ScaledContent; - - protected override Container Content => content; - private readonly Container content; + private readonly ScaledContainer scaledContent; + protected override Container Content => scaledContent; /// /// A which can have its internal coordinate system scaled to a specific size. @@ -31,17 +35,21 @@ namespace osu.Game.Rulesets.UI /// public ScalableContainer(float? customWidth = null, float? customHeight = null) { - AddInternal(ScaledContent = new ScaledContainer + AddInternal(scaledContent = new ScaledContainer { CustomWidth = customWidth, CustomHeight = customHeight, RelativeSizeAxes = Axes.Both, - Child = content = new Container { RelativeSizeAxes = Axes.Both } }); } private class ScaledContainer : Container { + /// + /// A function that converts coordinates from gamefield to screen space. + /// + public Func GamefieldToScreenSpace => content.ToScreenSpace; + /// /// The value to scale the width of the content to match. /// If null, is used. @@ -54,6 +62,22 @@ namespace osu.Game.Rulesets.UI /// public float? CustomHeight; + private readonly Container content; + protected override Container Content => content; + + public ScaledContainer() + { + AddInternal(content = new Container { RelativeSizeAxes = Axes.Both }); + } + + protected override void Update() + { + base.Update(); + + content.Scale = sizeScale; + content.Size = Vector2.Divide(Vector2.One, sizeScale); + } + /// /// The scale that is required for the size of the content to match and . /// @@ -70,17 +94,6 @@ namespace osu.Game.Rulesets.UI return Vector2.One; } } - - /// - /// Scale the content to the required container size by multiplying by . - /// - protected override Vector2 DrawScale => sizeScale * base.DrawScale; - - protected override void Update() - { - base.Update(); - RelativeChildSize = new Vector2(CustomWidth.HasValue ? sizeScale.X : RelativeChildSize.X, CustomHeight.HasValue ? sizeScale.Y : RelativeChildSize.Y); - } } } } From b842f682eb32b38abee9a855e88d3778c695dab0 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 2 Apr 2018 12:06:34 +0800 Subject: [PATCH 365/537] Use Linq.Append and Prepend. --- osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs | 2 +- osu.Game/Beatmaps/Formats/Decoder.cs | 2 +- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 6 ++---- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- 9 files changed, 10 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs index 42fe95356d..a06390a4ea 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModAutoplay : ModAutoplay { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray(); protected override Score CreateReplayScore(Beatmap beatmap) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs index f94ee484fc..07128cb8ff 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs @@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModNoFail : ModNoFail { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index c9def8c8cf..ed774f0d0a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModRelax : ModRelax { public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs index 797e0af0ad..6c15095bfe 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs @@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModSuddenDeath : ModSuddenDeath { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); } } diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 9f10485c5f..c07bedc8a6 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.Formats public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams) { var output = CreateTemplateObject(); - foreach (StreamReader stream in new[] { primaryStream }.Concat(otherStreams)) + foreach (StreamReader stream in otherStreams.Prepend(primaryStream)) ParseStreamInto(stream, output); return output; } diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 97e473a797..9e74a935ea 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -45,7 +45,7 @@ namespace osu.Game.Input.Bindings }; protected override IEnumerable KeyBindingInputQueue => - handler == null ? base.KeyBindingInputQueue : new[] { handler }.Concat(base.KeyBindingInputQueue); + handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler); } public enum GlobalAction diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 379d25313e..71c346d404 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.KeyBinding private FillFlowContainer buttons; - public IEnumerable FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())); + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text); public KeyBindingRow(object action, IEnumerable bindings) { diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c076b53f51..24a0a7e643 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -128,10 +128,8 @@ namespace osu.Game.Rulesets.Edit selectionLayer.SelectionFinished += hitObjectMaskLayer.AddSelectionOverlay; toolboxCollection.Items = - new[] { new RadioButton("Select", () => setCompositionTool(null)) } - .Concat( - CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) - ) + CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) + .Prepend(new RadioButton("Select", () => setCompositionTool(null))) .ToList(); toolboxCollection.Items[0].Select(); diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index d5a91e1a6b..a3ea3b056b 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -150,7 +150,7 @@ namespace osu.Game.Screens.Select var mods = modSelect.SelectedMods.Value; if (mods.All(m => m.GetType() != autoType)) { - modSelect.SelectedMods.Value = mods.Concat(new[] { auto }); + modSelect.SelectedMods.Value = mods.Append(auto); removeAutoModOnResume = true; } } From 82a847b820e11adb2bbbfccd4c0778d3e9ca9638 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Apr 2018 15:16:10 +0900 Subject: [PATCH 366/537] Extract random selection logic into CarouselRoot --- osu.Game/Screens/Select/BeatmapCarousel.cs | 6 ++--- .../Carousel/CarouselGroupEagerSelect.cs | 27 +++++++++---------- .../Screens/Select/Carousel/CarouselRoot.cs | 23 ++++++++++++++++ 3 files changed, 39 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Screens/Select/Carousel/CarouselRoot.cs diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 12791eb193..dfbbc2b5d5 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Select get { return beatmapSets.Select(g => g.BeatmapSet); } set { - CarouselGroup newRoot = new CarouselGroupEagerSelect(this); + CarouselRoot newRoot = new CarouselRoot(this); Task.Run(() => { @@ -102,11 +102,11 @@ namespace osu.Game.Screens.Select private readonly Stack randomSelectedBeatmaps = new Stack(); protected List Items = new List(); - private CarouselGroup root; + private CarouselRoot root; public BeatmapCarousel() { - root = new CarouselGroupEagerSelect(this); + root = new CarouselRoot(this); Child = new OsuContextMenuContainer { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 7f64af8216..fabf333461 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -11,11 +11,8 @@ namespace osu.Game.Screens.Select.Carousel /// public class CarouselGroupEagerSelect : CarouselGroup { - private readonly BeatmapCarousel parent; - - public CarouselGroupEagerSelect(BeatmapCarousel parent = null) + public CarouselGroupEagerSelect() { - this.parent = parent; State.ValueChanged += v => { if (v == CarouselItemState.Selected) @@ -23,13 +20,16 @@ namespace osu.Game.Screens.Select.Carousel }; } + /// + /// The last selected item. + /// + protected CarouselItem LastSelected { get; private set; } + /// /// We need to keep track of the index for cases where the selection is removed but we want to select a new item based on its old location. /// private int lastSelectedIndex; - private CarouselItem lastSelected; - /// /// To avoid overhead during filter operations, we don't attempt any selections until after all /// children have been filtered. This bool will be true during the base @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Select.Carousel { base.RemoveChild(i); - if (i != lastSelected) + if (i != LastSelected) updateSelectedIndex(); } @@ -86,12 +86,11 @@ namespace osu.Game.Screens.Select.Carousel // we only perform eager selection if none of our children are in a selected state already. if (Children.Any(i => i.State == CarouselItemState.Selected)) return; - if (parent != null && lastSelected == null) - { - parent.SelectNextRandom(); - return; - } + PerformSelection(); + } + protected virtual void PerformSelection() + { CarouselItem nextToSelect = Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered) ?? Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered); @@ -104,10 +103,10 @@ namespace osu.Game.Screens.Select.Carousel private void updateSelected(CarouselItem newSelection) { - lastSelected = newSelection; + LastSelected = newSelection; updateSelectedIndex(); } - private void updateSelectedIndex() => lastSelectedIndex = lastSelected == null ? 0 : Math.Max(0, InternalChildren.IndexOf(lastSelected)); + private void updateSelectedIndex() => lastSelectedIndex = LastSelected == null ? 0 : Math.Max(0, InternalChildren.IndexOf(LastSelected)); } } diff --git a/osu.Game/Screens/Select/Carousel/CarouselRoot.cs b/osu.Game/Screens/Select/Carousel/CarouselRoot.cs new file mode 100644 index 0000000000..b23d7631c9 --- /dev/null +++ b/osu.Game/Screens/Select/Carousel/CarouselRoot.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Select.Carousel +{ + public class CarouselRoot : CarouselGroupEagerSelect + { + private readonly BeatmapCarousel carousel; + + public CarouselRoot(BeatmapCarousel carousel) + { + this.carousel = carousel; + } + + protected override void PerformSelection() + { + if (LastSelected == null) + carousel.SelectNextRandom(); + else + base.PerformSelection(); + } + } +} From 69e2d4fd22b61c150adda0c76d0330362f64e800 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Apr 2018 15:23:58 +0900 Subject: [PATCH 367/537] Make CarouselRoot a nested class --- osu.Game/Screens/Select/BeatmapCarousel.cs | 18 +++++++++++++++ .../Screens/Select/Carousel/CarouselRoot.cs | 23 ------------------- 2 files changed, 18 insertions(+), 23 deletions(-) delete mode 100644 osu.Game/Screens/Select/Carousel/CarouselRoot.cs diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index dfbbc2b5d5..96f2e40553 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -628,5 +628,23 @@ namespace osu.Game.Screens.Select // layer transformations on top, with a similar reasoning to the previous comment. p.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1)); } + + private class CarouselRoot : CarouselGroupEagerSelect + { + private readonly BeatmapCarousel carousel; + + public CarouselRoot(BeatmapCarousel carousel) + { + this.carousel = carousel; + } + + protected override void PerformSelection() + { + if (LastSelected == null) + carousel.SelectNextRandom(); + else + base.PerformSelection(); + } + } } } diff --git a/osu.Game/Screens/Select/Carousel/CarouselRoot.cs b/osu.Game/Screens/Select/Carousel/CarouselRoot.cs deleted file mode 100644 index b23d7631c9..0000000000 --- a/osu.Game/Screens/Select/Carousel/CarouselRoot.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Screens.Select.Carousel -{ - public class CarouselRoot : CarouselGroupEagerSelect - { - private readonly BeatmapCarousel carousel; - - public CarouselRoot(BeatmapCarousel carousel) - { - this.carousel = carousel; - } - - protected override void PerformSelection() - { - if (LastSelected == null) - carousel.SelectNextRandom(); - else - base.PerformSelection(); - } - } -} From 75f7d43d9d7b84bd38ed28e1e438d715c2295f92 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Mon, 2 Apr 2018 11:56:35 +0300 Subject: [PATCH 368/537] Testcases for CarouselRoot --- .../Visual/TestCaseBeatmapCarousel.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index c68e548f44..4ae897bc1a 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual private readonly Stack selectedSets = new Stack(); + private readonly HashSet eagerSelectedIDs = new HashSet(); private BeatmapInfo currentSelection; @@ -80,6 +81,7 @@ namespace osu.Game.Tests.Visual testEmptyTraversal(); testHiding(); testSelectingFilteredRuleset(); + testCarouselRootIsRandom(); } private void ensureRandomFetchSuccess() => @@ -151,6 +153,17 @@ namespace osu.Game.Tests.Visual AddAssert("Selection is visible", selectedBeatmapVisible); } + private void checkNonmatchingFilter() + { + AddStep("Toggel non-matching filter", () => + { + carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false); + carousel.Filter(new FilterCriteria(), false); + eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID); + } + ); + } + /// /// Test keyboard traversal /// @@ -403,6 +416,23 @@ namespace osu.Game.Tests.Visual AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle)); } + private void testCarouselRootIsRandom() + { + List beatmapSets = new List(); + + for (int i = 1; i <= 50; i++) + beatmapSets.Add(createTestBeatmapSet(i)); + + AddStep("Load 50 Beatmaps", () => { carousel.BeatmapSets = beatmapSets; }); + advanceSelection(direction: 1, diff: false); + checkNonmatchingFilter(); + checkNonmatchingFilter(); + checkNonmatchingFilter(); + checkNonmatchingFilter(); + checkNonmatchingFilter(); + AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1); + } + private BeatmapSetInfo createTestBeatmapSet(int id) { return new BeatmapSetInfo From d24d81d8a9c9e7d002045fd08cbf04e4731abdc9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Apr 2018 20:07:18 +0900 Subject: [PATCH 369/537] De-pluralize some methods --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 74b7d0272e..afb9907473 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -81,13 +81,13 @@ namespace osu.Game.Beatmaps.Formats handleDifficulty(line); return; case Section.Events: - handleEvents(line); + handleEvent(line); return; case Section.TimingPoints: - handleTimingPoints(line); + handleTimingPoint(line); return; case Section.HitObjects: - handleHitObjects(line); + handleHitObject(line); return; } @@ -246,7 +246,7 @@ namespace osu.Game.Beatmaps.Formats } } - private void handleEvents(string line) + private void handleEvent(string line) { string[] split = line.Split(','); @@ -275,7 +275,7 @@ namespace osu.Game.Beatmaps.Formats } } - private void handleTimingPoints(string line) + private void handleTimingPoint(string line) { string[] split = line.Split(','); @@ -361,7 +361,7 @@ namespace osu.Game.Beatmaps.Formats } } - private void handleHitObjects(string line) + private void handleHitObject(string line) { // If the ruleset wasn't specified, assume the osu!standard ruleset. if (parser == null) From fd9480cfb68c2cb344fa3c2b8a2d31cd22a3ccbd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Apr 2018 20:08:40 +0900 Subject: [PATCH 370/537] Handle timingpoint FormatException --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 150 +++++++++--------- 1 file changed, 78 insertions(+), 72 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index afb9907473..52f1a01fcb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -277,87 +277,93 @@ namespace osu.Game.Beatmaps.Formats private void handleTimingPoint(string line) { - string[] split = line.Split(','); - - double time = getOffsetTime(double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo)); - double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); - double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; - - TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; - if (split.Length >= 3) - timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); - - LegacySampleBank sampleSet = defaultSampleBank; - if (split.Length >= 4) - sampleSet = (LegacySampleBank)int.Parse(split[3]); - - //SampleBank sampleBank = SampleBank.Default; - //if (split.Length >= 5) - // sampleBank = (SampleBank)int.Parse(split[4]); - - int sampleVolume = defaultSampleVolume; - if (split.Length >= 6) - sampleVolume = int.Parse(split[5]); - - bool timingChange = true; - if (split.Length >= 7) - timingChange = split[6][0] == '1'; - - bool kiaiMode = false; - bool omitFirstBarSignature = false; - if (split.Length >= 8) + try { - int effectFlags = int.Parse(split[7]); - kiaiMode = (effectFlags & 1) > 0; - omitFirstBarSignature = (effectFlags & 8) > 0; - } + string[] split = line.Split(','); - string stringSampleSet = sampleSet.ToString().ToLower(); - if (stringSampleSet == @"none") - stringSampleSet = @"normal"; + double time = getOffsetTime(double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo)); + double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); + double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); - SampleControlPoint samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); + TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; + if (split.Length >= 3) + timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); - if (timingChange) - { - beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + LegacySampleBank sampleSet = defaultSampleBank; + if (split.Length >= 4) + sampleSet = (LegacySampleBank)int.Parse(split[3]); + + //SampleBank sampleBank = SampleBank.Default; + //if (split.Length >= 5) + // sampleBank = (SampleBank)int.Parse(split[4]); + + int sampleVolume = defaultSampleVolume; + if (split.Length >= 6) + sampleVolume = int.Parse(split[5]); + + bool timingChange = true; + if (split.Length >= 7) + timingChange = split[6][0] == '1'; + + bool kiaiMode = false; + bool omitFirstBarSignature = false; + if (split.Length >= 8) { - Time = time, - BeatLength = beatLength, - TimeSignature = timeSignature - }); - } + int effectFlags = int.Parse(split[7]); + kiaiMode = (effectFlags & 1) > 0; + omitFirstBarSignature = (effectFlags & 8) > 0; + } - if (speedMultiplier != difficultyPoint.SpeedMultiplier) - { - beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); - beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint - { - Time = time, - SpeedMultiplier = speedMultiplier - }); - } + string stringSampleSet = sampleSet.ToString().ToLower(); + if (stringSampleSet == @"none") + stringSampleSet = @"normal"; - if (stringSampleSet != samplePoint.SampleBank || sampleVolume != samplePoint.SampleVolume) - { - beatmap.ControlPointInfo.SamplePoints.Add(new SampleControlPoint - { - Time = time, - SampleBank = stringSampleSet, - SampleVolume = sampleVolume - }); - } + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); + SampleControlPoint samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); - if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) - { - beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + if (timingChange) { - Time = time, - KiaiMode = kiaiMode, - OmitFirstBarLine = omitFirstBarSignature - }); + beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + { + Time = time, + BeatLength = beatLength, + TimeSignature = timeSignature + }); + } + + if (speedMultiplier != difficultyPoint.SpeedMultiplier) + { + beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); + beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint + { + Time = time, + SpeedMultiplier = speedMultiplier + }); + } + + if (stringSampleSet != samplePoint.SampleBank || sampleVolume != samplePoint.SampleVolume) + { + beatmap.ControlPointInfo.SamplePoints.Add(new SampleControlPoint + { + Time = time, + SampleBank = stringSampleSet, + SampleVolume = sampleVolume + }); + } + + if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) + { + beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + { + Time = time, + KiaiMode = kiaiMode, + OmitFirstBarLine = omitFirstBarSignature + }); + } + } + catch (FormatException e) + { } } From 0065724992cb505cbd7b51fc191bbf1bafe05909 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Apr 2018 13:05:01 +0900 Subject: [PATCH 371/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 85b3494117..6852878ce3 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 85b3494117ccef1b396b70957e1cffaba06e2b54 +Subproject commit 6852878ce30e1bfde301282563d09c7927d9106c From 4f19059e55c381c6a7410a8c4bae8eaf5f465dc5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Apr 2018 21:29:49 +0900 Subject: [PATCH 372/537] DragBox -> DragLayer --- .../Screens/Compose/Layers/{DragBox.cs => DragLayer.cs} | 8 ++++---- .../Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game/Screens/Edit/Screens/Compose/Layers/{DragBox.cs => DragLayer.cs} (89%) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs similarity index 89% rename from osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs rename to osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs index ea170a0326..5ad2aeb0e2 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragBox.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs @@ -14,9 +14,9 @@ using OpenTK.Graphics; namespace osu.Game.Screens.Edit.Screens.Compose.Layers { /// - /// A box that handles and displays drag selection for a collection of s. + /// A layer that handles and displays drag selection for a collection of s. /// - public class DragBox : CompositeDrawable + public class DragLayer : CompositeDrawable { /// /// Invoked when the drag selection has finished. @@ -28,10 +28,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private Drawable box; /// - /// Creates a new . + /// Creates a new . /// /// The selectable s. - public DragBox(MaskContainer maskContainer) + public DragLayer(MaskContainer maskContainer) { this.maskContainer = maskContainer; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index f972f9ac81..259bddce46 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers selectionBox = composer.CreateSelectionBox(maskContainer); - var dragBox = new DragBox(maskContainer); + var dragBox = new DragLayer(maskContainer); dragBox.DragEnd += () => selectionBox.UpdateVisibility(); InternalChildren = new Drawable[] From 65f0e91734d847ec53c6ef7d7ebf3f442a4053be Mon Sep 17 00:00:00 2001 From: DrabWeb Date: Wed, 4 Apr 2018 03:04:26 -0300 Subject: [PATCH 373/537] Use Interpolation function directly for resizing tab strips. --- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 1624b255e0..8b692f32bc 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; +using osu.Framework.MathUtils; using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface @@ -90,7 +91,7 @@ namespace osu.Game.Graphics.UserInterface // dont bother calculating if the strip is invisible if (strip.Colour.MaxAlpha > 0) - strip.ResizeWidthTo(StripWidth(), 500, Easing.OutQuint); + strip.Width = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), strip.Width, StripWidth(), 0, 500, Easing.OutQuint); } public class OsuTabItem : TabItem, IHasAccentColour From 9df525a38d86d18b8048131ce796301556394730 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Wed, 4 Apr 2018 15:11:16 +0900 Subject: [PATCH 374/537] Remove extra whitespace In general we don't really do this elsewhere in the codebase. --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 4b01cc5ccd..f20997e96d 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -95,14 +95,11 @@ namespace osu.Game.Rulesets.Judgements case HitResult.Perfect: case HitResult.Great: return colours.Blue; - case HitResult.Ok: case HitResult.Good: return colours.Green; - case HitResult.Meh: return colours.Yellow; - case HitResult.Miss: return colours.Red; } From bed46b1f7e38b3d92ce55f7bce63bb0680f92625 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Wed, 4 Apr 2018 15:13:42 +0900 Subject: [PATCH 375/537] Split local variable assignment from construction of children --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index f20997e96d..a1a27c0d43 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -48,6 +48,7 @@ namespace osu.Game.Rulesets.Judgements private void load(OsuColour colours) { this.colours = colours; + Child = new SkinnableDrawable($"Play/{Judgement.Result}", _ => JudgementText = new OsuSpriteText { Text = Judgement.Result.GetDescription().ToUpper(), From d453c2589a33d9c303850a6dcc5a98ee8f5355a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 16:02:20 +0900 Subject: [PATCH 376/537] Add an explanatory comment for weird override --- osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs index 427acbef5a..f81aa440ac 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs @@ -63,7 +63,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers #region User Input Handling - // Only handle input on selectable or selected masks + /// + /// Handle input on currently selectable or already selected masks. + /// Keep in mind that selectedMasks may contain masks for non-current objects, which we still want to handle input while selected. + /// public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => selectableMasks.Reverse().Concat(selectedMasks).Any(m => m.ReceiveMouseInputAt(screenSpacePos)); protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) From 364c3bca06122e0c04ed72240cd9a379381df19b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Apr 2018 16:24:03 +0900 Subject: [PATCH 377/537] Fix osu!catch autoplay missing starts/ends of JuiceStreams Fixes #2328. Would only happen when ticks and ends were spaced too far apart (or there were no ticks in a juicestream). --- .../TestCaseJuiceStream.cs | 62 +++++++++++++++++++ .../Replays/CatchAutoGenerator.cs | 1 + 2 files changed, 63 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/TestCaseJuiceStream.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseJuiceStream.cs new file mode 100644 index 0000000000..0af60cc452 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseJuiceStream.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestCaseJuiceStream : TestCasePlayer + { + public TestCaseJuiceStream() + : base(new CatchRuleset()) + { + } + + protected override Beatmap CreateBeatmap(Ruleset ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, + Ruleset = ruleset.RulesetInfo + } + }; + + for (int i = 0; i < 100; i++) + { + float width = (i % 10 + 1) / 20f; + + beatmap.HitObjects.Add(new JuiceStream + { + X = 0.5f - width / 2, + ControlPoints = new List + { + Vector2.Zero, + new Vector2(width * CatchPlayfield.BASE_WIDTH, 0) + }, + CurveType = CurveType.Linear, + Distance = width * CatchPlayfield.BASE_WIDTH, + StartTime = i * 2000, + NewCombo = i % 8 == 0 + }); + } + + return beatmap; + } + + protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) + { + beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + return base.CreatePlayer(beatmap, ruleset); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index f1503a14ee..244ab2b508 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -108,6 +108,7 @@ namespace osu.Game.Rulesets.Catch.Replays case BananaShower.Banana _: case TinyDroplet _: case Droplet _: + case Fruit _: moveToNext(nestedObj); break; } From 4196bb8c245d4b79d1d7d1ebb3f56b06708618c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 16:24:13 +0900 Subject: [PATCH 378/537] Move selection logic to MaskContainer --- .../Edit/Screens/Compose/Layers/DragLayer.cs | 9 +-------- .../Edit/Screens/Compose/Layers/MaskContainer.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs index 5ad2aeb0e2..8ed9cab79d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs @@ -78,14 +78,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers box.Position = topLeft; box.Size = bottomRight - topLeft; - foreach (var mask in maskContainer.AliveMasks) - { - if (mask.IsPresent && dragRectangle.Contains(mask.SelectionPoint)) - mask.Select(); - else - mask.Deselect(); - } - + maskContainer.Select(dragRectangle); return true; } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 89bae004b5..9aea17844a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; namespace osu.Game.Screens.Edit.Screens.Compose.Layers { @@ -48,6 +49,21 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers return result; } + /// + /// Select all masks in a given rectangle selection area. + /// + /// The rectangle to perform a selection on in screen-space coordinates. + public void Select(RectangleF rect) + { + foreach (var mask in AliveMasks) + { + if (mask.IsPresent && rect.Contains(mask.SelectionPoint)) + mask.Select(); + else + mask.Deselect(); + } + } + private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask); private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask); From c712b29b5b28f0ac30c624fc34b2e67f991c75d9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 16:24:27 +0900 Subject: [PATCH 379/537] Rename dragBox to dragLayer --- .../Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 259bddce46..ca8525b842 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -35,15 +35,15 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers selectionBox = composer.CreateSelectionBox(maskContainer); - var dragBox = new DragLayer(maskContainer); - dragBox.DragEnd += () => selectionBox.UpdateVisibility(); + var dragLayer = new DragLayer(maskContainer); + dragLayer.DragEnd += () => selectionBox.UpdateVisibility(); InternalChildren = new Drawable[] { - dragBox, + dragLayer, maskContainer, selectionBox, - dragBox.CreateProxy() + dragLayer.CreateProxy() }; foreach (var obj in playfield.HitObjects.Objects) From d4cb00e08f87f2a15e045a2c9fcfc2618dd82d59 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Apr 2018 17:12:41 +0900 Subject: [PATCH 380/537] Don't display judgements in OsuEditPlayfield --- osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs | 1 + osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs index 5f232b1889..46a3d8575f 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs @@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Osu.Edit public class OsuEditPlayfield : OsuPlayfield { protected override bool ProxyApproachCircles => false; + protected override bool DisplayJudgements => false; } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 0c5d757474..9010f66acb 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.UI // Todo: This should not be a thing, but is currently required for the editor // https://github.com/ppy/osu-framework/issues/1283 protected virtual bool ProxyApproachCircles => true; + protected virtual bool DisplayJudgements => true; public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); @@ -73,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.UI private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) { - if (!judgedObject.DisplayJudgement) + if (!judgedObject.DisplayJudgement || !DisplayJudgements) return; DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject) From c2d371797ea4f5405ca28992e28ed9f1df500ad0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 17:38:34 +0900 Subject: [PATCH 381/537] Fix unbind failure --- osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 9aea17844a..9367d9e2dc 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers if (result) { drawable.Selected -= onMaskSelected; - drawable.Deselected += onMaskDeselected; + drawable.Deselected -= onMaskDeselected; } return result; From b6b8c5165729be6489fe0915eecbcf5d4a508d20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 18:20:32 +0900 Subject: [PATCH 382/537] Remove DragLayer dependency on MaskContainer --- .../Screens/Edit/Screens/Compose/Layers/DragLayer.cs | 11 +++++------ .../Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs index 8ed9cab79d..376747ee20 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs @@ -18,23 +18,22 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// public class DragLayer : CompositeDrawable { + private readonly Action performSelection; + /// /// Invoked when the drag selection has finished. /// public event Action DragEnd; - private readonly MaskContainer maskContainer; - private Drawable box; /// /// Creates a new . /// /// The selectable s. - public DragLayer(MaskContainer maskContainer) + public DragLayer(Action performSelection) { - this.maskContainer = maskContainer; - + this.performSelection = performSelection; RelativeSizeAxes = Axes.Both; AlwaysPresent = true; Alpha = 0; @@ -78,7 +77,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers box.Position = topLeft; box.Size = bottomRight - topLeft; - maskContainer.Select(dragRectangle); + performSelection?.Invoke(dragRectangle); return true; } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index ca8525b842..ad8e752d19 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -35,8 +35,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers selectionBox = composer.CreateSelectionBox(maskContainer); - var dragLayer = new DragLayer(maskContainer); dragLayer.DragEnd += () => selectionBox.UpdateVisibility(); + var dragLayer = new DragLayer(maskContainer.Select); InternalChildren = new Drawable[] { From 4d71f2084cdd40d2d5c471e3150e863dfc84b088 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 18:21:27 +0900 Subject: [PATCH 383/537] Move individual mask selection logic out of MaskSelection --- .../Visual/TestCaseEditorSelectionLayer.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 +- osu.Game/Rulesets/Edit/HitObjectMask.cs | 17 +++- .../Edit/Screens/Compose/Layers/DragLayer.cs | 2 +- .../Compose/Layers/HitObjectMaskLayer.cs | 20 ++--- .../Screens/Compose/Layers/MaskContainer.cs | 17 +++- .../Layers/{Selection.cs => MaskSelection.cs} | 77 +++++++++---------- 7 files changed, 82 insertions(+), 57 deletions(-) rename osu.Game/Screens/Edit/Screens/Compose/Layers/{Selection.cs => MaskSelection.cs} (69%) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 4e39548b5b..1d110477f7 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { - typeof(SelectionBox), + typeof(MaskSelection), typeof(HitObjectComposer), typeof(OsuHitObjectComposer), typeof(HitObjectMaskLayer), diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 4cd5d857c0..d87d00d11f 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -248,11 +248,11 @@ namespace osu.Game.Rulesets.Edit public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null; /// - /// Creates a which outlines s + /// Creates a which outlines s /// and handles hitobject pattern adjustments. /// /// The container. - public virtual SelectionBox CreateSelectionBox(MaskContainer maskContainer) => new SelectionBox(maskContainer); + public virtual MaskSelection CreateSelectionBox(MaskContainer maskContainer) => new MaskSelection(maskContainer); /// /// Creates a which provides a layer above or below the . diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index 981e109747..ed6df54722 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; +using osu.Framework.Input; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; @@ -24,6 +25,13 @@ namespace osu.Game.Rulesets.Edit /// public event Action Deselected; + /// + /// Invoked when this has rqeuested selection. + /// Will fire even if already selected. + /// Does not actually perform selection. + /// + public event Action SelectionRequested; + /// /// The which this applies to. /// @@ -31,7 +39,8 @@ namespace osu.Game.Rulesets.Edit protected override bool ShouldBeAlive => HitObject.IsAlive || State == Visibility.Visible; public override bool RemoveWhenNotAlive => false; - public override bool HandleMouseInput => HitObject.IsPresent; + + public override bool HandleMouseInput => ShouldBeAlive; public HitObjectMask(DrawableHitObject hitObject) { @@ -55,6 +64,12 @@ namespace osu.Game.Rulesets.Edit return true; } + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + SelectionRequested?.Invoke(this); + return base.OnMouseDown(state, args); + } + /// /// Deselects this , causing it to become invisible. /// diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs index 376747ee20..67dc45a7b2 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { Masking = true, BorderColour = Color4.White, - BorderThickness = SelectionBox.BORDER_RADIUS, + BorderThickness = MaskSelection.BORDER_RADIUS, Child = new Box { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index ad8e752d19..06ae9140bf 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private readonly HitObjectComposer composer; private MaskContainer maskContainer; - private SelectionBox selectionBox; + private MaskSelection maskSelection; public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer) { @@ -33,16 +33,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { maskContainer = new MaskContainer(); - selectionBox = composer.CreateSelectionBox(maskContainer); + maskSelection = composer.CreateSelectionBox(maskContainer); - dragLayer.DragEnd += () => selectionBox.UpdateVisibility(); var dragLayer = new DragLayer(maskContainer.Select); + dragLayer.DragEnd += () => maskSelection.UpdateVisibility(); InternalChildren = new Drawable[] { dragLayer, + maskSelection, maskContainer, - selectionBox, dragLayer.CreateProxy() }; @@ -50,6 +50,12 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers addMask(obj); } + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + maskContainer.DeselectAll(); + return true; + } + /// /// Adds a mask for a which adds movement support. /// @@ -75,11 +81,5 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers maskContainer.Remove(mask); } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - selectionBox.DeselectAll(); - return true; - } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 9367d9e2dc..4cfca2c93a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -23,17 +23,25 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// public event Action MaskDeselected; + public event Action MaskSelectionRequested; + /// /// All the s with == true. /// public IEnumerable AliveMasks => AliveInternalChildren.Cast(); + public MaskContainer() + { + RelativeSizeAxes = Axes.Both; + } + public override void Add(HitObjectMask drawable) { base.Add(drawable); drawable.Selected += onMaskSelected; drawable.Deselected += onMaskDeselected; + drawable.SelectionRequested += onSelectionRequested; } public override bool Remove(HitObjectMask drawable) @@ -44,6 +52,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { drawable.Selected -= onMaskSelected; drawable.Deselected -= onMaskDeselected; + drawable.SelectionRequested -= onSelectionRequested; } return result; @@ -59,13 +68,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { if (mask.IsPresent && rect.Contains(mask.SelectionPoint)) mask.Select(); - else - mask.Deselect(); } } + /// + /// Deselects all selected s. + /// + public void DeselectAll() => AliveMasks.ToList().ForEach(m => m.Deselect()); + private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask); private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask); + private void onSelectionRequested(HitObjectMask mask) => MaskSelectionRequested?.Invoke(mask); protected override int Compare(Drawable x, Drawable y) { diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs similarity index 69% rename from osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs rename to osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index f81aa440ac..666fe16afb 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/Selection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -19,19 +18,19 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// /// A box which surrounds s and provides interactive handles, context menus etc. /// - public class SelectionBox : CompositeDrawable + public class MaskSelection : CompositeDrawable { public const float BORDER_RADIUS = 2; private readonly MaskContainer maskContainer; private readonly SortedList selectedMasks; - private IEnumerable selectableMasks => maskContainer.AliveMasks; private Drawable outline; - public SelectionBox(MaskContainer maskContainer) + public MaskSelection(MaskContainer maskContainer) { + // todo: remove this this.maskContainer = maskContainer; selectedMasks = new SortedList(maskContainer.Compare); @@ -42,6 +41,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers maskContainer.MaskSelected += onSelected; maskContainer.MaskDeselected += onDeselected; + maskContainer.MaskSelectionRequested += onSelectionRequested; } [BackgroundDependencyLoader] @@ -63,42 +63,23 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers #region User Input Handling - /// - /// Handle input on currently selectable or already selected masks. - /// Keep in mind that selectedMasks may contain masks for non-current objects, which we still want to handle input while selected. - /// - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => selectableMasks.Reverse().Concat(selectedMasks).Any(m => m.ReceiveMouseInputAt(screenSpacePos)); + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => handleInput(state); + + protected override bool OnDragStart(InputState state) => handleInput(state); + + protected override bool OnDragEnd(InputState state) => true; + + private bool handleInput(InputState state) { - // If masks are overlapping, make sure we don't change the selection if the overlapped portion is pressed - if (selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position))) - return true; - - DeselectAll(); - selectableMasks.Reverse().First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)).Select(); + if (!selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position))) + return false; UpdateVisibility(); return true; } - protected override bool OnClick(InputState state) - { - // If there's only mask, this isn't going to change anything, so we can save on doing some processing here - if (selectedMasks.Count == 1) - return true; - - var toSelect = selectedMasks.Reverse().First(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); - - DeselectAll(); - toSelect.Select(); - - UpdateVisibility(); - return true; - } - - protected override bool OnDragStart(InputState state) => true; - protected override bool OnDrag(InputState state) { // Todo: Various forms of snapping @@ -116,8 +97,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers return true; } - protected override bool OnDragEnd(InputState state) => true; - #endregion #region Selection Handling @@ -133,15 +112,32 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers UpdateVisibility(); } - /// - /// Deselects all selected s. - /// - public void DeselectAll() => selectedMasks.ToList().ForEach(m => m.Deselect()); + private void onSelectionRequested(HitObjectMask mask) + { + if (GetContainingInputManager().CurrentState.Keyboard.ControlPressed) + { + if (mask.State == Visibility.Visible) + // we don't want this deselection to affect input for this frame. + Schedule(() => mask.Deselect()); + else + mask.Select(); + } + else + { + if (mask.State == Visibility.Visible) + return; + + maskContainer.DeselectAll(); + mask.Select(); + } + + UpdateVisibility(); + } #endregion /// - /// Updates whether this is visible. + /// Updates whether this is visible. /// internal void UpdateVisibility() { @@ -183,6 +179,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers maskContainer.MaskSelected -= onSelected; maskContainer.MaskDeselected -= onDeselected; + maskContainer.MaskSelectionRequested -= onSelectionRequested; } } } From e69951b59f63769397da87caeec84e3cae6c2221 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 18:48:19 +0900 Subject: [PATCH 384/537] Rename test to signify it's got auto enabled --- .../{TestCaseJuiceStream.cs => TestCaseAutoJuiceStream.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Rulesets.Catch.Tests/{TestCaseJuiceStream.cs => TestCaseAutoJuiceStream.cs} (92%) diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs similarity index 92% rename from osu.Game.Rulesets.Catch.Tests/TestCaseJuiceStream.cs rename to osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index 0af60cc452..11a22c69f3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -13,9 +13,9 @@ using OpenTK; namespace osu.Game.Rulesets.Catch.Tests { - public class TestCaseJuiceStream : TestCasePlayer + public class TestCaseAutoJuiceStream : TestCasePlayer { - public TestCaseJuiceStream() + public TestCaseAutoJuiceStream() : base(new CatchRuleset()) { } From bce114a37b018d67b3eafd4b17a91758f9bba25a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 18:55:17 +0900 Subject: [PATCH 385/537] Make AliveMasks private --- .../Screens/Edit/Screens/Compose/Layers/MaskContainer.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 4cfca2c93a..cbe064d179 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -25,10 +25,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers public event Action MaskSelectionRequested; - /// - /// All the s with == true. - /// - public IEnumerable AliveMasks => AliveInternalChildren.Cast(); + private IEnumerable aliveMasks => AliveInternalChildren.Cast(); public MaskContainer() { @@ -64,7 +61,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// The rectangle to perform a selection on in screen-space coordinates. public void Select(RectangleF rect) { - foreach (var mask in AliveMasks) + foreach (var mask in aliveMasks) { if (mask.IsPresent && rect.Contains(mask.SelectionPoint)) mask.Select(); @@ -74,7 +71,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// /// Deselects all selected s. /// - public void DeselectAll() => AliveMasks.ToList().ForEach(m => m.Deselect()); + public void DeselectAll() => aliveMasks.ToList().ForEach(m => m.Deselect()); private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask); private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask); From 31a7db0a35555a34574a7d5239f24a9553caf1c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 19:42:59 +0900 Subject: [PATCH 386/537] Fix drag mishaps --- osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 666fe16afb..65e85d29bc 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -63,8 +63,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers #region User Input Handling - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => handleInput(state); protected override bool OnDragStart(InputState state) => handleInput(state); @@ -73,7 +71,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private bool handleInput(InputState state) { - if (!selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.Position))) + if (!selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.PositionMouseDown ?? state.Mouse.NativeState.Position))) return false; UpdateVisibility(); From a997ec6139bc46672a49a72a8ca986fab2e3b2a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 19:51:56 +0900 Subject: [PATCH 387/537] Fix ShouldBeAlive state --- osu.Game/Rulesets/Edit/HitObjectMask.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index ed6df54722..79c671f335 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -37,10 +37,9 @@ namespace osu.Game.Rulesets.Edit /// public readonly DrawableHitObject HitObject; - protected override bool ShouldBeAlive => HitObject.IsAlive || State == Visibility.Visible; - public override bool RemoveWhenNotAlive => false; - + protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == Visibility.Visible; public override bool HandleMouseInput => ShouldBeAlive; + public override bool RemoveWhenNotAlive => false; public HitObjectMask(DrawableHitObject hitObject) { From 5c036b966b30eb1f2619a10f964d57f429fbdb5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 20:02:38 +0900 Subject: [PATCH 388/537] Formatting fixes --- osu.Game/Rulesets/Edit/HitObjectMask.cs | 5 ++--- osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs | 1 + .../Screens/Edit/Screens/Compose/Layers/MaskContainer.cs | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index 79c671f335..910da712b4 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -26,9 +26,8 @@ namespace osu.Game.Rulesets.Edit public event Action Deselected; /// - /// Invoked when this has rqeuested selection. - /// Will fire even if already selected. - /// Does not actually perform selection. + /// Invoked when this has requested selection. + /// Will fire even if already selected. Does not actually perform selection. /// public event Action SelectionRequested; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs index 67dc45a7b2..51bb61b607 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs @@ -34,6 +34,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers public DragLayer(Action performSelection) { this.performSelection = performSelection; + RelativeSizeAxes = Axes.Both; AlwaysPresent = true; Alpha = 0; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index cbe064d179..c29a254cab 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -23,6 +23,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// public event Action MaskDeselected; + /// + /// Invoked when any requests selection. + /// public event Action MaskSelectionRequested; private IEnumerable aliveMasks => AliveInternalChildren.Cast(); From 2b15555ede43b3657e75a6e14d669c76569f0b76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 20:05:01 +0900 Subject: [PATCH 389/537] Remove MaskContainer dependency in MaskSelection --- osu-framework | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 +- .../Compose/Layers/HitObjectMaskLayer.cs | 8 ++- .../Screens/Compose/Layers/MaskContainer.cs | 2 +- .../Screens/Compose/Layers/MaskSelection.cs | 49 ++++++++++--------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/osu-framework b/osu-framework index 6852878ce3..e72c85be22 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 6852878ce30e1bfde301282563d09c7927d9106c +Subproject commit e72c85be22b9d853df075b965cdd433eb9deccf3 diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index d87d00d11f..9b33ad2563 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -251,8 +251,7 @@ namespace osu.Game.Rulesets.Edit /// Creates a which outlines s /// and handles hitobject pattern adjustments. /// - /// The container. - public virtual MaskSelection CreateSelectionBox(MaskContainer maskContainer) => new MaskSelection(maskContainer); + public virtual MaskSelection CreateMaskSelection() => new MaskSelection(); /// /// Creates a which provides a layer above or below the . diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 06ae9140bf..c407f363a5 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -18,7 +18,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private readonly HitObjectComposer composer; private MaskContainer maskContainer; - private MaskSelection maskSelection; public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer) { @@ -33,7 +32,12 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { maskContainer = new MaskContainer(); - maskSelection = composer.CreateSelectionBox(maskContainer); + var maskSelection = composer.CreateMaskSelection(); + + maskContainer.MaskSelected += maskSelection.HandleSelected; + maskContainer.MaskDeselected += maskSelection.HandleDeselected; + maskContainer.MaskSelectionRequested += maskSelection.HandleSelectionRequested; + maskSelection.DeselectAll = maskContainer.DeselectAll; var dragLayer = new DragLayer(maskContainer.Select); dragLayer.DragEnd += () => maskSelection.UpdateVisibility(); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index c29a254cab..401cd16fef 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers return Compare(xMask, yMask); } - public int Compare(HitObjectMask x, HitObjectMask y) + public static int Compare(HitObjectMask x, HitObjectMask y) { // Put earlier hitobjects towards the end of the list, so they handle input first int i = y.HitObject.HitObject.StartTime.CompareTo(x.HitObject.HitObject.StartTime); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 65e85d29bc..c874d84997 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -22,26 +23,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { public const float BORDER_RADIUS = 2; - private readonly MaskContainer maskContainer; - private readonly SortedList selectedMasks; private Drawable outline; - public MaskSelection(MaskContainer maskContainer) + public MaskSelection() { - // todo: remove this - this.maskContainer = maskContainer; - - selectedMasks = new SortedList(maskContainer.Compare); + selectedMasks = new SortedList(MaskContainer.Compare); RelativeSizeAxes = Axes.Both; AlwaysPresent = true; Alpha = 0; - - maskContainer.MaskSelected += onSelected; - maskContainer.MaskDeselected += onDeselected; - maskContainer.MaskSelectionRequested += onSelectionRequested; } [BackgroundDependencyLoader] @@ -99,9 +91,22 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers #region Selection Handling - private void onSelected(HitObjectMask mask) => selectedMasks.Add(mask); + /// + /// Bind an action to deselect all selected masks. + /// + public Action DeselectAll { private get; set; } - private void onDeselected(HitObjectMask mask) + /// + /// Handle a mask becoming selected. + /// + /// The mask. + public void HandleSelected(HitObjectMask mask) => selectedMasks.Add(mask); + + /// + /// Handle a mask becoming deselected. + /// + /// The mask. + public void HandleDeselected(HitObjectMask mask) { selectedMasks.Remove(mask); @@ -110,7 +115,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers UpdateVisibility(); } - private void onSelectionRequested(HitObjectMask mask) + /// + /// Handle a mask requesting selection. + /// + /// The mask. + public void HandleSelectionRequested(HitObjectMask mask) { if (GetContainingInputManager().CurrentState.Keyboard.ControlPressed) { @@ -125,7 +134,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers if (mask.State == Visibility.Visible) return; - maskContainer.DeselectAll(); + + DeselectAll?.Invoke(); mask.Select(); } @@ -170,14 +180,5 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers outline.Size = bottomRight - topLeft; outline.Position = topLeft; } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - maskContainer.MaskSelected -= onSelected; - maskContainer.MaskDeselected -= onDeselected; - maskContainer.MaskSelectionRequested -= onSelectionRequested; - } } } From 94c3f385418263bc5c99af6b3cb99431c4ccfda4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 20:06:45 +0900 Subject: [PATCH 390/537] Pass down input state instead of parent lookup --- osu.Game/Rulesets/Edit/HitObjectMask.cs | 4 ++-- .../Screens/Edit/Screens/Compose/Layers/MaskContainer.cs | 5 +++-- .../Screens/Edit/Screens/Compose/Layers/MaskSelection.cs | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index 910da712b4..741f600a21 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Edit /// Invoked when this has requested selection. /// Will fire even if already selected. Does not actually perform selection. /// - public event Action SelectionRequested; + public event Action SelectionRequested; /// /// The which this applies to. @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Edit protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - SelectionRequested?.Invoke(this); + SelectionRequested?.Invoke(this, state); return base.OnMouseDown(state, args); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 401cd16fef..cade7daae2 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Game.Rulesets.Edit; using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// /// Invoked when any requests selection. /// - public event Action MaskSelectionRequested; + public event Action MaskSelectionRequested; private IEnumerable aliveMasks => AliveInternalChildren.Cast(); @@ -78,7 +79,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask); private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask); - private void onSelectionRequested(HitObjectMask mask) => MaskSelectionRequested?.Invoke(mask); + private void onSelectionRequested(HitObjectMask mask, InputState state) => MaskSelectionRequested?.Invoke(mask, state); protected override int Compare(Drawable x, Drawable y) { diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index c874d84997..249659c2a4 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -119,9 +119,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// Handle a mask requesting selection. /// /// The mask. - public void HandleSelectionRequested(HitObjectMask mask) + public void HandleSelectionRequested(HitObjectMask mask, InputState state) { - if (GetContainingInputManager().CurrentState.Keyboard.ControlPressed) + if (state.Keyboard.ControlPressed) { if (mask.State == Visibility.Visible) // we don't want this deselection to affect input for this frame. From b7325d73e8c1ac260a346404d7b8a5c5175ab9aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 20:44:07 +0900 Subject: [PATCH 391/537] Don't inherit VisbilityContainer --- .../Selection/Overlays/SliderCircleMask.cs | 3 +- osu.Game/Rulesets/Edit/HitObjectMask.cs | 92 +++++++++++++------ .../Screens/Compose/Layers/MaskSelection.cs | 7 +- 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs index 4e22b4f693..96ff14205e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects; @@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays AddInternal(new RingPiece()); - State = Visibility.Visible; + Select(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index 741f600a21..4be79df285 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Input; @@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Edit /// /// A mask placed above a adding editing functionality. /// - public class HitObjectMask : VisibilityContainer + public class HitObjectMask : CompositeDrawable, IStateful { /// /// Invoked when this has been selected. @@ -36,7 +37,7 @@ namespace osu.Game.Rulesets.Edit /// public readonly DrawableHitObject HitObject; - protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == Visibility.Visible; + protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == SelectionState.Selected; public override bool HandleMouseInput => ShouldBeAlive; public override bool RemoveWhenNotAlive => false; @@ -45,45 +46,72 @@ namespace osu.Game.Rulesets.Edit HitObject = hitObject; AlwaysPresent = true; - State = Visibility.Hidden; + Alpha = 0; + } + + private SelectionState state; + + public event Action StateChanged; + + public SelectionState State + { + get => state; + set + { + if (state == value) return; + + state = value; + switch (state) + { + case SelectionState.Selected: + Show(); + Selected?.Invoke(this); + break; + case SelectionState.NotSelected: + Hide(); + Deselected?.Invoke(this); + break; + } + } } /// /// Selects this , causing it to become visible. /// - /// True if the was selected. False if the was already selected. - public bool Select() - { - if (State == Visibility.Visible) - return false; - - Show(); - Selected?.Invoke(this); - return true; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - SelectionRequested?.Invoke(this, state); - return base.OnMouseDown(state, args); - } + public void Select() => State = SelectionState.Selected; /// /// Deselects this , causing it to become invisible. /// - /// True if the was deselected. False if the was already deselected. - public bool Deselect() - { - if (State == Visibility.Hidden) - return false; + public void Deselect() => State = SelectionState.NotSelected; - Hide(); - Deselected?.Invoke(this); - return true; + public bool IsSelected => State == SelectionState.Selected; + + private bool selectionRequested; + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + selectionRequested = false; + + if (State == SelectionState.NotSelected && !selectionRequested) + { + SelectionRequested?.Invoke(this, state); + selectionRequested = true; + } + + return base.OnMouseDown(state, args); } - protected override void PopIn() => Alpha = 1; - protected override void PopOut() => Alpha = 0; + protected override bool OnClick(InputState state) + { + if (State == SelectionState.Selected && !selectionRequested) + { + selectionRequested = true; + SelectionRequested?.Invoke(this, state); + } + + return base.OnClick(state); + } /// /// The screen-space point that causes this to be selected. @@ -95,4 +123,10 @@ namespace osu.Game.Rulesets.Edit /// public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; } + + public enum SelectionState + { + NotSelected, + Selected + } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 249659c2a4..54cbd9f1b5 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -123,15 +123,14 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { if (state.Keyboard.ControlPressed) { - if (mask.State == Visibility.Visible) - // we don't want this deselection to affect input for this frame. - Schedule(() => mask.Deselect()); + if (mask.IsSelected) + mask.Deselect(); else mask.Select(); } else { - if (mask.State == Visibility.Visible) + if (mask.IsSelected) return; From 216c4629e025ea71b317c50bd5d796e50a7ef5da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Apr 2018 20:44:22 +0900 Subject: [PATCH 392/537] Fix dragging backwards not deselecting pending selection --- osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index cade7daae2..aae4822826 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -69,6 +69,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { if (mask.IsPresent && rect.Contains(mask.SelectionPoint)) mask.Select(); + else + mask.Deselect(); } } From c304c1eecf9713602ed2fa55caa343d8d2b67bfc Mon Sep 17 00:00:00 2001 From: naoey Date: Fri, 9 Mar 2018 19:46:16 +0530 Subject: [PATCH 393/537] Make LinkFlowContainer handle beatmap id lookup in game. --- .../Graphics/Containers/LinkFlowContainer.cs | 6 +++--- .../API/Requests/GetBeatmapSetRequest.cs | 16 ++++++++++++---- osu.Game/OsuGame.cs | 6 ++++++ osu.Game/Overlays/BeatmapSetOverlay.cs | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 1d231ada23..0631d24f87 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -71,9 +71,9 @@ namespace osu.Game.Graphics.Containers switch (linkType) { case LinkAction.OpenBeatmap: - // todo: replace this with overlay.ShowBeatmap(id) once an appropriate API call is implemented. - if (int.TryParse(linkArgument, out int beatmapId)) - Process.Start($"https://osu.ppy.sh/b/{beatmapId}"); + // TODO: proper query params handling + if (int.TryParse(linkArgument.Contains('?') ? linkArgument.Split('?')[0] : linkArgument, out int beatmapId)) + game?.ShowBeatmap(beatmapId); break; case LinkAction.OpenBeatmapSet: if (int.TryParse(linkArgument, out int setId)) diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs index cba1d9471c..645c54ad6b 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs @@ -5,13 +5,21 @@ namespace osu.Game.Online.API.Requests { public class GetBeatmapSetRequest : APIRequest { - private readonly int beatmapSetId; + private readonly int id; + private readonly BeatmapSetLookupType type; - public GetBeatmapSetRequest(int beatmapSetId) + public GetBeatmapSetRequest(int id, BeatmapSetLookupType type = BeatmapSetLookupType.SetId) { - this.beatmapSetId = beatmapSetId; + this.id = id; + this.type = type; } - protected override string Target => $@"beatmapsets/{beatmapSetId}"; + protected override string Target => type == BeatmapSetLookupType.SetId ? $@"beatmapsets/{id}" : $@"beatmapsets/lookup?beatmap_id={id}"; + } + + public enum BeatmapSetLookupType + { + SetId, + BeatmapId, } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 89447b8ed6..a1f42fbfdd 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -160,6 +160,12 @@ namespace osu.Game /// The user to display. public void ShowUser(long userId) => userProfile.ShowUser(userId); + /// + /// Show a beatmap's set as an overlay, displaying the given beatmap. + /// + /// The beatmap to show. + public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.ShowBeatmap(beatmapId); + protected void LoadScore(Score s) { scoreLoad?.Cancel(); diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index f0f8a6ef10..c7c8a4d50e 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -17,6 +17,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; using osu.Game.Overlays.BeatmapSet.Scores; +using System.Linq; namespace osu.Game.Overlays { @@ -139,6 +140,23 @@ namespace osu.Game.Overlays return true; } + public void ShowBeatmap(int beatmapId) + { + var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); + req.Success += res => + { + ShowBeatmapSet(res.ToBeatmapSet(rulesets)); + header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); + }; + api.Queue(req); + } + + public void ShowBeatmap(BeatmapInfo beatmap) + { + ShowBeatmapSet(beatmap.BeatmapSet); + header.Picker.Beatmap.Value = beatmap; + } + public void ShowBeatmapSet(int beatmapSetId) { // todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. From 7482d5986a6fa6312e4c33f6336afe8137b6ee51 Mon Sep 17 00:00:00 2001 From: naoey Date: Sat, 10 Mar 2018 11:25:26 +0530 Subject: [PATCH 394/537] Add a loading state to BeatmapSetOverlay. - Handle null value in header and info sections - Add item to context menu for carousel beatmaps to show details --- .../Visual/TestCaseBeatmapSetOverlay.cs | 1 + osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 3 + osu.Game/Overlays/BeatmapSet/Info.cs | 3 + osu.Game/Overlays/BeatmapSetOverlay.cs | 55 ++++++++++++++++--- .../Carousel/DrawableCarouselBeatmap.cs | 11 +++- 6 files changed, 64 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs index 6605c61026..09e76c6354 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs @@ -374,6 +374,7 @@ namespace osu.Game.Tests.Visual AddStep(@"hide", overlay.Hide); AddStep(@"show without reload", overlay.Show); + AddStep(@"show loading", () => overlay.BeatmapSet = null); } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index b09e151ebc..2ac232f2ce 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -150,7 +150,7 @@ namespace osu.Game.Overlays.BeatmapSet Beatmap.TriggerChange(); } - private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version; + private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap?.Version; private void updateDifficultyButtons() { diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index b9a35ec1f0..bbf385fe0f 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -48,6 +48,9 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; + if (beatmapSet == null) + return; + Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; title.Text = BeatmapSet.Metadata.Title; artist.Text = BeatmapSet.Metadata.Artist; diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index a0b6d9cefa..2d0a97aafb 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -34,6 +34,9 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; + if (beatmapSet == null) + return; + source.Text = BeatmapSet.Metadata.Source; tags.Text = BeatmapSet.Metadata.Tags; } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index c7c8a4d50e..c431ce7561 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -18,17 +18,22 @@ using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; using osu.Game.Overlays.BeatmapSet.Scores; using System.Linq; +using osu.Framework.Configuration; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays { public class BeatmapSetOverlay : WaveOverlayContainer { + private const int fade_duration = 300; + public const float X_PADDING = 40; public const float RIGHT_WIDTH = 275; private readonly Header header; private readonly Info info; private readonly ScoresContainer scores; + private readonly LoadingAnimation loading; private APIAccess api; private RulesetStore rulesets; @@ -36,6 +41,31 @@ namespace osu.Game.Overlays private readonly ScrollContainer scroll; + private BeatmapSetInfo beatmapSet; + + public BeatmapSetInfo BeatmapSet + { + get => beatmapSet; + set + { + if (value == beatmapSet) + return; + + beatmapSet = value; + + if (beatmapSet == null) + { + scroll.FadeOut(fade_duration); + loading.FadeIn(fade_duration); + return; + } + + header.BeatmapSet = info.BeatmapSet = beatmapSet; + loading.FadeOut(fade_duration); + scroll.FadeIn(fade_duration); + } + } + // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; @@ -67,10 +97,17 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.2f) }, + loading = new LoadingAnimation + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 1, + }, scroll = new ScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, + Alpha = 0, Child = new ReverseChildIDFillFlowContainer { RelativeSizeAxes = Axes.X, @@ -89,7 +126,9 @@ namespace osu.Game.Overlays header.Picker.Beatmap.ValueChanged += b => { info.Beatmap = b; - updateScores(b); + + if (b != null) + updateScores(b); }; } @@ -132,6 +171,7 @@ namespace osu.Game.Overlays base.PopOut(); header.Details.StopPreview(); FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); + BeatmapSet = null; } protected override bool OnClick(InputState state) @@ -142,6 +182,7 @@ namespace osu.Game.Overlays public void ShowBeatmap(int beatmapId) { + BeatmapSet = null; var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); req.Success += res => { @@ -149,25 +190,21 @@ namespace osu.Game.Overlays header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); }; api.Queue(req); - } - - public void ShowBeatmap(BeatmapInfo beatmap) - { - ShowBeatmapSet(beatmap.BeatmapSet); - header.Picker.Beatmap.Value = beatmap; + Show(); } public void ShowBeatmapSet(int beatmapSetId) { - // todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. + BeatmapSet = null; var req = new GetBeatmapSetRequest(beatmapSetId); req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets)); api.Queue(req); + Show(); } public void ShowBeatmapSet(BeatmapSetInfo set) { - header.BeatmapSet = info.BeatmapSet = set; + BeatmapSet = set; Show(); scroll.ScrollTo(0); } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 38cb5fc5d8..c0cb469e15 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using OpenTK; using OpenTK.Graphics; @@ -35,6 +36,8 @@ namespace osu.Game.Screens.Select.Carousel private Triangles triangles; private StarCounter starCounter; + private BeatmapSetOverlay beatmapOverlay; + public DrawableCarouselBeatmap(CarouselBeatmap panel) : base(panel) { beatmap = panel.Beatmap; @@ -42,8 +45,10 @@ namespace osu.Game.Screens.Select.Carousel } [BackgroundDependencyLoader(true)] - private void load(SongSelect songSelect, BeatmapManager manager) + private void load(SongSelect songSelect, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay) { + this.beatmapOverlay = beatmapOverlay; + if (songSelect != null) { startRequested = songSelect.FinaliseSelection; @@ -171,6 +176,10 @@ namespace osu.Game.Screens.Select.Carousel new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested?.Invoke(beatmap)), new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap)), new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap)), + new OsuMenuItem("Details", MenuItemType.Standard, () => + { + if (beatmap.OnlineBeatmapID.HasValue) beatmapOverlay?.ShowBeatmap(beatmap.OnlineBeatmapID.Value); + }), }; } } From ca4299c6fe0ae4f5d6b8da646b1bf8456a7ea0f4 Mon Sep 17 00:00:00 2001 From: naoey Date: Wed, 4 Apr 2018 22:02:45 +0530 Subject: [PATCH 395/537] Remove unused using and fix possible nullref. --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 0631d24f87..df780cf242 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -72,7 +72,7 @@ namespace osu.Game.Graphics.Containers { case LinkAction.OpenBeatmap: // TODO: proper query params handling - if (int.TryParse(linkArgument.Contains('?') ? linkArgument.Split('?')[0] : linkArgument, out int beatmapId)) + if (linkArgument != null && int.TryParse(linkArgument.Contains('?') ? linkArgument.Split('?')[0] : linkArgument, out int beatmapId)) game?.ShowBeatmap(beatmapId); break; case LinkAction.OpenBeatmapSet: diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index c431ce7561..c144e03bcb 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -18,7 +18,6 @@ using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; using osu.Game.Overlays.BeatmapSet.Scores; using System.Linq; -using osu.Framework.Configuration; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays From 0ad4b8a6f8cb7f00bc34a8336294f4672fe1d6a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Apr 2018 15:55:47 +0900 Subject: [PATCH 396/537] Remove TestTestCase No longer necessary as we have restructured tests considerably. --- osu.Game/Tests/TestTestCase.cs | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 osu.Game/Tests/TestTestCase.cs diff --git a/osu.Game/Tests/TestTestCase.cs b/osu.Game/Tests/TestTestCase.cs deleted file mode 100644 index 4efd57095e..0000000000 --- a/osu.Game/Tests/TestTestCase.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Testing; - -namespace osu.Game.Tests -{ - [TestFixture] - internal class TestTestCase : TestCase - { - // This TestCase is required for nunit to not throw errors - // See: https://github.com/nunit/nunit/issues/1118 - } -} From 37fb207abd55e7bdccd31fd7eab0cbe0dc0059d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Apr 2018 15:55:47 +0900 Subject: [PATCH 397/537] Remove TestTestCase No longer necessary as we have restructured tests considerably. --- osu-framework | 2 +- osu.Game/Tests/TestTestCase.cs | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 osu.Game/Tests/TestTestCase.cs diff --git a/osu-framework b/osu-framework index 6852878ce3..e4b0b57f5d 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 6852878ce30e1bfde301282563d09c7927d9106c +Subproject commit e4b0b57f5d3a80c09dcdfb6c8d30962e842b9fc3 diff --git a/osu.Game/Tests/TestTestCase.cs b/osu.Game/Tests/TestTestCase.cs deleted file mode 100644 index 4efd57095e..0000000000 --- a/osu.Game/Tests/TestTestCase.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Testing; - -namespace osu.Game.Tests -{ - [TestFixture] - internal class TestTestCase : TestCase - { - // This TestCase is required for nunit to not throw errors - // See: https://github.com/nunit/nunit/issues/1118 - } -} From 345cfb077d415d43c16dedeb51331c41ba2b177b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Apr 2018 21:03:39 +0900 Subject: [PATCH 398/537] No need to sort list any more --- .../Screens/Edit/Screens/Compose/Layers/MaskContainer.cs | 2 +- .../Screens/Edit/Screens/Compose/Layers/MaskSelection.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index aae4822826..12b5971051 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers return Compare(xMask, yMask); } - public static int Compare(HitObjectMask x, HitObjectMask y) + public int Compare(HitObjectMask x, HitObjectMask y) { // Put earlier hitobjects towards the end of the list, so they handle input first int i = y.HitObject.HitObject.StartTime.CompareTo(x.HitObject.HitObject.StartTime); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 54cbd9f1b5..38efb1ae45 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -2,13 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; -using osu.Framework.Lists; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Types; @@ -23,13 +23,13 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers { public const float BORDER_RADIUS = 2; - private readonly SortedList selectedMasks; + private readonly List selectedMasks; private Drawable outline; public MaskSelection() { - selectedMasks = new SortedList(MaskContainer.Compare); + selectedMasks = new List(); RelativeSizeAxes = Axes.Both; AlwaysPresent = true; From 5749e7156011259b3197716089986a99795fc6b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Apr 2018 21:06:48 +0900 Subject: [PATCH 399/537] Apply review fixes --- osu.Game/Rulesets/Edit/HitObjectMask.cs | 2 +- osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index 4be79df285..d7984cdf0c 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Edit { selectionRequested = false; - if (State == SelectionState.NotSelected && !selectionRequested) + if (State == SelectionState.NotSelected) { SelectionRequested?.Invoke(this, state); selectionRequested = true; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 38efb1ae45..67bc5551da 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -133,7 +133,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers if (mask.IsSelected) return; - DeselectAll?.Invoke(); mask.Select(); } From 24b9a8c9838c1801fe9f2a4997375589bf1eb3b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Apr 2018 10:29:34 +0900 Subject: [PATCH 400/537] Allow HitObjectMasks to handle drag events directly --- osu.Game/Rulesets/Edit/HitObjectMask.cs | 16 +++++++++++++++- .../Compose/Layers/HitObjectMaskLayer.cs | 2 ++ .../Screens/Compose/Layers/MaskContainer.cs | 8 ++++++++ .../Screens/Compose/Layers/MaskSelection.cs | 19 +------------------ 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index d7984cdf0c..9f055ffc5d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -32,6 +32,11 @@ namespace osu.Game.Rulesets.Edit /// public event Action SelectionRequested; + /// + /// Invoked when this has requested drag. + /// + public event Action DragRequested; + /// /// The which this applies to. /// @@ -99,7 +104,7 @@ namespace osu.Game.Rulesets.Edit selectionRequested = true; } - return base.OnMouseDown(state, args); + return IsSelected; } protected override bool OnClick(InputState state) @@ -108,11 +113,20 @@ namespace osu.Game.Rulesets.Edit { selectionRequested = true; SelectionRequested?.Invoke(this, state); + return true; } return base.OnClick(state); } + protected override bool OnDragStart(InputState state) => true; + + protected override bool OnDrag(InputState state) + { + DragRequested?.Invoke(this, state); + return true; + } + /// /// The screen-space point that causes this to be selected. /// diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index c407f363a5..88f865be20 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -37,6 +37,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers maskContainer.MaskSelected += maskSelection.HandleSelected; maskContainer.MaskDeselected += maskSelection.HandleDeselected; maskContainer.MaskSelectionRequested += maskSelection.HandleSelectionRequested; + maskContainer.MaskDragRequested += maskSelection.HandleDrag; + maskSelection.DeselectAll = maskContainer.DeselectAll; var dragLayer = new DragLayer(maskContainer.Select); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 12b5971051..10c0b15ff8 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -29,6 +29,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// public event Action MaskSelectionRequested; + /// + /// Invoked when any requests drag. + /// + public event Action MaskDragRequested; + private IEnumerable aliveMasks => AliveInternalChildren.Cast(); public MaskContainer() @@ -43,6 +48,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers drawable.Selected += onMaskSelected; drawable.Deselected += onMaskDeselected; drawable.SelectionRequested += onSelectionRequested; + drawable.DragRequested += onDragRequested; } public override bool Remove(HitObjectMask drawable) @@ -54,6 +60,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers drawable.Selected -= onMaskSelected; drawable.Deselected -= onMaskDeselected; drawable.SelectionRequested -= onSelectionRequested; + drawable.DragRequested -= onDragRequested; } return result; @@ -82,6 +89,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask); private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask); private void onSelectionRequested(HitObjectMask mask, InputState state) => MaskSelectionRequested?.Invoke(mask, state); + private void onDragRequested(HitObjectMask mask, InputState state) => MaskDragRequested?.Invoke(mask, state); protected override int Compare(Drawable x, Drawable y) { diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 67bc5551da..64041d8ccb 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -55,22 +55,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers #region User Input Handling - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => handleInput(state); - - protected override bool OnDragStart(InputState state) => handleInput(state); - - protected override bool OnDragEnd(InputState state) => true; - - private bool handleInput(InputState state) - { - if (!selectedMasks.Any(m => m.ReceiveMouseInputAt(state.Mouse.NativeState.PositionMouseDown ?? state.Mouse.NativeState.Position))) - return false; - - UpdateVisibility(); - return true; - } - - protected override bool OnDrag(InputState state) + public void HandleDrag(HitObjectMask m, InputState state) { // Todo: Various forms of snapping @@ -83,8 +68,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers break; } } - - return true; } #endregion From 32e8d93596d95794b4b0d99e1b1ed810ceea2686 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Apr 2018 11:22:36 +0900 Subject: [PATCH 401/537] Fix selection changing when clicking overlapping hitobjects --- .../Screens/Compose/Layers/MaskContainer.cs | 19 +++++++++++++++++-- .../Screens/Compose/Layers/MaskSelection.cs | 1 - 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 10c0b15ff8..5f9d0bd96a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -86,8 +86,18 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// public void DeselectAll() => aliveMasks.ToList().ForEach(m => m.Deselect()); - private void onMaskSelected(HitObjectMask mask) => MaskSelected?.Invoke(mask); - private void onMaskDeselected(HitObjectMask mask) => MaskDeselected?.Invoke(mask); + private void onMaskSelected(HitObjectMask mask) + { + MaskSelected?.Invoke(mask); + ChangeChildDepth(mask, 1); + } + + private void onMaskDeselected(HitObjectMask mask) + { + MaskDeselected?.Invoke(mask); + ChangeChildDepth(mask, 0); + } + private void onSelectionRequested(HitObjectMask mask, InputState state) => MaskSelectionRequested?.Invoke(mask, state); private void onDragRequested(HitObjectMask mask, InputState state) => MaskDragRequested?.Invoke(mask, state); @@ -100,6 +110,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers public int Compare(HitObjectMask x, HitObjectMask y) { + // dpeth is used to denote selected status (we always want selected masks to handle input first). + int d = x.Depth.CompareTo(y.Depth); + if (d != 0) + return d; + // Put earlier hitobjects towards the end of the list, so they handle input first int i = y.HitObject.HitObject.StartTime.CompareTo(x.HitObject.HitObject.StartTime); return i == 0 ? CompareReverseChildID(x, y) : i; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 64041d8ccb..76b8027b07 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From 5426432e463dcca1bb0dfbce8b095b11ab0fb4f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Apr 2018 11:47:21 +0900 Subject: [PATCH 402/537] Fix drag select crashing --- osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 5f9d0bd96a..b631628c9e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// The rectangle to perform a selection on in screen-space coordinates. public void Select(RectangleF rect) { - foreach (var mask in aliveMasks) + foreach (var mask in aliveMasks.ToList()) { if (mask.IsPresent && rect.Contains(mask.SelectionPoint)) mask.Select(); From 714326b6066ae523ae0282992d56e76a61e71274 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Apr 2018 13:16:27 +0900 Subject: [PATCH 403/537] Fix TestCase not working with dynamic compilation --- osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 1d110477f7..9b50645b0d 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -13,7 +13,6 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Tests.Beatmaps; @@ -26,13 +25,10 @@ namespace osu.Game.Tests.Visual public override IReadOnlyList RequiredTypes => new[] { typeof(MaskSelection), + typeof(DragLayer), typeof(HitObjectComposer), typeof(OsuHitObjectComposer), typeof(HitObjectMaskLayer), - typeof(HitObjectMask), - typeof(HitCircleMask), - typeof(SliderMask), - typeof(SliderCircleMask), typeof(NotNullAttribute) }; From acbdbcc3df48a0589b0c6c82f4df5782ee6a9b79 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Apr 2018 13:17:26 +0900 Subject: [PATCH 404/537] Update AssemblyInfo in line with framework changes --- osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs | 3 +-- osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs | 3 +-- osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs | 3 +-- osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs | 3 +-- osu.Game/Properties/AssemblyInfo.cs | 11 +++++++++++ 5 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Properties/AssemblyInfo.cs diff --git a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs index 00fd8247d8..fed1013ae1 100644 --- a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs @@ -2,11 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; -using osu.Framework.Testing; // We publish our internal attributes to other sub-projects of the framework. // Note, that we omit visual tests as they are meant to test the framework // behavior "in the wild". [assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")] -[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs index c2c65433ec..515aeab9df 100644 --- a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs @@ -2,11 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; -using osu.Framework.Testing; // We publish our internal attributes to other sub-projects of the framework. // Note, that we omit visual tests as they are meant to test the framework // behavior "in the wild". [assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")] -[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs index 7532646a32..ea2c2c6729 100644 --- a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs @@ -2,11 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; -using osu.Framework.Testing; // We publish our internal attributes to other sub-projects of the framework. // Note, that we omit visual tests as they are meant to test the framework // behavior "in the wild". [assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")] -[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs index b7ed9f86b0..77218af5e1 100644 --- a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs @@ -2,11 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; -using osu.Framework.Testing; // We publish our internal attributes to other sub-projects of the framework. // Note, that we omit visual tests as they are meant to test the framework // behavior "in the wild". [assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")] -[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Dynamic")] diff --git a/osu.Game/Properties/AssemblyInfo.cs b/osu.Game/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..9384740308 --- /dev/null +++ b/osu.Game/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Runtime.CompilerServices; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Tests")] +[assembly: InternalsVisibleTo("osu.Game.Tests.Dynamic")] From 768a5e5383d1855fc54cf546c9959a63f49faae1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Apr 2018 15:20:09 +0900 Subject: [PATCH 405/537] Create ManualInputManagerTestCase A base class for running more input-driven tests. --- osu.Game.Tests/Visual/TestCaseCursors.cs | 129 ++++++++---------- .../Visual/ManualInputManagerTestCase.cs | 29 ++++ 2 files changed, 86 insertions(+), 72 deletions(-) create mode 100644 osu.Game/Tests/Visual/ManualInputManagerTestCase.cs diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs index 72e699c54b..4f4fdbeb5b 100644 --- a/osu.Game.Tests/Visual/TestCaseCursors.cs +++ b/osu.Game.Tests/Visual/TestCaseCursors.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.MathUtils; -using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; using OpenTK; @@ -18,88 +17,74 @@ using OpenTK.Graphics; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseCursors : OsuTestCase + public class TestCaseCursors : ManualInputManagerTestCase { - private readonly ManualInputManager inputManager; private readonly CursorOverrideContainer cursorOverrideContainer; private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; public TestCaseCursors() { - Child = inputManager = new ManualInputManager + Child = cursorOverrideContainer = new CursorOverrideContainer { - Child = cursorOverrideContainer = new CursorOverrideContainer + RelativeSizeAxes = Axes.Both, + Children = new[] { - RelativeSizeAxes = Axes.Both, - Children = new[] + // Middle user + cursorBoxes[0] = new CustomCursorBox(Color4.Green) { - // Middle user - cursorBoxes[0] = new CustomCursorBox(Color4.Green) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - }, - // Top-left user - cursorBoxes[1] = new CustomCursorBox(Color4.Blue) - { - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Bottom-right user - cursorBoxes[2] = new CustomCursorBox(Color4.Red) - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Bottom-left local - cursorBoxes[3] = new CustomCursorBox(Color4.Magenta, false) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Top-right local - cursorBoxes[4] = new CustomCursorBox(Color4.Cyan, false) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Left-local - cursorBoxes[5] = new CustomCursorBox(Color4.Yellow, false) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.2f, 1), - }, - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + // Top-left user + cursorBoxes[1] = new CustomCursorBox(Color4.Blue) + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-right user + cursorBoxes[2] = new CustomCursorBox(Color4.Red) + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-left local + cursorBoxes[3] = new CustomCursorBox(Color4.Magenta, false) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Top-right local + cursorBoxes[4] = new CustomCursorBox(Color4.Cyan, false) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Left-local + cursorBoxes[5] = new CustomCursorBox(Color4.Yellow, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.2f, 1), + }, } }; - returnUserInput(); - AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b)); testUserCursor(); testLocalCursor(); testUserCursorOverride(); testMultipleLocalCursors(); - returnUserInput(); - } - - /// - /// Returns input back to the user. - /// - private void returnUserInput() - { - AddStep("Return user input", () => inputManager.UseParentState = true); + ReturnUserInput(); } /// @@ -109,7 +94,7 @@ namespace osu.Game.Tests.Visual /// private void testUserCursor() { - AddStep("Move to green area", () => inputManager.MoveMouseTo(cursorBoxes[0])); + AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); AddStep("Move out", moveOut); @@ -124,7 +109,7 @@ namespace osu.Game.Tests.Visual /// private void testLocalCursor() { - AddStep("Move to purple area", () => inputManager.MoveMouseTo(cursorBoxes[3])); + AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3])); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); @@ -141,7 +126,7 @@ namespace osu.Game.Tests.Visual /// private void testUserCursorOverride() { - AddStep("Move to blue-green boundary", () => inputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddStep("Move to blue-green boundary", () => InputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); @@ -156,7 +141,7 @@ namespace osu.Game.Tests.Visual /// private void testMultipleLocalCursors() { - AddStep("Move to yellow-purple boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddStep("Move to yellow-purple boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); @@ -172,7 +157,7 @@ namespace osu.Game.Tests.Visual /// private void testUserOverrideWithLocal() { - AddStep("Move to yellow-blue boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); + AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); @@ -186,7 +171,7 @@ namespace osu.Game.Tests.Visual /// Moves the cursor to a point not covered by any cursor containers. /// private void moveOut() - => inputManager.MoveMouseTo(new Vector2(inputManager.ScreenSpaceDrawQuad.Centre.X, inputManager.ScreenSpaceDrawQuad.TopLeft.Y)); + => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.Centre.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y)); /// /// Checks if a cursor is visible. @@ -199,7 +184,7 @@ namespace osu.Game.Tests.Visual /// /// The cursor to check. private bool checkAtMouse(CursorContainer cursorContainer) - => Precision.AlmostEquals(inputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); + => Precision.AlmostEquals(InputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); private class CustomCursorBox : Container, IProvideCursor { diff --git a/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs b/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs new file mode 100644 index 0000000000..c2595231c9 --- /dev/null +++ b/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing.Input; + +namespace osu.Game.Tests.Visual +{ + public abstract class ManualInputManagerTestCase : OsuTestCase + { + protected override Container Content => InputManager; + protected readonly ManualInputManager InputManager; + + protected ManualInputManagerTestCase() + { + base.Content.Add(InputManager = new ManualInputManager()); + ReturnUserInput(); + } + + /// + /// Returns input back to the user. + /// + protected void ReturnUserInput() + { + AddStep("Return user input", () => InputManager.UseParentState = true); + } + } +} From c7abd56fc4c2b055c0255058a882df339bd33d2b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Apr 2018 17:40:06 +0900 Subject: [PATCH 406/537] Give editor a custom clock to handle seeking --- .../Visual/TestCaseEditorSeekSnapping.cs | 203 ++++++++---------- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 108 +--------- .../Timelines/Summary/Parts/MarkerPart.cs | 4 +- .../Timelines/Summary/SummaryTimeline.cs | 9 +- osu.Game/Screens/Edit/Editor.cs | 25 ++- osu.Game/Screens/Edit/EditorClock.cs | 107 +++++++++ 6 files changed, 227 insertions(+), 229 deletions(-) create mode 100644 osu.Game/Screens/Edit/EditorClock.cs diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index e9e966a826..b70519a7bf 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -3,23 +3,20 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; +using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Screens.Compose; -using osu.Game.Tests.Beatmaps; using OpenTK; using OpenTK.Graphics; @@ -29,26 +26,13 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { typeof(HitObjectComposer) }; - private Track track; - private HitObjectComposer composer; - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(4); - private DecoupleableInterpolatingFramedClock clock; - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); + private EditorClock clock; [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) + private void load() { - clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - dependencies.CacheAs(clock); - dependencies.CacheAs(clock); - dependencies.Cache(beatDivisor); - var testBeatmap = new Beatmap { ControlPointInfo = new ControlPointInfo @@ -70,23 +54,9 @@ namespace osu.Game.Tests.Visual } }; - osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap); - track = osuGame.Beatmap.Value.Track; + clock = new EditorClock(testBeatmap.ControlPointInfo, beatDivisor) { IsCoupled = false }; - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset()) }, - new Drawable[] { new TimingPointVisualiser(testBeatmap, track) { Clock = clock } }, - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.AutoSize), - } - }; + Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = clock }; testSeekNoSnapping(); testSeekSnappingOnBeat(); @@ -99,6 +69,15 @@ namespace osu.Game.Tests.Visual testSeekingWithFloatingPointBeatLength(); } + protected override bool OnWheel(InputState state) + { + if (state.Mouse.WheelDelta > 0) + clock.SeekBackward(true); + else + clock.SeekForward(true); + return true; + } + /// /// Tests whether time is correctly seeked without snapping. /// @@ -107,17 +86,17 @@ namespace osu.Game.Tests.Visual reset(); // Forwards - AddStep("Seek(0)", () => composer.SeekTo(0)); + AddStep("Seek(0)", () => clock.Seek(0)); AddAssert("Time = 0", () => clock.CurrentTime == 0); - AddStep("Seek(33)", () => composer.SeekTo(33)); + AddStep("Seek(33)", () => clock.Seek(33)); AddAssert("Time = 33", () => clock.CurrentTime == 33); - AddStep("Seek(89)", () => composer.SeekTo(89)); + AddStep("Seek(89)", () => clock.Seek(89)); AddAssert("Time = 89", () => clock.CurrentTime == 89); // Backwards - AddStep("Seek(25)", () => composer.SeekTo(25)); + AddStep("Seek(25)", () => clock.Seek(25)); AddAssert("Time = 25", () => clock.CurrentTime == 25); - AddStep("Seek(0)", () => composer.SeekTo(0)); + AddStep("Seek(0)", () => clock.Seek(0)); AddAssert("Time = 0", () => clock.CurrentTime == 0); } @@ -129,19 +108,19 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(0), Snap", () => composer.SeekTo(0, true)); + AddStep("Seek(0), Snap", () => clock.Seek(0, true)); AddAssert("Time = 0", () => clock.CurrentTime == 0); - AddStep("Seek(50), Snap", () => composer.SeekTo(50, true)); + AddStep("Seek(50), Snap", () => clock.Seek(50, true)); AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("Seek(100), Snap", () => composer.SeekTo(100, true)); + AddStep("Seek(100), Snap", () => clock.Seek(100, true)); AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("Seek(175), Snap", () => composer.SeekTo(175, true)); + AddStep("Seek(175), Snap", () => clock.Seek(175, true)); AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("Seek(350), Snap", () => composer.SeekTo(350, true)); + AddStep("Seek(350), Snap", () => clock.Seek(350, true)); AddAssert("Time = 350", () => clock.CurrentTime == 350); - AddStep("Seek(400), Snap", () => composer.SeekTo(400, true)); + AddStep("Seek(400), Snap", () => clock.Seek(400, true)); AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("Seek(450), Snap", () => composer.SeekTo(450, true)); + AddStep("Seek(450), Snap", () => clock.Seek(450, true)); AddAssert("Time = 450", () => clock.CurrentTime == 450); } @@ -154,17 +133,17 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(24), Snap", () => composer.SeekTo(24, true)); + AddStep("Seek(24), Snap", () => clock.Seek(24, true)); AddAssert("Time = 0", () => clock.CurrentTime == 0); - AddStep("Seek(26), Snap", () => composer.SeekTo(26, true)); + AddStep("Seek(26), Snap", () => clock.Seek(26, true)); AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("Seek(150), Snap", () => composer.SeekTo(150, true)); + AddStep("Seek(150), Snap", () => clock.Seek(150, true)); AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("Seek(170), Snap", () => composer.SeekTo(170, true)); + AddStep("Seek(170), Snap", () => clock.Seek(170, true)); AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("Seek(274), Snap", () => composer.SeekTo(274, true)); + AddStep("Seek(274), Snap", () => clock.Seek(274, true)); AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("Seek(276), Snap", () => composer.SeekTo(276, true)); + AddStep("Seek(276), Snap", () => clock.Seek(276, true)); AddAssert("Time = 350", () => clock.CurrentTime == 350); } @@ -175,15 +154,15 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("SeekForward", () => composer.SeekForward()); + AddStep("SeekForward", () => clock.SeekForward()); AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("SeekForward", () => composer.SeekForward()); + AddStep("SeekForward", () => clock.SeekForward()); AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("SeekForward", () => composer.SeekForward()); + AddStep("SeekForward", () => clock.SeekForward()); AddAssert("Time = 200", () => clock.CurrentTime == 200); - AddStep("SeekForward", () => composer.SeekForward()); + AddStep("SeekForward", () => clock.SeekForward()); AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("SeekForward", () => composer.SeekForward()); + AddStep("SeekForward", () => clock.SeekForward()); AddAssert("Time = 450", () => clock.CurrentTime == 450); } @@ -194,17 +173,17 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 350", () => clock.CurrentTime == 350); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 450", () => clock.CurrentTime == 450); } @@ -216,29 +195,29 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(49)", () => composer.SeekTo(49)); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("Seek(49)", () => clock.Seek(49)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("Seek(49.999)", () => composer.SeekTo(49.999)); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("Seek(49.999)", () => clock.Seek(49.999)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("Seek(99)", () => composer.SeekTo(99)); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("Seek(99)", () => clock.Seek(99)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("Seek(99.999)", () => composer.SeekTo(99.999)); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("Seek(99.999)", () => clock.Seek(99.999)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("Seek(174)", () => composer.SeekTo(174)); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("Seek(174)", () => clock.Seek(174)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("Seek(349)", () => composer.SeekTo(349)); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("Seek(349)", () => clock.Seek(349)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 350", () => clock.CurrentTime == 350); - AddStep("Seek(399)", () => composer.SeekTo(399)); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("Seek(399)", () => clock.Seek(399)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("Seek(449)", () => composer.SeekTo(449)); - AddStep("SeekForward, Snap", () => composer.SeekForward(true)); + AddStep("Seek(449)", () => clock.Seek(449)); + AddStep("SeekForward, Snap", () => clock.SeekForward(true)); AddAssert("Time = 450", () => clock.CurrentTime == 450); } @@ -249,18 +228,18 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(450)", () => composer.SeekTo(450)); - AddStep("SeekBackward", () => composer.SeekBackward()); + AddStep("Seek(450)", () => clock.Seek(450)); + AddStep("SeekBackward", () => clock.SeekBackward()); AddAssert("Time = 425", () => clock.CurrentTime == 425); - AddStep("SeekBackward", () => composer.SeekBackward()); + AddStep("SeekBackward", () => clock.SeekBackward()); AddAssert("Time = 375", () => clock.CurrentTime == 375); - AddStep("SeekBackward", () => composer.SeekBackward()); + AddStep("SeekBackward", () => clock.SeekBackward()); AddAssert("Time = 325", () => clock.CurrentTime == 325); - AddStep("SeekBackward", () => composer.SeekBackward()); + AddStep("SeekBackward", () => clock.SeekBackward()); AddAssert("Time = 125", () => clock.CurrentTime == 125); - AddStep("SeekBackward", () => composer.SeekBackward()); + AddStep("SeekBackward", () => clock.SeekBackward()); AddAssert("Time = 25", () => clock.CurrentTime == 25); - AddStep("SeekBackward", () => composer.SeekBackward()); + AddStep("SeekBackward", () => clock.SeekBackward()); AddAssert("Time = 0", () => clock.CurrentTime == 0); } @@ -271,18 +250,18 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(450)", () => composer.SeekTo(450)); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("Seek(450)", () => clock.Seek(450)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 350", () => clock.CurrentTime == 350); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 0", () => clock.CurrentTime == 0); } @@ -294,17 +273,17 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(451)", () => composer.SeekTo(451)); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("Seek(451)", () => clock.Seek(451)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 450", () => clock.CurrentTime == 450); - AddStep("Seek(450.999)", () => composer.SeekTo(450.999)); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("Seek(450.999)", () => clock.Seek(450.999)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 450", () => clock.CurrentTime == 450); - AddStep("Seek(401)", () => composer.SeekTo(401)); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("Seek(401)", () => clock.Seek(401)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("Seek(401.999)", () => composer.SeekTo(401.999)); - AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); + AddStep("Seek(401.999)", () => clock.Seek(401.999)); + AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); AddAssert("Time = 400", () => clock.CurrentTime == 400); } @@ -317,14 +296,14 @@ namespace osu.Game.Tests.Visual double lastTime = 0; - AddStep("Seek(0)", () => composer.SeekTo(0)); + AddStep("Seek(0)", () => clock.Seek(0)); for (int i = 0; i < 20; i++) { AddStep("SeekForward, Snap", () => { lastTime = clock.CurrentTime; - composer.SeekForward(true); + clock.SeekForward(true); }); AddAssert("Time > lastTime", () => clock.CurrentTime > lastTime); } @@ -334,7 +313,7 @@ namespace osu.Game.Tests.Visual AddStep("SeekBackward, Snap", () => { lastTime = clock.CurrentTime; - composer.SeekBackward(true); + clock.SeekBackward(true); }); AddAssert("Time < lastTime", () => clock.CurrentTime < lastTime); } @@ -344,7 +323,7 @@ namespace osu.Game.Tests.Visual private void reset() { - AddStep("Reset", () => composer.SeekTo(0)); + AddStep("Reset", () => clock.Seek(0)); } private class TestHitObjectComposer : HitObjectComposer @@ -359,13 +338,13 @@ namespace osu.Game.Tests.Visual private class TimingPointVisualiser : CompositeDrawable { - private readonly Track track; + private readonly double length; private readonly Drawable tracker; - public TimingPointVisualiser(Beatmap beatmap, Track track) + public TimingPointVisualiser(Beatmap beatmap, double length) { - this.track = track; + this.length = length; Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -417,7 +396,7 @@ namespace osu.Game.Tests.Visual for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint next = i == timingPoints.Count - 1 ? null : timingPoints[i + 1]; - timelineContainer.Add(new TimingPointTimeline(timingPoints[i], next?.Time ?? beatmap.HitObjects.Last().StartTime, track.Length)); + timelineContainer.Add(new TimingPointTimeline(timingPoints[i], next?.Time ?? length, length)); } } @@ -425,7 +404,7 @@ namespace osu.Game.Tests.Visual { base.Update(); - tracker.X = (float)(Time.Current / track.Length); + tracker.X = (float)(Time.Current / length); } private class TimingPointTimeline : CompositeDrawable diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 24a0a7e643..79ab67fafd 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -9,9 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input; using osu.Framework.Logging; -using osu.Framework.MathUtils; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Tools; @@ -35,8 +33,6 @@ namespace osu.Game.Rulesets.Edit private readonly Bindable beatmap = new Bindable(); private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - private IAdjustableClock adjustableClock; - protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; @@ -45,10 +41,8 @@ namespace osu.Game.Rulesets.Edit } [BackgroundDependencyLoader(true)] - private void load([NotNull] OsuGameBase osuGame, [NotNull] IAdjustableClock adjustableClock, [NotNull] IFrameBasedClock framedClock, [CanBeNull] BindableBeatDivisor beatDivisor) + private void load([NotNull] OsuGameBase osuGame, [NotNull] IFrameBasedClock framedClock, [CanBeNull] BindableBeatDivisor beatDivisor) { - this.adjustableClock = adjustableClock; - if (beatDivisor != null) this.beatDivisor.BindTo(beatDivisor); @@ -148,106 +142,6 @@ namespace osu.Game.Rulesets.Edit }); } - protected override bool OnWheel(InputState state) - { - if (state.Mouse.WheelDelta > 0) - SeekBackward(true); - else - SeekForward(true); - return true; - } - - /// - /// Seeks the current time one beat-snapped beat-length backwards. - /// - /// Whether to snap to the closest beat. - public void SeekBackward(bool snapped = false) => seek(-1, snapped); - - /// - /// Seeks the current time one beat-snapped beat-length forwards. - /// - /// Whether to snap to the closest beat. - public void SeekForward(bool snapped = false) => seek(1, snapped); - - private void seek(int direction, bool snapped) - { - var cpi = beatmap.Value.Beatmap.ControlPointInfo; - - var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime); - if (direction < 0 && timingPoint.Time == adjustableClock.CurrentTime) - { - // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into - int activeIndex = cpi.TimingPoints.IndexOf(timingPoint); - while (activeIndex > 0 && adjustableClock.CurrentTime == timingPoint.Time) - timingPoint = cpi.TimingPoints[--activeIndex]; - } - - double seekAmount = timingPoint.BeatLength / beatDivisor; - double seekTime = adjustableClock.CurrentTime + seekAmount * direction; - - if (!snapped || cpi.TimingPoints.Count == 0) - { - adjustableClock.Seek(seekTime); - return; - } - - // We will be snapping to beats within timingPoint - seekTime -= timingPoint.Time; - - // Determine the index from timingPoint of the closest beat to seekTime, accounting for scrolling direction - int closestBeat; - if (direction > 0) - closestBeat = (int)Math.Floor(seekTime / seekAmount); - else - closestBeat = (int)Math.Ceiling(seekTime / seekAmount); - - seekTime = timingPoint.Time + closestBeat * seekAmount; - - // Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this. - // Instead, we'll go to the next beat in the direction when this is the case - if (Precision.AlmostEquals(adjustableClock.CurrentTime, seekTime)) - { - closestBeat += direction > 0 ? 1 : -1; - seekTime = timingPoint.Time + closestBeat * seekAmount; - } - - if (seekTime < timingPoint.Time && timingPoint != cpi.TimingPoints.First()) - seekTime = timingPoint.Time; - - var nextTimingPoint = cpi.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); - if (seekTime > nextTimingPoint?.Time) - seekTime = nextTimingPoint.Time; - - adjustableClock.Seek(seekTime); - } - - public void SeekTo(double seekTime, bool snapped = false) - { - if (!snapped) - { - adjustableClock.Seek(seekTime); - return; - } - - var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime); - double beatSnapLength = timingPoint.BeatLength / beatDivisor; - - // We will be snapping to beats within the timing point - seekTime -= timingPoint.Time; - - // Determine the index from the current timing point of the closest beat to seekTime - int closestBeat = (int)Math.Round(seekTime / beatSnapLength); - seekTime = timingPoint.Time + closestBeat * beatSnapLength; - - // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to - // the next timing point's start time - var nextTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); - if (seekTime > nextTimingPoint?.Time) - seekTime = nextTimingPoint.Time; - - adjustableClock.Seek(seekTime); - } - private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); 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 b249713581..9efe93c5a7 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -55,11 +55,9 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts if (Beatmap.Value.Track.Length == double.PositiveInfinity) return; float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); - seekTo(markerPos / DrawWidth * Beatmap.Value.Track.Length); + adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length); } - private void seekTo(double time) => adjustableClock.Seek(time); - protected override void Update() { base.Update(); diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index 0e80c13257..b368b92e42 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -17,8 +17,15 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary /// public class SummaryTimeline : BottomBarContainer { + private readonly IAdjustableClock adjustableClock; + + public SummaryTimeline(IAdjustableClock adjustableClock) + { + this.adjustableClock = adjustableClock; + } + [BackgroundDependencyLoader] - private void load(OsuColour colours, IAdjustableClock adjustableClock) + private void load(OsuColour colours) { TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 8b651000fd..8a8932970c 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -12,6 +12,7 @@ using osu.Game.Screens.Edit.Menus; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Framework.Allocation; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; using osu.Framework.Timing; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Edit.Screens; @@ -32,6 +33,10 @@ namespace osu.Game.Screens.Edit private EditorScreen currentScreen; + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); + + private EditorClock clock; + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) @@ -42,11 +47,11 @@ namespace osu.Game.Screens.Edit { // TODO: should probably be done at a RulesetContainer level to share logic with Player. var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); - var adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - adjustableClock.ChangeSource(sourceClock); + clock = new EditorClock(Beatmap.Value.Beatmap.ControlPointInfo, beatDivisor) { IsCoupled = false }; + clock.ChangeSource(sourceClock); - dependencies.CacheAs(adjustableClock); - dependencies.CacheAs(adjustableClock); + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); EditorMenuBar menuBar; TimeInfoContainer timeInfo; @@ -123,7 +128,7 @@ namespace osu.Game.Screens.Edit Padding = new MarginPadding { Right = 10 }, Child = timeInfo = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, }, - timeline = new SummaryTimeline + timeline = new SummaryTimeline(clock) { RelativeSizeAxes = Axes.Both, }, @@ -147,7 +152,6 @@ namespace osu.Game.Screens.Edit menuBar.Mode.ValueChanged += onModeChanged; bottomBackground.Colour = colours.Gray2; - } private void exportBeatmap() @@ -176,6 +180,15 @@ namespace osu.Game.Screens.Edit screenContainer.Add(currentScreen); } + protected override bool OnWheel(InputState state) + { + if (state.Mouse.WheelDelta > 0) + clock.SeekBackward(true); + else + clock.SeekForward(true); + return true; + } + protected override void OnResuming(Screen last) { Beatmap.Value.Track?.Stop(); diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs new file mode 100644 index 0000000000..ddde819757 --- /dev/null +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -0,0 +1,107 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Screens.Edit.Screens.Compose; + +namespace osu.Game.Screens.Edit +{ + public class EditorClock : DecoupleableInterpolatingFramedClock + { + private readonly ControlPointInfo controlPointInfo; + private readonly BindableBeatDivisor beatDivisor; + + public EditorClock(ControlPointInfo controlPointInfo, BindableBeatDivisor beatDivisor) + { + this.controlPointInfo = controlPointInfo; + this.beatDivisor = beatDivisor; + } + + public bool SeekSnapped(double position) + { + var timingPoint = controlPointInfo.TimingPointAt(position); + double beatSnapLength = timingPoint.BeatLength / beatDivisor; + + // We will be snapping to beats within the timing point + position -= timingPoint.Time; + + // Determine the index from the current timing point of the closest beat to position + int closestBeat = (int)Math.Round(position / beatSnapLength); + position = timingPoint.Time + closestBeat * beatSnapLength; + + // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to + // the next timing point's start time + var nextTimingPoint = controlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + if (position > nextTimingPoint?.Time) + position = nextTimingPoint.Time; + + return Seek(position); + } + + /// + /// Seeks the current time one beat-snapped beat-length backwards. + /// + /// Whether to snap to the closest beat. + public void SeekBackward(bool snapped = false) => seek(-1, snapped); + + /// + /// Seeks the current time one beat-snapped beat-length forwards. + /// + /// Whether to snap to the closest beat. + public void SeekForward(bool snapped = false) => seek(1, snapped); + + private void seek(int direction, bool snapped) + { + var timingPoint = controlPointInfo.TimingPointAt(CurrentTime); + if (direction < 0 && timingPoint.Time == CurrentTime) + { + // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into + int activeIndex = controlPointInfo.TimingPoints.IndexOf(timingPoint); + while (activeIndex > 0 && CurrentTime == timingPoint.Time) + timingPoint = controlPointInfo.TimingPoints[--activeIndex]; + } + + double seekAmount = timingPoint.BeatLength / beatDivisor; + double seekTime = CurrentTime + seekAmount * direction; + + if (!snapped || controlPointInfo.TimingPoints.Count == 0) + { + Seek(seekTime); + return; + } + + // We will be snapping to beats within timingPoint + seekTime -= timingPoint.Time; + + // Determine the index from timingPoint of the closest beat to seekTime, accounting for scrolling direction + int closestBeat; + if (direction > 0) + closestBeat = (int)Math.Floor(seekTime / seekAmount); + else + closestBeat = (int)Math.Ceiling(seekTime / seekAmount); + + seekTime = timingPoint.Time + closestBeat * seekAmount; + + // Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this. + // Instead, we'll go to the next beat in the direction when this is the case + if (Precision.AlmostEquals(CurrentTime, seekTime)) + { + closestBeat += direction > 0 ? 1 : -1; + seekTime = timingPoint.Time + closestBeat * seekAmount; + } + + if (seekTime < timingPoint.Time && timingPoint != controlPointInfo.TimingPoints.First()) + seekTime = timingPoint.Time; + + var nextTimingPoint = controlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + if (seekTime > nextTimingPoint?.Time) + seekTime = nextTimingPoint.Time; + + Seek(seekTime); + } + } +} From fdb3227fd7448cd119e04c3b3f2a411bacf1111e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Apr 2018 18:12:44 +0900 Subject: [PATCH 407/537] Fix + cleanup testcases --- .../Visual/TestCaseEditorCompose.cs | 12 +- .../Visual/TestCaseEditorSeekSnapping.cs | 286 +++++++++--------- .../Visual/TestCaseEditorSelectionLayer.cs | 12 +- .../Visual/TestCaseEditorSummaryTimeline.cs | 24 +- osu.Game/Tests/Visual/EditorClockTestCase.cs | 62 ++++ 5 files changed, 207 insertions(+), 189 deletions(-) create mode 100644 osu.Game/Tests/Visual/EditorClockTestCase.cs diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index cd25bc1683..8cc7a01acb 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Timing; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Tests.Beatmaps; @@ -13,24 +12,15 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseEditorCompose : OsuTestCase + public class TestCaseEditorCompose : EditorClockTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(Compose) }; - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); - [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); - var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - dependencies.CacheAs(clock); - dependencies.CacheAs(clock); - var compose = new Compose(); compose.Beatmap.BindTo(osuGame.Beatmap); diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index b70519a7bf..62c02ee5aa 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -8,30 +8,29 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Edit; -using osu.Game.Screens.Edit.Screens.Compose; +using osu.Game.Tests.Beatmaps; using OpenTK; using OpenTK.Graphics; namespace osu.Game.Tests.Visual { - public class TestCaseEditorSeekSnapping : OsuTestCase + public class TestCaseEditorSeekSnapping : EditorClockTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(HitObjectComposer) }; - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(4); - - private EditorClock clock; + public TestCaseEditorSeekSnapping() + { + BeatDivisor.Value = 4; + } [BackgroundDependencyLoader] - private void load() + private void load(OsuGameBase osuGame) { var testBeatmap = new Beatmap { @@ -54,9 +53,9 @@ namespace osu.Game.Tests.Visual } }; - clock = new EditorClock(testBeatmap.ControlPointInfo, beatDivisor) { IsCoupled = false }; + osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap); - Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = clock }; + Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock }; testSeekNoSnapping(); testSeekSnappingOnBeat(); @@ -69,15 +68,6 @@ namespace osu.Game.Tests.Visual testSeekingWithFloatingPointBeatLength(); } - protected override bool OnWheel(InputState state) - { - if (state.Mouse.WheelDelta > 0) - clock.SeekBackward(true); - else - clock.SeekForward(true); - return true; - } - /// /// Tests whether time is correctly seeked without snapping. /// @@ -86,18 +76,18 @@ namespace osu.Game.Tests.Visual reset(); // Forwards - AddStep("Seek(0)", () => clock.Seek(0)); - AddAssert("Time = 0", () => clock.CurrentTime == 0); - AddStep("Seek(33)", () => clock.Seek(33)); - AddAssert("Time = 33", () => clock.CurrentTime == 33); - AddStep("Seek(89)", () => clock.Seek(89)); - AddAssert("Time = 89", () => clock.CurrentTime == 89); + AddStep("Seek(0)", () => Clock.Seek(0)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + AddStep("Seek(33)", () => Clock.Seek(33)); + AddAssert("Time = 33", () => Clock.CurrentTime == 33); + AddStep("Seek(89)", () => Clock.Seek(89)); + AddAssert("Time = 89", () => Clock.CurrentTime == 89); // Backwards - AddStep("Seek(25)", () => clock.Seek(25)); - AddAssert("Time = 25", () => clock.CurrentTime == 25); - AddStep("Seek(0)", () => clock.Seek(0)); - AddAssert("Time = 0", () => clock.CurrentTime == 0); + AddStep("Seek(25)", () => Clock.Seek(25)); + AddAssert("Time = 25", () => Clock.CurrentTime == 25); + AddStep("Seek(0)", () => Clock.Seek(0)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); } /// @@ -108,20 +98,20 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(0), Snap", () => clock.Seek(0, true)); - AddAssert("Time = 0", () => clock.CurrentTime == 0); - AddStep("Seek(50), Snap", () => clock.Seek(50, true)); - AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("Seek(100), Snap", () => clock.Seek(100, true)); - AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("Seek(175), Snap", () => clock.Seek(175, true)); - AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("Seek(350), Snap", () => clock.Seek(350, true)); - AddAssert("Time = 350", () => clock.CurrentTime == 350); - AddStep("Seek(400), Snap", () => clock.Seek(400, true)); - AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("Seek(450), Snap", () => clock.Seek(450, true)); - AddAssert("Time = 450", () => clock.CurrentTime == 450); + AddStep("Seek(0), Snap", () => Clock.SeekSnapped(0)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + AddStep("Seek(50), Snap", () => Clock.SeekSnapped(50)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("Seek(100), Snap", () => Clock.SeekSnapped(100)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("Seek(175), Snap", () => Clock.SeekSnapped(175)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("Seek(350), Snap", () => Clock.SeekSnapped(350)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + AddStep("Seek(400), Snap", () => Clock.SeekSnapped(400)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("Seek(450), Snap", () => Clock.SeekSnapped(450)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); } /// @@ -133,18 +123,18 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(24), Snap", () => clock.Seek(24, true)); - AddAssert("Time = 0", () => clock.CurrentTime == 0); - AddStep("Seek(26), Snap", () => clock.Seek(26, true)); - AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("Seek(150), Snap", () => clock.Seek(150, true)); - AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("Seek(170), Snap", () => clock.Seek(170, true)); - AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("Seek(274), Snap", () => clock.Seek(274, true)); - AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("Seek(276), Snap", () => clock.Seek(276, true)); - AddAssert("Time = 350", () => clock.CurrentTime == 350); + AddStep("Seek(24), Snap", () => Clock.SeekSnapped(24)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + AddStep("Seek(26), Snap", () => Clock.SeekSnapped(26)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("Seek(150), Snap", () => Clock.SeekSnapped(150)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("Seek(170), Snap", () => Clock.SeekSnapped(170)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("Seek(274), Snap", () => Clock.SeekSnapped(274)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("Seek(276), Snap", () => Clock.SeekSnapped(276)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); } /// @@ -154,16 +144,16 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("SeekForward", () => clock.SeekForward()); - AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("SeekForward", () => clock.SeekForward()); - AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("SeekForward", () => clock.SeekForward()); - AddAssert("Time = 200", () => clock.CurrentTime == 200); - AddStep("SeekForward", () => clock.SeekForward()); - AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("SeekForward", () => clock.SeekForward()); - AddAssert("Time = 450", () => clock.CurrentTime == 450); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 200", () => Clock.CurrentTime == 200); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); } /// @@ -173,18 +163,18 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 350", () => clock.CurrentTime == 350); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 450", () => clock.CurrentTime == 450); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); } /// @@ -195,30 +185,30 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(49)", () => clock.Seek(49)); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("Seek(49.999)", () => clock.Seek(49.999)); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("Seek(99)", () => clock.Seek(99)); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("Seek(99.999)", () => clock.Seek(99.999)); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("Seek(174)", () => clock.Seek(174)); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("Seek(349)", () => clock.Seek(349)); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 350", () => clock.CurrentTime == 350); - AddStep("Seek(399)", () => clock.Seek(399)); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("Seek(449)", () => clock.Seek(449)); - AddStep("SeekForward, Snap", () => clock.SeekForward(true)); - AddAssert("Time = 450", () => clock.CurrentTime == 450); + AddStep("Seek(49)", () => Clock.Seek(49)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("Seek(49.999)", () => Clock.Seek(49.999)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("Seek(99)", () => Clock.Seek(99)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("Seek(99.999)", () => Clock.Seek(99.999)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("Seek(174)", () => Clock.Seek(174)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("Seek(349)", () => Clock.Seek(349)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + AddStep("Seek(399)", () => Clock.Seek(399)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("Seek(449)", () => Clock.Seek(449)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); } /// @@ -228,19 +218,19 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(450)", () => clock.Seek(450)); - AddStep("SeekBackward", () => clock.SeekBackward()); - AddAssert("Time = 425", () => clock.CurrentTime == 425); - AddStep("SeekBackward", () => clock.SeekBackward()); - AddAssert("Time = 375", () => clock.CurrentTime == 375); - AddStep("SeekBackward", () => clock.SeekBackward()); - AddAssert("Time = 325", () => clock.CurrentTime == 325); - AddStep("SeekBackward", () => clock.SeekBackward()); - AddAssert("Time = 125", () => clock.CurrentTime == 125); - AddStep("SeekBackward", () => clock.SeekBackward()); - AddAssert("Time = 25", () => clock.CurrentTime == 25); - AddStep("SeekBackward", () => clock.SeekBackward()); - AddAssert("Time = 0", () => clock.CurrentTime == 0); + AddStep("Seek(450)", () => Clock.Seek(450)); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 425", () => Clock.CurrentTime == 425); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 375", () => Clock.CurrentTime == 375); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 325", () => Clock.CurrentTime == 325); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 125", () => Clock.CurrentTime == 125); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 25", () => Clock.CurrentTime == 25); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); } /// @@ -250,19 +240,19 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(450)", () => clock.Seek(450)); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 350", () => clock.CurrentTime == 350); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 175", () => clock.CurrentTime == 175); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 100", () => clock.CurrentTime == 100); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 50", () => clock.CurrentTime == 50); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 0", () => clock.CurrentTime == 0); + AddStep("Seek(450)", () => Clock.Seek(450)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); } /// @@ -273,18 +263,18 @@ namespace osu.Game.Tests.Visual { reset(); - AddStep("Seek(451)", () => clock.Seek(451)); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 450", () => clock.CurrentTime == 450); - AddStep("Seek(450.999)", () => clock.Seek(450.999)); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 450", () => clock.CurrentTime == 450); - AddStep("Seek(401)", () => clock.Seek(401)); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 400", () => clock.CurrentTime == 400); - AddStep("Seek(401.999)", () => clock.Seek(401.999)); - AddStep("SeekBackward, Snap", () => clock.SeekBackward(true)); - AddAssert("Time = 400", () => clock.CurrentTime == 400); + AddStep("Seek(451)", () => Clock.Seek(451)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); + AddStep("Seek(450.999)", () => Clock.Seek(450.999)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); + AddStep("Seek(401)", () => Clock.Seek(401)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("Seek(401.999)", () => Clock.Seek(401.999)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); } /// @@ -296,34 +286,34 @@ namespace osu.Game.Tests.Visual double lastTime = 0; - AddStep("Seek(0)", () => clock.Seek(0)); + AddStep("Seek(0)", () => Clock.Seek(0)); for (int i = 0; i < 20; i++) { AddStep("SeekForward, Snap", () => { - lastTime = clock.CurrentTime; - clock.SeekForward(true); + lastTime = Clock.CurrentTime; + Clock.SeekForward(true); }); - AddAssert("Time > lastTime", () => clock.CurrentTime > lastTime); + AddAssert("Time > lastTime", () => Clock.CurrentTime > lastTime); } for (int i = 0; i < 20; i++) { AddStep("SeekBackward, Snap", () => { - lastTime = clock.CurrentTime; - clock.SeekBackward(true); + lastTime = Clock.CurrentTime; + Clock.SeekBackward(true); }); - AddAssert("Time < lastTime", () => clock.CurrentTime < lastTime); + AddAssert("Time < lastTime", () => Clock.CurrentTime < lastTime); } - AddAssert("Time = 0", () => clock.CurrentTime == 0); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); } private void reset() { - AddStep("Reset", () => clock.Seek(0)); + AddStep("Reset", () => Clock.Seek(0)); } private class TestHitObjectComposer : HitObjectComposer diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index bbbfef477a..24f5905131 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Timing; using OpenTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; @@ -20,7 +19,7 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseEditorSelectionLayer : OsuTestCase + public class TestCaseEditorSelectionLayer : EditorClockTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -35,11 +34,6 @@ namespace osu.Game.Tests.Visual typeof(SliderCircleMask) }; - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); - [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { @@ -65,10 +59,6 @@ namespace osu.Game.Tests.Visual }, }); - var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - dependencies.CacheAs(clock); - dependencies.CacheAs(clock); - Child = new OsuHitObjectComposer(new OsuRuleset()); } } diff --git a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs index bbe2956c5d..13e7fb8c7d 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs @@ -6,46 +6,32 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using OpenTK; using osu.Game.Screens.Edit.Components.Timelines.Summary; -using osu.Framework.Configuration; -using osu.Framework.Timing; using osu.Game.Rulesets.Osu; using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseEditorSummaryTimeline : OsuTestCase + public class TestCaseEditorSummaryTimeline : EditorClockTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(SummaryTimeline) }; - private readonly Bindable beatmap = new Bindable(); - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); - [BackgroundDependencyLoader] - private void load() + private void load(OsuGameBase osuGame) { - beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); - - var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - dependencies.CacheAs(clock); - dependencies.CacheAs(clock); + osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); SummaryTimeline summaryTimeline; - Add(summaryTimeline = new SummaryTimeline + Add(summaryTimeline = new SummaryTimeline(Clock) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(500, 50) }); - summaryTimeline.Beatmap.BindTo(beatmap); + summaryTimeline.Beatmap.BindTo(osuGame.Beatmap); } } } diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs new file mode 100644 index 0000000000..2ae382eb5b --- /dev/null +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Input; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Screens.Compose; + +namespace osu.Game.Tests.Visual +{ + /// + /// Provides a clock, beat-divisor, and scrolling capability for test cases of editor components that + /// are preferrably tested within the presence of a clock and seek controls. + /// + public abstract class EditorClockTestCase : OsuTestCase + { + protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor(); + protected EditorClock Clock { get; private set; } + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + + private OsuGameBase osuGame; + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + this.osuGame = osuGame; + + osuGame.Beatmap.ValueChanged += reinitializeClock; + reinitializeClock(osuGame.Beatmap.Value); + } + + protected override void Dispose(bool isDisposing) + { + osuGame.Beatmap.ValueChanged -= reinitializeClock; + + base.Dispose(isDisposing); + } + + private void reinitializeClock(WorkingBeatmap working) + { + Clock = new EditorClock(working.Beatmap.ControlPointInfo, BeatDivisor) { IsCoupled = false }; + dependencies.CacheAs(Clock); + dependencies.CacheAs(Clock); + } + + protected override bool OnWheel(InputState state) + { + if (state.Mouse.WheelDelta > 0) + Clock.SeekBackward(true); + else + Clock.SeekForward(true); + + return true; + } + } +} From 248be8e35fed7e67c00325941a5ea758335646b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Apr 2018 18:21:45 +0900 Subject: [PATCH 408/537] HitObjectComposer no longer needs a beat divisor --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 79ab67fafd..9d0246b2fc 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -15,7 +14,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; @@ -31,7 +29,6 @@ namespace osu.Game.Rulesets.Edit private readonly List layerContainers = new List(); private readonly Bindable beatmap = new Bindable(); - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); protected HitObjectComposer(Ruleset ruleset) { @@ -40,12 +37,9 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both; } - [BackgroundDependencyLoader(true)] - private void load([NotNull] OsuGameBase osuGame, [NotNull] IFrameBasedClock framedClock, [CanBeNull] BindableBeatDivisor beatDivisor) + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame, IFrameBasedClock framedClock) { - if (beatDivisor != null) - this.beatDivisor.BindTo(beatDivisor); - beatmap.BindTo(osuGame.Beatmap); try From 9e8490735f6a7bc6d76d81c83eaf0ced345486eb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Apr 2018 18:22:24 +0900 Subject: [PATCH 409/537] Fix Compose not binding to the editor's beat divisor --- osu.Game/Screens/Edit/Editor.cs | 1 + osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 13 +++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 8a8932970c..d63ee3fe75 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -52,6 +52,7 @@ namespace osu.Game.Screens.Edit dependencies.CacheAs(clock); dependencies.CacheAs(clock); + dependencies.Cache(beatDivisor); EditorMenuBar menuBar; TimeInfoContainer timeInfo; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 91adc8324a..bd672451c0 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using JetBrains.Annotations; using osu.Framework.Allocation; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; @@ -21,15 +22,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose private Container composerContainer; - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); - - [BackgroundDependencyLoader] - private void load() + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] BindableBeatDivisor beatDivisor) { - dependencies.Cache(beatDivisor); + if (beatDivisor != null) + this.beatDivisor.BindTo(beatDivisor); ScrollableTimeline timeline; Children = new Drawable[] From b238130fe446e25d4d220b2b1a29612f815803e9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Apr 2018 18:23:22 +0900 Subject: [PATCH 410/537] DI beat divisors to test cases with editor clocks --- osu.Game/Tests/Visual/EditorClockTestCase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs index 2ae382eb5b..202100c810 100644 --- a/osu.Game/Tests/Visual/EditorClockTestCase.cs +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -31,6 +31,8 @@ namespace osu.Game.Tests.Visual { this.osuGame = osuGame; + dependencies.Cache(BeatDivisor); + osuGame.Beatmap.ValueChanged += reinitializeClock; reinitializeClock(osuGame.Beatmap.Value); } From 8ef5855e84d283de6b3ed6125c6aaec556e1f5cf Mon Sep 17 00:00:00 2001 From: Endrik Date: Fri, 6 Apr 2018 12:38:17 +0300 Subject: [PATCH 411/537] Fix typo --- osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index 4ae897bc1a..9af4f15d1f 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -155,7 +155,7 @@ namespace osu.Game.Tests.Visual private void checkNonmatchingFilter() { - AddStep("Toggel non-matching filter", () => + AddStep("Toggle non-matching filter", () => { carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false); carousel.Filter(new FilterCriteria(), false); From e59124962c35234a9d9352d2b0e868d766118f2a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Apr 2018 18:38:44 +0900 Subject: [PATCH 412/537] Remove re-instantiation of clock in EditorClockTestCase --- osu.Game/Screens/Edit/EditorClock.cs | 22 ++++++------ osu.Game/Tests/Visual/EditorClockTestCase.cs | 35 +++++++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index ddde819757..a084096c3b 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -12,18 +12,20 @@ namespace osu.Game.Screens.Edit { public class EditorClock : DecoupleableInterpolatingFramedClock { - private readonly ControlPointInfo controlPointInfo; + public ControlPointInfo ControlPointInfo; + private readonly BindableBeatDivisor beatDivisor; public EditorClock(ControlPointInfo controlPointInfo, BindableBeatDivisor beatDivisor) { - this.controlPointInfo = controlPointInfo; this.beatDivisor = beatDivisor; + + ControlPointInfo = controlPointInfo; } public bool SeekSnapped(double position) { - var timingPoint = controlPointInfo.TimingPointAt(position); + var timingPoint = ControlPointInfo.TimingPointAt(position); double beatSnapLength = timingPoint.BeatLength / beatDivisor; // We will be snapping to beats within the timing point @@ -35,7 +37,7 @@ namespace osu.Game.Screens.Edit // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to // the next timing point's start time - var nextTimingPoint = controlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); if (position > nextTimingPoint?.Time) position = nextTimingPoint.Time; @@ -56,19 +58,19 @@ namespace osu.Game.Screens.Edit private void seek(int direction, bool snapped) { - var timingPoint = controlPointInfo.TimingPointAt(CurrentTime); + var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime); if (direction < 0 && timingPoint.Time == CurrentTime) { // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into - int activeIndex = controlPointInfo.TimingPoints.IndexOf(timingPoint); + int activeIndex = ControlPointInfo.TimingPoints.IndexOf(timingPoint); while (activeIndex > 0 && CurrentTime == timingPoint.Time) - timingPoint = controlPointInfo.TimingPoints[--activeIndex]; + timingPoint = ControlPointInfo.TimingPoints[--activeIndex]; } double seekAmount = timingPoint.BeatLength / beatDivisor; double seekTime = CurrentTime + seekAmount * direction; - if (!snapped || controlPointInfo.TimingPoints.Count == 0) + if (!snapped || ControlPointInfo.TimingPoints.Count == 0) { Seek(seekTime); return; @@ -94,10 +96,10 @@ namespace osu.Game.Screens.Edit seekTime = timingPoint.Time + closestBeat * seekAmount; } - if (seekTime < timingPoint.Time && timingPoint != controlPointInfo.TimingPoints.First()) + if (seekTime < timingPoint.Time && timingPoint != ControlPointInfo.TimingPoints.First()) seekTime = timingPoint.Time; - var nextTimingPoint = controlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); if (seekTime > nextTimingPoint?.Time) seekTime = nextTimingPoint.Time; diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs index 202100c810..3e42f7a242 100644 --- a/osu.Game/Tests/Visual/EditorClockTestCase.cs +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Input; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Screens.Compose; @@ -17,7 +18,7 @@ namespace osu.Game.Tests.Visual public abstract class EditorClockTestCase : OsuTestCase { protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor(); - protected EditorClock Clock { get; private set; } + protected readonly EditorClock Clock; private DependencyContainer dependencies; @@ -26,31 +27,26 @@ namespace osu.Game.Tests.Visual private OsuGameBase osuGame; + protected EditorClockTestCase() + { + Clock = new EditorClock(new ControlPointInfo(), BeatDivisor) { IsCoupled = false }; + } + [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { this.osuGame = osuGame; dependencies.Cache(BeatDivisor); - - osuGame.Beatmap.ValueChanged += reinitializeClock; - reinitializeClock(osuGame.Beatmap.Value); - } - - protected override void Dispose(bool isDisposing) - { - osuGame.Beatmap.ValueChanged -= reinitializeClock; - - base.Dispose(isDisposing); - } - - private void reinitializeClock(WorkingBeatmap working) - { - Clock = new EditorClock(working.Beatmap.ControlPointInfo, BeatDivisor) { IsCoupled = false }; dependencies.CacheAs(Clock); dependencies.CacheAs(Clock); + + osuGame.Beatmap.ValueChanged += beatmapChanged; + beatmapChanged(osuGame.Beatmap.Value); } + private void beatmapChanged(WorkingBeatmap working) => Clock.ControlPointInfo = working.Beatmap.ControlPointInfo; + protected override bool OnWheel(InputState state) { if (state.Mouse.WheelDelta > 0) @@ -60,5 +56,12 @@ namespace osu.Game.Tests.Visual return true; } + + protected override void Dispose(bool isDisposing) + { + osuGame.Beatmap.ValueChanged -= beatmapChanged; + + base.Dispose(isDisposing); + } } } From 070e68f235060a6e9977143cfcf36834e6c10155 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Apr 2018 19:14:04 +0900 Subject: [PATCH 413/537] Give the test case clock an accurate IsRunning value --- osu.Game/Tests/Visual/EditorClockTestCase.cs | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs index 3e42f7a242..5cc03a4553 100644 --- a/osu.Game/Tests/Visual/EditorClockTestCase.cs +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -45,7 +45,26 @@ namespace osu.Game.Tests.Visual beatmapChanged(osuGame.Beatmap.Value); } - private void beatmapChanged(WorkingBeatmap working) => Clock.ControlPointInfo = working.Beatmap.ControlPointInfo; + private void beatmapChanged(WorkingBeatmap working) + { + Clock.ControlPointInfo = working.Beatmap.ControlPointInfo; + Clock.ChangeSource((IAdjustableClock)working.Track ?? new StopwatchClock()); + Clock.ProcessFrame(); + } + + protected override void Update() + { + base.Update(); + + // We don't have any explicit way to start/stop the track, but want a relatively accurate IsRunning state + // The track's IsRunning state is used to determine our clock's IsRunning state + if (osuGame.Beatmap.Value.Track.IsRunning) + Clock.Start(); + else + Clock.Stop(); + + Clock.ProcessFrame(); + } protected override bool OnWheel(InputState state) { From cd48cb18874f328e0df98b21a8d63defe71ddbfb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Apr 2018 15:17:39 +0900 Subject: [PATCH 414/537] Add comment --- .../Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 88f865be20..423cf0ed29 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -21,7 +21,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer) { + // we need the playfield as HitObjects may not be initialised until its BDL. this.playfield = playfield; + this.composer = composer; RelativeSizeAxes = Axes.Both; From ae2dce254aceb1439cd8dce7cc6efb37de652570 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Apr 2018 15:18:01 +0900 Subject: [PATCH 415/537] Rename TestCase --- ...CaseEditorSelectionLayer.cs => TestCaseHitObjectComposer.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/{TestCaseEditorSelectionLayer.cs => TestCaseHitObjectComposer.cs} (94%) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs similarity index 94% rename from osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs rename to osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs index 9b50645b0d..72d60d8e01 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs @@ -20,7 +20,7 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseEditorSelectionLayer : OsuTestCase + public class TestCaseHitObjectComposer : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { From c80c1071e82dbd22f6c08dace146a4ae4bbf75da Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Apr 2018 19:54:19 +0900 Subject: [PATCH 416/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index e4b0b57f5d..02d7a0fa47 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit e4b0b57f5d3a80c09dcdfb6c8d30962e842b9fc3 +Subproject commit 02d7a0fa4798d197cd08570ee48951edbb7c7860 From 640be621ac4e178f1e1b6f24283e13719e7f7ef6 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Sat, 7 Apr 2018 13:29:46 +0300 Subject: [PATCH 417/537] Handle multiple song previews playing in different beatmap categories on profile --- .../Beatmaps/PaginatedBeatmapContainer.cs | 4 ++++ .../Profile/Sections/BeatmapsSection.cs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index da070d1cc2..637985e3cd 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using OpenTK; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -19,6 +20,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private DirectPanel currentlyPlaying; + public event Action BeatmapAdded; + public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") : base(user, header, missing) { @@ -63,6 +66,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps currentlyPlaying = panel; }; + BeatmapAdded?.Invoke(panel); } }; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 760054716f..928ac677a4 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -1,7 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Direct; using osu.Game.Overlays.Profile.Sections.Beatmaps; namespace osu.Game.Overlays.Profile.Sections @@ -12,6 +14,8 @@ namespace osu.Game.Overlays.Profile.Sections public override string Identifier => "beatmaps"; + private DirectPanel currentlyPlaying; + public BeatmapsSection() { Children = new[] @@ -21,6 +25,19 @@ namespace osu.Game.Overlays.Profile.Sections new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"), }; + + foreach (var beatmapContainer in Children.OfType()) + { + beatmapContainer.BeatmapAdded += panel => panel.PreviewPlaying.ValueChanged += isPlaying => + { + if (!isPlaying) return; + + if (currentlyPlaying != null && currentlyPlaying != panel) + currentlyPlaying.PreviewPlaying.Value = false; + + currentlyPlaying = panel; + }; + } } } } From 421e9e0641d0d42d2b4a32b9f585d3c97c6eaaac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 Apr 2018 12:58:34 +0900 Subject: [PATCH 418/537] Add xmldoc to some high-level classes to explain their separation --- osu.Game/OsuGame.cs | 4 ++++ osu.Game/OsuGameBase.cs | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 89447b8ed6..2f6f8ff348 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -36,6 +36,10 @@ using osu.Game.Overlays.Volume; namespace osu.Game { + /// + /// The full osu! experience. Builds on top of to add menus and binding logic + /// for initial components that are generally retrieved via DI. + /// public class OsuGame : OsuGameBase, IKeyBindingHandler { public Toolbar Toolbar; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 54a279e977..533a04286b 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -34,6 +34,11 @@ using osu.Game.Skinning; namespace osu.Game { + /// + /// The most basic that can be used to host osu! components and systems. + /// Unlike , this class will not load any kind of UI, allowing it to be used + /// for provide dependencies to test cases without interfering with them. + /// public class OsuGameBase : Framework.Game, ICanAcceptFiles { protected OsuConfigManager LocalConfig; From 58dbc63c6e85fecf9f13aea709e1397bd790161b Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Sun, 8 Apr 2018 15:24:34 +0200 Subject: [PATCH 419/537] Add HardRock position mangling for CatchTheBeat --- .../Mods/CatchModHardRock.cs | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index ed33bf7124..5dadec9b15 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -1,13 +1,88 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Mods; +using System; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModHardRock : ModHardRock + public class CatchModHardRock : ModHardRock, IApplicableToHitObject { public override double ScoreMultiplier => 1.12; public override bool Ranked => true; + + private float lastStartX; + private int lastStartTime; + + public void ApplyToHitObject(CatchHitObject hitObject) + { + // Code from Stable, we keep calculation on a scale of 0 to 512 + float position = hitObject.X * 512; + int startTime = (int)hitObject.StartTime; + + if (lastStartX == 0) + { + lastStartX = position; + lastStartTime = startTime; + return; + } + + float diff = lastStartX - position; + int timeDiff = startTime - lastStartTime; + + if (timeDiff > 1000) + { + lastStartX = position; + lastStartTime = startTime; + return; + } + + if (diff == 0) + { + bool right = RNG.NextBool(); + + float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4)); + + if (right) + { + if (position + rand <= 512) + position += rand; + else + position -= rand; + } + else + { + if (position - rand >= 0) + position -= rand; + else + position += rand; + } + + hitObject.X = position / 512; + + return; + } + + if (Math.Abs(diff) < timeDiff / 3) + { + if (diff > 0) + { + if (position - diff > 0) + position -= diff; + } + else + { + if (position - diff < 512) + position -= diff; + } + } + + hitObject.X = position / 512; + + lastStartX = position; + lastStartTime = startTime; + } } } From b40af0848f5cb2b4579ac30730bbd7606c3e5ee2 Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Sun, 8 Apr 2018 15:52:40 +0200 Subject: [PATCH 420/537] White space --- osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index 5dadec9b15..97b1645e85 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Mods { public override double ScoreMultiplier => 1.12; public override bool Ranked => true; - + private float lastStartX; private int lastStartTime; From 7e78b2e54e51096ce927db1f2abb839adf6cd1ad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Apr 2018 11:37:03 +0900 Subject: [PATCH 421/537] Remove TestCaseGamefield Unused. Unneeded. We already have TestCaseAllPlayers and individual tests in all rulesets. Eventually we probably also want osu.Game.Tests to not reference ruleset projects anyway. --- osu.Game.Tests/Visual/TestCaseGamefield.cs | 90 ---------------------- 1 file changed, 90 deletions(-) delete mode 100644 osu.Game.Tests/Visual/TestCaseGamefield.cs diff --git a/osu.Game.Tests/Visual/TestCaseGamefield.cs b/osu.Game.Tests/Visual/TestCaseGamefield.cs deleted file mode 100644 index 80b3f9eb40..0000000000 --- a/osu.Game.Tests/Visual/TestCaseGamefield.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseGamefield : OsuTestCase - { - protected override void LoadComplete() - { - base.LoadComplete(); - - /*int time = 500; - for (int i = 0; i < 100; i++) - { - objects.Add(new HitCircle - { - StartTime = time, - Position = new Vector2(RNG.Next(0, (int)OsuPlayfield.BASE_SIZE.X), RNG.Next(0, (int)OsuPlayfield.BASE_SIZE.Y)), - Scale = RNG.NextSingle(0.5f, 1.0f), - }); - - time += RNG.Next(50, 500); - }*/ - - var controlPointInfo = new ControlPointInfo(); - controlPointInfo.TimingPoints.Add(new TimingControlPoint - { - BeatLength = 200 - }); - - /*WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap - { - HitObjects = objects, - BeatmapInfo = new BeatmapInfo - { - Difficulty = new BeatmapDifficulty(), - Ruleset = rulesets.Query().First(), - Metadata = new BeatmapMetadata - { - Artist = @"Unknown", - Title = @"Sample Beatmap", - Author = @"peppy", - }, - }, - ControlPointInfo = controlPointInfo - }); - - AddRange(new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - //ensure we are at offset 0 - Clock = new FramedClock(), - Children = new Drawable[] - { - new OsuRulesetContainer(new OsuRuleset(), beatmap, false) - { - Scale = new Vector2(0.5f), - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft - }, - new TaikoRulesetContainer(new TaikoRuleset(),beatmap, false) - { - Scale = new Vector2(0.5f), - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }, - new CatchRulesetContainer(new CatchRuleset(),beatmap, false) - { - Scale = new Vector2(0.5f), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft - }, - new ManiaRulesetContainer(new ManiaRuleset(),beatmap, false) - { - Scale = new Vector2(0.5f), - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight - } - } - } - });*/ - } - } -} From b97c4e8b44202aa8b3049ed33661555ee1e5593b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Apr 2018 12:45:44 +0900 Subject: [PATCH 422/537] Fix all possible cases of crossthread import data races --- osu.Game/Database/ArchiveModelManager.cs | 2 ++ osu.Game/Overlays/BeatmapSet/Header.cs | 4 ++-- osu.Game/Overlays/DirectOverlay.cs | 12 ++++++++-- osu.Game/Overlays/Music/PlaylistOverlay.cs | 18 ++++++++++++-- .../Overlays/Settings/Sections/SkinSection.cs | 24 +++++++++++++++---- 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 7050e34712..3afb22f0ad 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -35,11 +35,13 @@ namespace osu.Game.Database /// /// Fired when a new becomes available in the database. + /// This is not guaranteed to run on the update thread. /// public event Action ItemAdded; /// /// Fired when a is removed from the database. + /// This is not guaranteed to run on the update thread. /// public event Action ItemRemoved; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index b9a35ec1f0..8056dbff3c 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -246,11 +246,11 @@ namespace osu.Game.Overlays.BeatmapSet if (beatmaps != null) beatmaps.ItemAdded -= handleBeatmapAdd; } - private void handleBeatmapAdd(BeatmapSetInfo beatmap) + private void handleBeatmapAdd(BeatmapSetInfo beatmap) => Schedule(() => { if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID) downloadButtonsContainer.FadeOut(transition_duration); - } + }); private void download(bool noVideo) { diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 8d8a4aebaa..3f1aa04c36 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -188,12 +188,12 @@ namespace osu.Game.Overlays beatmaps.ItemAdded += setAdded; } - private void setAdded(BeatmapSetInfo set) + private void setAdded(BeatmapSetInfo set) => Schedule(() => { // if a new map was imported, we should remove it from search results (download completed etc.) panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire(); BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID); - } + }); private void updateResultCounts() { @@ -323,6 +323,14 @@ namespace osu.Game.Overlays private int distinctCount(List list) => list.Distinct().ToArray().Length; + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmaps != null) + beatmaps.ItemAdded -= setAdded; + } + public class ResultCounts { public readonly int Artists; diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index ac7ec6257b..c981e5f493 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -74,8 +74,8 @@ namespace osu.Game.Overlays.Music }, }; - beatmaps.ItemAdded += list.AddBeatmapSet; - beatmaps.ItemRemoved += list.RemoveBeatmapSet; + beatmaps.ItemAdded += handleBeatmapAdded; + beatmaps.ItemRemoved += handleBeatmapRemoved; list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); @@ -95,6 +95,9 @@ namespace osu.Game.Overlays.Music beatmapBacking.TriggerChange(); } + private void handleBeatmapAdded(BeatmapSetInfo setInfo) => Schedule(() => list.AddBeatmapSet(setInfo)); + private void handleBeatmapRemoved(BeatmapSetInfo setInfo) => Schedule(() => list.RemoveBeatmapSet(setInfo)); + protected override void PopIn() { filter.Search.HoldFocus = true; @@ -153,6 +156,17 @@ namespace osu.Game.Overlays.Music track.Restart(); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmaps != null) + { + beatmaps.ItemAdded -= handleBeatmapAdded; + beatmaps.ItemRemoved -= handleBeatmapRemoved; + } + } } //todo: placeholder diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 5df5304751..f16ddb2811 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -21,9 +21,13 @@ namespace osu.Game.Overlays.Settings.Sections public override FontAwesome Icon => FontAwesome.fa_paint_brush; + private SkinManager skins; + [BackgroundDependencyLoader] private void load(OsuConfigManager config, SkinManager skins) { + this.skins = skins; + FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { @@ -47,15 +51,27 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); - skins.ItemAdded += _ => reloadSkins(); - skins.ItemRemoved += _ => reloadSkins(); + skins.ItemAdded += reloadSkins; + skins.ItemRemoved += reloadSkins; - reloadSkins(); + reloadSkins(null); skinDropdown.Bindable = config.GetBindable(OsuSetting.Skin); } + private void reloadSkins(SkinInfo changed) => Schedule(() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID))); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (skins != null) + { + skins.ItemAdded -= reloadSkins; + skins.ItemRemoved -= reloadSkins; + } + } + private class SizeSlider : OsuSliderBar { public override string TooltipText => Current.Value.ToString(@"0.##x"); From 82d9504cbf866e601dd3e7c519113354f7433c36 Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Mon, 9 Apr 2018 09:02:32 +0200 Subject: [PATCH 423/537] Changed relative position to [0, 1] --- osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index 97b1645e85..1656a5c40b 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -3,6 +3,7 @@ using osu.Framework.MathUtils; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; using System; @@ -18,8 +19,7 @@ namespace osu.Game.Rulesets.Catch.Mods public void ApplyToHitObject(CatchHitObject hitObject) { - // Code from Stable, we keep calculation on a scale of 0 to 512 - float position = hitObject.X * 512; + float position = hitObject.X; int startTime = (int)hitObject.StartTime; if (lastStartX == 0) @@ -43,11 +43,11 @@ namespace osu.Game.Rulesets.Catch.Mods { bool right = RNG.NextBool(); - float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4)); + float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4d)) / CatchPlayfield.BASE_WIDTH; if (right) { - if (position + rand <= 512) + if (position + rand <= 1) position += rand; else position -= rand; @@ -60,12 +60,12 @@ namespace osu.Game.Rulesets.Catch.Mods position += rand; } - hitObject.X = position / 512; + hitObject.X = position; return; } - if (Math.Abs(diff) < timeDiff / 3) + if (Math.Abs(diff) < timeDiff / 3d) { if (diff > 0) { @@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Catch.Mods } else { - if (position - diff < 512) + if (position - diff < 1) position -= diff; } } - hitObject.X = position / 512; + hitObject.X = position; lastStartX = position; lastStartTime = startTime; From cea3e1c7f5b62eab2d7de8c3e4da17f03bbdc97f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Apr 2018 18:44:50 +0900 Subject: [PATCH 424/537] Remove now unnecessary approachcircle proxy disables Prereqs: - [ ] ppy/osu-framework#1505 --- osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs | 1 - osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs index 46a3d8575f..22ce433eec 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs @@ -7,7 +7,6 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuEditPlayfield : OsuPlayfield { - protected override bool ProxyApproachCircles => false; protected override bool DisplayJudgements => false; } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 9010f66acb..3e894e609a 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -21,9 +21,6 @@ namespace osu.Game.Rulesets.Osu.UI private readonly JudgementContainer judgementLayer; private readonly ConnectionRenderer connectionLayer; - // Todo: This should not be a thing, but is currently required for the editor - // https://github.com/ppy/osu-framework/issues/1283 - protected virtual bool ProxyApproachCircles => true; protected virtual bool DisplayJudgements => true; public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); @@ -59,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.UI h.OnJudgement += onJudgement; var c = h as IDrawableHitObjectWithProxiedApproach; - if (c != null && ProxyApproachCircles) + if (c != null) approachCircles.Add(c.ProxiedLayer.CreateProxy()); base.Add(h); From 767ecb4422e9cdef40551d4c9b9aa56805080f63 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 10 Apr 2018 00:11:53 -0700 Subject: [PATCH 425/537] Fix rank status --- osu.Game/Beatmaps/RankStatus.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/RankStatus.cs b/osu.Game/Beatmaps/RankStatus.cs index 5ca358ddef..8792c088af 100644 --- a/osu.Game/Beatmaps/RankStatus.cs +++ b/osu.Game/Beatmaps/RankStatus.cs @@ -13,8 +13,7 @@ namespace osu.Game.Beatmaps Approved = 1, Loved = 8, Favourites = 2, - [Description("Mod Requests")] - ModRequests = 3, + Qualified = 3, Pending = 4, Graveyard = 5, [Description("My Maps")] From d851446acaad3a2b5bd9ca41b913ec6ff0316a8d Mon Sep 17 00:00:00 2001 From: naoey Date: Tue, 10 Apr 2018 19:15:37 +0530 Subject: [PATCH 426/537] Remove redundant anchor and use Show/Hide instead of FadeIn/Out. --- osu.Game/Overlays/BeatmapSetOverlay.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index c144e03bcb..98466ed986 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -55,12 +55,12 @@ namespace osu.Game.Overlays if (beatmapSet == null) { scroll.FadeOut(fade_duration); - loading.FadeIn(fade_duration); + loading.Show(); return; } header.BeatmapSet = info.BeatmapSet = beatmapSet; - loading.FadeOut(fade_duration); + loading.Hide(); scroll.FadeIn(fade_duration); } } @@ -98,9 +98,7 @@ namespace osu.Game.Overlays }, loading = new LoadingAnimation { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Alpha = 1, + State = Visibility.Visible, }, scroll = new ScrollContainer { From 903dd7a01515c0ed9ace9d192587af5a188a241f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Apr 2018 19:24:19 +0900 Subject: [PATCH 427/537] Fix regression causing hard crash Regressed in #2373. My bad. --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index f16ddb2811..a2215035dd 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -51,15 +51,17 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - skins.ItemAdded += reloadSkins; - skins.ItemRemoved += reloadSkins; + skins.ItemAdded += onItemsChanged; + skins.ItemRemoved += onItemsChanged; - reloadSkins(null); + reloadSkins(); skinDropdown.Bindable = config.GetBindable(OsuSetting.Skin); } - private void reloadSkins(SkinInfo changed) => Schedule(() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID))); + private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); + + private void onItemsChanged(SkinInfo _) => Schedule(reloadSkins); protected override void Dispose(bool isDisposing) { @@ -67,8 +69,8 @@ namespace osu.Game.Overlays.Settings.Sections if (skins != null) { - skins.ItemAdded -= reloadSkins; - skins.ItemRemoved -= reloadSkins; + skins.ItemAdded -= onItemsChanged; + skins.ItemRemoved -= onItemsChanged; } } From 6cdfaffcf7c6da6beda62118397af17dcf243e44 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Wed, 11 Apr 2018 16:19:21 +0300 Subject: [PATCH 428/537] PaginatedBeatmapContainer.BeganPlayingPreview --- .../Beatmaps/PaginatedBeatmapContainer.cs | 12 +++++++++--- .../Overlays/Profile/Sections/BeatmapsSection.cs | 15 +++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 637985e3cd..6e22d51958 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private DirectPanel currentlyPlaying; - public event Action BeatmapAdded; + public event Action BeganPlayingPreview; public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") : base(user, header, missing) @@ -61,16 +61,22 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps { if (!isPlaying) return; + BeganPlayingPreview?.Invoke(this); if (currentlyPlaying != null && currentlyPlaying != panel) - currentlyPlaying.PreviewPlaying.Value = false; + StopPlayingPreview(); currentlyPlaying = panel; }; - BeatmapAdded?.Invoke(panel); } }; Api.Queue(req); } + + public void StopPlayingPreview() + { + if (currentlyPlaying != null) + currentlyPlaying.PreviewPlaying.Value = false; + } } } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 928ac677a4..f1f2421237 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -26,16 +26,15 @@ namespace osu.Game.Overlays.Profile.Sections new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"), }; - foreach (var beatmapContainer in Children.OfType()) + foreach (var paginatedBeatmapContainer in Children.OfType()) { - beatmapContainer.BeatmapAdded += panel => panel.PreviewPlaying.ValueChanged += isPlaying => + paginatedBeatmapContainer.BeganPlayingPreview += (BeatmapContainer) => { - if (!isPlaying) return; - - if (currentlyPlaying != null && currentlyPlaying != panel) - currentlyPlaying.PreviewPlaying.Value = false; - - currentlyPlaying = panel; + foreach (var bc in Children.OfType()) + { + if (bc != BeatmapContainer) + bc.StopPlayingPreview(); + } }; } } From 9793137bfa7e4e54b346cd1cfd3d7f3b4c61b5fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Apr 2018 22:31:50 +0900 Subject: [PATCH 429/537] Add back app.manifest Resolves #2380 --- app.manifest | 46 ++++++++++++++++++++++++++++++++++++++++++++++ osu.Game.props | 3 +++ 2 files changed, 49 insertions(+) create mode 100644 app.manifest diff --git a/app.manifest b/app.manifest new file mode 100644 index 0000000000..d1c97498f4 --- /dev/null +++ b/app.manifest @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + diff --git a/osu.Game.props b/osu.Game.props index 87edafb97f..ec859e64a5 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -3,6 +3,9 @@ 7 + + ..\app.manifest + osu.licenseheader From 6a8f568f66b9e8bc14f2ce892f6c31110ecf96ff Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Wed, 11 Apr 2018 16:32:58 +0300 Subject: [PATCH 430/537] BeatmapContainer -> beatmapContainer --- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index f1f2421237..a0d37ffe71 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -28,11 +28,11 @@ namespace osu.Game.Overlays.Profile.Sections foreach (var paginatedBeatmapContainer in Children.OfType()) { - paginatedBeatmapContainer.BeganPlayingPreview += (BeatmapContainer) => + paginatedBeatmapContainer.BeganPlayingPreview += beatmapContainer => { foreach (var bc in Children.OfType()) { - if (bc != BeatmapContainer) + if (bc != beatmapContainer) bc.StopPlayingPreview(); } }; From d7812ab12e37e70abc948fb437dec84d96f784d1 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Wed, 11 Apr 2018 21:22:52 +0300 Subject: [PATCH 431/537] CursorOverrideContainer.ShowMenuCursor --- .../Graphics/Cursor/CursorOverrideContainer.cs | 9 +++++++++ osu.Game/Graphics/ScreenshotManager.cs | 16 ++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs index 0fae4579fa..73fa065919 100644 --- a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs +++ b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs @@ -21,6 +21,7 @@ namespace osu.Game.Graphics.Cursor /// Whether any cursors can be displayed. /// public bool CanShowCursor = true; + public bool ShowMenuCursor = true; public CursorContainer Cursor { get; } public bool ProvidingUserCursor => true; @@ -53,6 +54,14 @@ namespace osu.Game.Graphics.Cursor return; } + if (currentTarget?.Cursor is MenuCursor) + { + if (ShowMenuCursor && currentTarget?.Cursor.State == Visibility.Hidden) + currentTarget?.Cursor?.Show(); + else if (!ShowMenuCursor && currentTarget?.Cursor.State == Visibility.Visible) + currentTarget?.Cursor?.Hide(); + } + var newTarget = inputManager.HoveredDrawables.OfType().FirstOrDefault(t => t.ProvidingUserCursor) ?? this; if (currentTarget == newTarget) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 434a9d0a72..e918ff016e 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -11,7 +11,6 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Platform; @@ -33,7 +32,7 @@ namespace osu.Game.Graphics private NotificationOverlay notificationOverlay; private SampleChannel shutter; - private CursorContainer menuCursorContainer; + private CursorOverrideContainer cursorOverrideContainer; [BackgroundDependencyLoader] private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio, CursorOverrideContainer cursorOverrideContainer) @@ -41,7 +40,7 @@ namespace osu.Game.Graphics this.host = host; this.storage = storage.GetStorageForDirectory(@"screenshots"); this.notificationOverlay = notificationOverlay; - menuCursorContainer = cursorOverrideContainer.Cursor; + this.cursorOverrideContainer = cursorOverrideContainer; screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); captureMenuCursor = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor); @@ -66,16 +65,14 @@ namespace osu.Game.Graphics public async void TakeScreenshotAsync() { - var menuCursorWasHidden = false; - if (!captureMenuCursor.Value && menuCursorContainer.State == Visibility.Visible) + if (!captureMenuCursor.Value) { - menuCursorContainer.ToggleVisibility(); + cursorOverrideContainer.ShowMenuCursor = false; await Task.Run(() => { - while (menuCursorContainer.ActiveCursor.Alpha > 0) + while (cursorOverrideContainer.Cursor.ActiveCursor.Alpha > 0) Thread.Sleep(1); }); - menuCursorWasHidden = true; } using (var bitmap = await host.TakeScreenshotAsync()) @@ -108,8 +105,7 @@ namespace osu.Game.Graphics }); } - if (menuCursorWasHidden) - menuCursorContainer.ToggleVisibility(); + cursorOverrideContainer.ShowMenuCursor = true; } private string getFileName() From 0bede523817eacea0b2f6656cb3f800cb1e87991 Mon Sep 17 00:00:00 2001 From: DrabWeb Date: Wed, 28 Mar 2018 17:57:15 -0300 Subject: [PATCH 432/537] Move the waves portion of WaveOverlayContainer to WaveContainer to allow usage in other places. --- .../Visual/TestCaseWaveContainer.cs | 54 ++++++ osu.Game/Graphics/Containers/WaveContainer.cs | 167 +++++++++++++++++ osu.Game/Overlays/BeatmapSetOverlay.cs | 12 +- osu.Game/Overlays/DirectOverlay.cs | 8 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 31 ++- osu.Game/Overlays/SocialOverlay.cs | 8 +- osu.Game/Overlays/UserProfileOverlay.cs | 12 +- osu.Game/Overlays/WaveOverlayContainer.cs | 176 +----------------- 8 files changed, 261 insertions(+), 207 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseWaveContainer.cs create mode 100644 osu.Game/Graphics/Containers/WaveContainer.cs diff --git a/osu.Game.Tests/Visual/TestCaseWaveContainer.cs b/osu.Game.Tests/Visual/TestCaseWaveContainer.cs new file mode 100644 index 0000000000..e332777053 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseWaveContainer.cs @@ -0,0 +1,54 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseWaveContainer : OsuTestCase + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + WaveContainer container; + Add(container = new WaveContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(400), + FirstWaveColour = colours.Red, + SecondWaveColour = colours.Green, + ThirdWaveColour = colours.Blue, + FourthWaveColour = colours.Pink, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextSize = 20, + Text = @"Wave Container", + }, + }, + }); + + AddStep(@"show", container.Show); + AddStep(@"hide", container.Hide); + } + } +} diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs new file mode 100644 index 0000000000..f112a6477e --- /dev/null +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -0,0 +1,167 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using OpenTK.Graphics; + +namespace osu.Game.Graphics.Containers +{ + public class WaveContainer : VisibilityContainer + { + public const float APPEAR_DURATION = 800; + public const float DISAPPEAR_DURATION = 500; + + private const Easing easing_show = Easing.OutSine; + private const Easing easing_hide = Easing.InSine; + + private readonly Wave firstWave; + private readonly Wave secondWave; + private readonly Wave thirdWave; + private readonly Wave fourthWave; + + private readonly Container wavesContainer; + private readonly Container contentContainer; + + protected override Container Content => contentContainer; + + public Color4 FirstWaveColour + { + get => firstWave.Colour; + set => firstWave.Colour = value; + } + + public Color4 SecondWaveColour + { + get => secondWave.Colour; + set => secondWave.Colour = value; + } + + public Color4 ThirdWaveColour + { + get => thirdWave.Colour; + set => thirdWave.Colour = value; + } + + public Color4 FourthWaveColour + { + get => fourthWave.Colour; + set => fourthWave.Colour = value; + } + + public WaveContainer() + { + Masking = true; + + AddInternal(wavesContainer = new Container + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Masking = true, + Children = new[] + { + firstWave = new Wave + { + Rotation = 13, + FinalPosition = -930, + }, + secondWave = new Wave + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Rotation = -7, + FinalPosition = -560, + }, + thirdWave = new Wave + { + Rotation = 4, + FinalPosition = -390, + }, + fourthWave = new Wave + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Rotation = -2, + FinalPosition = -220, + }, + }, + }); + + AddInternal(contentContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }); + } + + protected override void PopIn() + { + foreach (var w in wavesContainer.Children) + w.State = Visibility.Visible; + + this.FadeIn(100, Easing.OutQuint); + contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint); + + this.FadeIn(100, Easing.OutQuint); + } + + protected override void PopOut() + { + this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); + contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In); + + foreach (var w in wavesContainer.Children) + w.State = Visibility.Hidden; + + this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // This is done as an optimization, such that invisible parts of the waves + // are masked away, and thus do not consume fill rate. + wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y)); + } + + private class Wave : VisibilityContainer + { + public float FinalPosition; + + protected override bool StartHidden => true; + + public Wave() + { + RelativeSizeAxes = Axes.X; + Width = 1.5f; + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(50), + Radius = 20f, + }; + + Child = new Box { RelativeSizeAxes = Axes.Both }; + } + + protected override void Update() + { + base.Update(); + + // We can not use RelativeSizeAxes for Height, because the height + // of our parent diminishes as the content moves up. + Height = Parent.Parent.DrawSize.Y * 1.5f; + } + + protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show); + protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index f0f8a6ef10..448ff2d7c9 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -40,10 +40,10 @@ namespace osu.Game.Overlays public BeatmapSetOverlay() { - FirstWaveColour = OsuColour.Gray(0.4f); - SecondWaveColour = OsuColour.Gray(0.3f); - ThirdWaveColour = OsuColour.Gray(0.2f); - FourthWaveColour = OsuColour.Gray(0.1f); + Waves.FirstWaveColour = OsuColour.Gray(0.4f); + Waves.SecondWaveColour = OsuColour.Gray(0.3f); + Waves.ThirdWaveColour = OsuColour.Gray(0.2f); + Waves.FourthWaveColour = OsuColour.Gray(0.1f); Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; @@ -123,14 +123,14 @@ namespace osu.Game.Overlays protected override void PopIn() { base.PopIn(); - FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In); + FadeEdgeEffectTo(0.25f, WaveContainer.APPEAR_DURATION, Easing.In); } protected override void PopOut() { base.PopOut(); header.Details.StopPreview(); - FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); + FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } protected override bool OnClick(InputState state) diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 3f1aa04c36..bfd2d94287 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -89,10 +89,10 @@ namespace osu.Game.Overlays // osu!direct colours are not part of the standard palette - FirstWaveColour = OsuColour.FromHex(@"19b0e2"); - SecondWaveColour = OsuColour.FromHex(@"2280a2"); - ThirdWaveColour = OsuColour.FromHex(@"005774"); - FourthWaveColour = OsuColour.FromHex(@"003a4e"); + Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2"); + Waves.SecondWaveColour = OsuColour.FromHex(@"2280a2"); + Waves.ThirdWaveColour = OsuColour.FromHex(@"005774"); + Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e"); ScrollFlow.Children = new Drawable[] { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d8c95da94f..bf91d3f508 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -18,6 +18,7 @@ using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; using osu.Game.Rulesets; using osu.Game.Graphics.UserInterface; @@ -113,14 +114,14 @@ namespace osu.Game.Overlays.Mods { base.PopOut(); - footerContainer.MoveToX(footerContainer.DrawSize.X, DISAPPEAR_DURATION, Easing.InSine); - footerContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine); + footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); foreach (ModSection section in ModSectionsContainer.Children) { - section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.MoveToX(100f, DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); } } @@ -128,14 +129,14 @@ namespace osu.Game.Overlays.Mods { base.PopIn(); - footerContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint); - footerContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint); + footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); foreach (ModSection section in ModSectionsContainer.Children) { - section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); } } @@ -181,14 +182,12 @@ namespace osu.Game.Overlays.Mods public ModSelectOverlay() { - FirstWaveColour = OsuColour.FromHex(@"19b0e2"); - SecondWaveColour = OsuColour.FromHex(@"2280a2"); - ThirdWaveColour = OsuColour.FromHex(@"005774"); - FourthWaveColour = OsuColour.FromHex(@"003a4e"); + Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2"); + Waves.SecondWaveColour = OsuColour.FromHex(@"2280a2"); + Waves.ThirdWaveColour = OsuColour.FromHex(@"005774"); + Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e"); Height = 510; - Content.RelativeSizeAxes = Axes.X; - Content.AutoSizeAxes = Axes.Y; Children = new Drawable[] { new Container diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs index ddcb933e5d..295c2965a0 100644 --- a/osu.Game/Overlays/SocialOverlay.cs +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -48,10 +48,10 @@ namespace osu.Game.Overlays public SocialOverlay() { - FirstWaveColour = OsuColour.FromHex(@"cb5fa0"); - SecondWaveColour = OsuColour.FromHex(@"b04384"); - ThirdWaveColour = OsuColour.FromHex(@"9b2b6e"); - FourthWaveColour = OsuColour.FromHex(@"6d214d"); + Waves.FirstWaveColour = OsuColour.FromHex(@"cb5fa0"); + Waves.SecondWaveColour = OsuColour.FromHex(@"b04384"); + Waves.ThirdWaveColour = OsuColour.FromHex(@"9b2b6e"); + Waves.FourthWaveColour = OsuColour.FromHex(@"6d214d"); Add(loading = new LoadingAnimation()); diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index aed0a6d7c6..08646cd044 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -35,10 +35,10 @@ namespace osu.Game.Overlays public UserProfileOverlay() { - FirstWaveColour = OsuColour.Gray(0.4f); - SecondWaveColour = OsuColour.Gray(0.3f); - ThirdWaveColour = OsuColour.Gray(0.2f); - FourthWaveColour = OsuColour.Gray(0.1f); + Waves.FirstWaveColour = OsuColour.Gray(0.4f); + Waves.SecondWaveColour = OsuColour.Gray(0.3f); + Waves.ThirdWaveColour = OsuColour.Gray(0.2f); + Waves.FourthWaveColour = OsuColour.Gray(0.1f); RelativeSizeAxes = Axes.Both; RelativePositionAxes = Axes.Both; @@ -64,13 +64,13 @@ namespace osu.Game.Overlays protected override void PopIn() { base.PopIn(); - FadeEdgeEffectTo(0.5f, APPEAR_DURATION, Easing.In); + FadeEdgeEffectTo(0.5f, WaveContainer.APPEAR_DURATION, Easing.In); } protected override void PopOut() { base.PopOut(); - FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); + FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } public void ShowUser(long userId) diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index 074d83a5ad..8a9065eccb 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -1,203 +1,37 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; -using System; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays { public abstract class WaveOverlayContainer : OsuFocusedOverlayContainer { - protected const float APPEAR_DURATION = 800; - protected const float DISAPPEAR_DURATION = 500; - - private const Easing easing_show = Easing.OutSine; - private const Easing easing_hide = Easing.InSine; - - private readonly Wave firstWave; - private readonly Wave secondWave; - private readonly Wave thirdWave; - private readonly Wave fourthWave; - - private readonly Container wavesContainer; - - private readonly Container contentContainer; + protected readonly WaveContainer Waves; protected override bool BlockPassThroughKeyboard => true; - - protected override Container Content => contentContainer; - - protected Color4 FirstWaveColour - { - get - { - return firstWave.Colour; - } - set - { - if (firstWave.Colour == value) return; - firstWave.Colour = value; - } - } - - protected Color4 SecondWaveColour - { - get - { - return secondWave.Colour; - } - set - { - if (secondWave.Colour == value) return; - secondWave.Colour = value; - } - } - - protected Color4 ThirdWaveColour - { - get - { - return thirdWave.Colour; - } - set - { - if (thirdWave.Colour == value) return; - thirdWave.Colour = value; - } - } - - protected Color4 FourthWaveColour - { - get - { - return fourthWave.Colour; - } - set - { - if (fourthWave.Colour == value) return; - fourthWave.Colour = value; - } - } + protected override Container Content => Waves; protected WaveOverlayContainer() { - Masking = true; - - AddInternal(wavesContainer = new Container - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Masking = true, - Children = new[] - { - firstWave = new Wave - { - Rotation = 13, - FinalPosition = -930, - }, - secondWave = new Wave - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Rotation = -7, - FinalPosition = -560, - }, - thirdWave = new Wave - { - Rotation = 4, - FinalPosition = -390, - }, - fourthWave = new Wave - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Rotation = -2, - FinalPosition = -220, - }, - }, - }); - - AddInternal(contentContainer = new Container + AddInternal(Waves = new WaveContainer { RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, }); } protected override void PopIn() { base.PopIn(); - - foreach (var w in wavesContainer.Children) - w.State = Visibility.Visible; - - this.FadeIn(100, Easing.OutQuint); - contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint); - - this.FadeIn(100, Easing.OutQuint); + Waves.Show(); } protected override void PopOut() { base.PopOut(); - - this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); - contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In); - - foreach (var w in wavesContainer.Children) - w.State = Visibility.Hidden; - - this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - // This is done as an optimization, such that invisible parts of the waves - // are masked away, and thus do not consume fill rate. - wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y)); - } - - private class Wave : VisibilityContainer - { - public float FinalPosition; - - protected override bool StartHidden => true; - - public Wave() - { - RelativeSizeAxes = Axes.X; - Width = 1.5f; - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(50), - Radius = 20f, - }; - - Child = new Box { RelativeSizeAxes = Axes.Both }; - } - - protected override void Update() - { - base.Update(); - - // We can not use RelativeSizeAxes for Height, because the height - // of our parent diminishes as the content moves up. - Height = Parent.Parent.DrawSize.Y * 1.5f; - } - - protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show); - protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide); + Waves.Hide(); } } } From fbc50d6030a0c9d66d3096509cbfff473b7bcc78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Apr 2018 13:35:53 +0900 Subject: [PATCH 433/537] Merge master into editor-clock --- .../Properties/AssemblyInfo.cs | 3 +- .../Properties/AssemblyInfo.cs | 3 +- .../Selection/Overlays/HitCircleMask.cs | 2 + .../Selection/Overlays/SliderCircleMask.cs | 5 + .../Layers/Selection/Overlays/SliderMask.cs | 4 + .../Objects/Drawables/DrawableSlider.cs | 4 - osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 3 +- .../Properties/AssemblyInfo.cs | 3 +- .../Properties/AssemblyInfo.cs | 3 +- osu.Game.Tests/Visual/TestCaseCursors.cs | 129 +++++----- ...nLayer.cs => TestCaseHitObjectComposer.cs} | 23 +- osu.Game/Database/ArchiveModelManager.cs | 2 + osu.Game/Overlays/BeatmapSet/Header.cs | 4 +- osu.Game/Overlays/DirectOverlay.cs | 12 +- osu.Game/Overlays/Music/PlaylistOverlay.cs | 18 +- .../Overlays/Settings/Sections/SkinSection.cs | 24 +- osu.Game/Properties/AssemblyInfo.cs | 11 + osu.Game/Rulesets/Edit/HitObjectComposer.cs | 22 +- osu.Game/Rulesets/Edit/HitObjectMask.cs | 127 ++++++++- .../Objects/Drawables/DrawableHitObject.cs | 12 - .../Edit/Screens/Compose/Layers/DragLayer.cs | 92 +++++++ .../Compose/Layers/HitObjectMaskLayer.cs | 87 ++++--- .../Screens/Compose/Layers/MaskContainer.cs | 123 +++++++++ .../Screens/Compose/Layers/MaskSelection.cs | 164 ++++++++++++ .../Screens/Compose/Layers/SelectionBox.cs | 102 -------- .../Screens/Compose/Layers/SelectionLayer.cs | 240 ------------------ .../Visual/ManualInputManagerTestCase.cs | 29 +++ 27 files changed, 746 insertions(+), 505 deletions(-) rename osu.Game.Tests/Visual/{TestCaseEditorSelectionLayer.cs => TestCaseHitObjectComposer.cs} (71%) create mode 100644 osu.Game/Properties/AssemblyInfo.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs delete mode 100644 osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs delete mode 100644 osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs create mode 100644 osu.Game/Tests/Visual/ManualInputManagerTestCase.cs diff --git a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs index 00fd8247d8..fed1013ae1 100644 --- a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs @@ -2,11 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; -using osu.Framework.Testing; // We publish our internal attributes to other sub-projects of the framework. // Note, that we omit visual tests as they are meant to test the framework // behavior "in the wild". [assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")] -[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs index c2c65433ec..515aeab9df 100644 --- a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs @@ -2,11 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; -using osu.Framework.Testing; // We publish our internal attributes to other sub-projects of the framework. // Note, that we omit visual tests as they are meant to test the framework // behavior "in the wild". [assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")] -[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs index b48dd73bb5..89a7686581 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays Size = hitCircle.Size; Scale = hitCircle.Scale; + CornerRadius = Size.X / 2; + AddInternal(new RingPiece()); hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position; diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs index 586b516a11..96ff14205e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs @@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays Scale = slider.HeadCircle.Scale; AddInternal(new RingPiece()); + + Select(); } [BackgroundDependencyLoader] @@ -52,5 +54,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays RelativeAnchorPosition = hitObject.RelativeAnchorPosition; } + + // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input. + public override bool HandleMouseInput => false; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs index 53f02617cd..629bce1847 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects; @@ -59,5 +60,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays } public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos); + + public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition); + public override Quad SelectionQuad => body.PathDrawQuad; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 3872821b96..5373926138 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -10,7 +10,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; -using osu.Framework.Graphics.Primitives; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; using OpenTK.Graphics; @@ -177,8 +176,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public Drawable ProxiedLayer => HeadCircle.ApproachCircle; public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos); - - public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition); - public override Quad SelectionQuad => Body.PathDrawQuad; } } diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index c00c30ced9..f64db6ba9e 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -7,10 +7,11 @@ using osu.Game.Rulesets.Objects; using OpenTK; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit.Types; namespace osu.Game.Rulesets.Osu.Objects { - public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition + public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasEditablePosition { public const double OBJECT_RADIUS = 64; diff --git a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs index 7532646a32..ea2c2c6729 100644 --- a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs @@ -2,11 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; -using osu.Framework.Testing; // We publish our internal attributes to other sub-projects of the framework. // Note, that we omit visual tests as they are meant to test the framework // behavior "in the wild". [assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")] -[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs index b7ed9f86b0..77218af5e1 100644 --- a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs @@ -2,11 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Runtime.CompilerServices; -using osu.Framework.Testing; // We publish our internal attributes to other sub-projects of the framework. // Note, that we omit visual tests as they are meant to test the framework // behavior "in the wild". [assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")] -[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Dynamic")] diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs index 72e699c54b..4f4fdbeb5b 100644 --- a/osu.Game.Tests/Visual/TestCaseCursors.cs +++ b/osu.Game.Tests/Visual/TestCaseCursors.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.MathUtils; -using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; using OpenTK; @@ -18,88 +17,74 @@ using OpenTK.Graphics; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseCursors : OsuTestCase + public class TestCaseCursors : ManualInputManagerTestCase { - private readonly ManualInputManager inputManager; private readonly CursorOverrideContainer cursorOverrideContainer; private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; public TestCaseCursors() { - Child = inputManager = new ManualInputManager + Child = cursorOverrideContainer = new CursorOverrideContainer { - Child = cursorOverrideContainer = new CursorOverrideContainer + RelativeSizeAxes = Axes.Both, + Children = new[] { - RelativeSizeAxes = Axes.Both, - Children = new[] + // Middle user + cursorBoxes[0] = new CustomCursorBox(Color4.Green) { - // Middle user - cursorBoxes[0] = new CustomCursorBox(Color4.Green) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - }, - // Top-left user - cursorBoxes[1] = new CustomCursorBox(Color4.Blue) - { - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Bottom-right user - cursorBoxes[2] = new CustomCursorBox(Color4.Red) - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Bottom-left local - cursorBoxes[3] = new CustomCursorBox(Color4.Magenta, false) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Top-right local - cursorBoxes[4] = new CustomCursorBox(Color4.Cyan, false) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Left-local - cursorBoxes[5] = new CustomCursorBox(Color4.Yellow, false) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.2f, 1), - }, - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + // Top-left user + cursorBoxes[1] = new CustomCursorBox(Color4.Blue) + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-right user + cursorBoxes[2] = new CustomCursorBox(Color4.Red) + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-left local + cursorBoxes[3] = new CustomCursorBox(Color4.Magenta, false) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Top-right local + cursorBoxes[4] = new CustomCursorBox(Color4.Cyan, false) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Left-local + cursorBoxes[5] = new CustomCursorBox(Color4.Yellow, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.2f, 1), + }, } }; - returnUserInput(); - AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b)); testUserCursor(); testLocalCursor(); testUserCursorOverride(); testMultipleLocalCursors(); - returnUserInput(); - } - - /// - /// Returns input back to the user. - /// - private void returnUserInput() - { - AddStep("Return user input", () => inputManager.UseParentState = true); + ReturnUserInput(); } /// @@ -109,7 +94,7 @@ namespace osu.Game.Tests.Visual /// private void testUserCursor() { - AddStep("Move to green area", () => inputManager.MoveMouseTo(cursorBoxes[0])); + AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); AddStep("Move out", moveOut); @@ -124,7 +109,7 @@ namespace osu.Game.Tests.Visual /// private void testLocalCursor() { - AddStep("Move to purple area", () => inputManager.MoveMouseTo(cursorBoxes[3])); + AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3])); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); @@ -141,7 +126,7 @@ namespace osu.Game.Tests.Visual /// private void testUserCursorOverride() { - AddStep("Move to blue-green boundary", () => inputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddStep("Move to blue-green boundary", () => InputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); @@ -156,7 +141,7 @@ namespace osu.Game.Tests.Visual /// private void testMultipleLocalCursors() { - AddStep("Move to yellow-purple boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddStep("Move to yellow-purple boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); @@ -172,7 +157,7 @@ namespace osu.Game.Tests.Visual /// private void testUserOverrideWithLocal() { - AddStep("Move to yellow-blue boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); + AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); @@ -186,7 +171,7 @@ namespace osu.Game.Tests.Visual /// Moves the cursor to a point not covered by any cursor containers. /// private void moveOut() - => inputManager.MoveMouseTo(new Vector2(inputManager.ScreenSpaceDrawQuad.Centre.X, inputManager.ScreenSpaceDrawQuad.TopLeft.Y)); + => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.Centre.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y)); /// /// Checks if a cursor is visible. @@ -199,7 +184,7 @@ namespace osu.Game.Tests.Visual /// /// The cursor to check. private bool checkAtMouse(CursorContainer cursorContainer) - => Precision.AlmostEquals(inputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); + => Precision.AlmostEquals(InputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); private class CustomCursorBox : Container, IProvideCursor { diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs similarity index 71% rename from osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs rename to osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs index 24f5905131..72d60d8e01 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs @@ -3,15 +3,16 @@ using System; using System.Collections.Generic; +using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Timing; using OpenTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Tests.Beatmaps; @@ -19,21 +20,23 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseEditorSelectionLayer : EditorClockTestCase + public class TestCaseHitObjectComposer : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { - typeof(SelectionLayer), - typeof(SelectionBox), + typeof(MaskSelection), + typeof(DragLayer), typeof(HitObjectComposer), typeof(OsuHitObjectComposer), typeof(HitObjectMaskLayer), - typeof(HitObjectMask), - typeof(HitCircleMask), - typeof(SliderMask), - typeof(SliderCircleMask) + typeof(NotNullAttribute) }; + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { @@ -59,6 +62,10 @@ namespace osu.Game.Tests.Visual }, }); + var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); + Child = new OsuHitObjectComposer(new OsuRuleset()); } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 7050e34712..3afb22f0ad 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -35,11 +35,13 @@ namespace osu.Game.Database /// /// Fired when a new becomes available in the database. + /// This is not guaranteed to run on the update thread. /// public event Action ItemAdded; /// /// Fired when a is removed from the database. + /// This is not guaranteed to run on the update thread. /// public event Action ItemRemoved; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index b9a35ec1f0..8056dbff3c 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -246,11 +246,11 @@ namespace osu.Game.Overlays.BeatmapSet if (beatmaps != null) beatmaps.ItemAdded -= handleBeatmapAdd; } - private void handleBeatmapAdd(BeatmapSetInfo beatmap) + private void handleBeatmapAdd(BeatmapSetInfo beatmap) => Schedule(() => { if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID) downloadButtonsContainer.FadeOut(transition_duration); - } + }); private void download(bool noVideo) { diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 8d8a4aebaa..3f1aa04c36 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -188,12 +188,12 @@ namespace osu.Game.Overlays beatmaps.ItemAdded += setAdded; } - private void setAdded(BeatmapSetInfo set) + private void setAdded(BeatmapSetInfo set) => Schedule(() => { // if a new map was imported, we should remove it from search results (download completed etc.) panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire(); BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID); - } + }); private void updateResultCounts() { @@ -323,6 +323,14 @@ namespace osu.Game.Overlays private int distinctCount(List list) => list.Distinct().ToArray().Length; + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmaps != null) + beatmaps.ItemAdded -= setAdded; + } + public class ResultCounts { public readonly int Artists; diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index ac7ec6257b..c981e5f493 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -74,8 +74,8 @@ namespace osu.Game.Overlays.Music }, }; - beatmaps.ItemAdded += list.AddBeatmapSet; - beatmaps.ItemRemoved += list.RemoveBeatmapSet; + beatmaps.ItemAdded += handleBeatmapAdded; + beatmaps.ItemRemoved += handleBeatmapRemoved; list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); @@ -95,6 +95,9 @@ namespace osu.Game.Overlays.Music beatmapBacking.TriggerChange(); } + private void handleBeatmapAdded(BeatmapSetInfo setInfo) => Schedule(() => list.AddBeatmapSet(setInfo)); + private void handleBeatmapRemoved(BeatmapSetInfo setInfo) => Schedule(() => list.RemoveBeatmapSet(setInfo)); + protected override void PopIn() { filter.Search.HoldFocus = true; @@ -153,6 +156,17 @@ namespace osu.Game.Overlays.Music track.Restart(); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmaps != null) + { + beatmaps.ItemAdded -= handleBeatmapAdded; + beatmaps.ItemRemoved -= handleBeatmapRemoved; + } + } } //todo: placeholder diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 5df5304751..a2215035dd 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -21,9 +21,13 @@ namespace osu.Game.Overlays.Settings.Sections public override FontAwesome Icon => FontAwesome.fa_paint_brush; + private SkinManager skins; + [BackgroundDependencyLoader] private void load(OsuConfigManager config, SkinManager skins) { + this.skins = skins; + FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { @@ -47,15 +51,29 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); - skins.ItemAdded += _ => reloadSkins(); - skins.ItemRemoved += _ => reloadSkins(); + skins.ItemAdded += onItemsChanged; + skins.ItemRemoved += onItemsChanged; reloadSkins(); skinDropdown.Bindable = config.GetBindable(OsuSetting.Skin); } + private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); + + private void onItemsChanged(SkinInfo _) => Schedule(reloadSkins); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (skins != null) + { + skins.ItemAdded -= onItemsChanged; + skins.ItemRemoved -= onItemsChanged; + } + } + private class SizeSlider : OsuSliderBar { public override string TooltipText => Current.Value.ToString(@"0.##x"); diff --git a/osu.Game/Properties/AssemblyInfo.cs b/osu.Game/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..9384740308 --- /dev/null +++ b/osu.Game/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Runtime.CompilerServices; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Tests")] +[assembly: InternalsVisibleTo("osu.Game.Tests.Dynamic")] diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 9d0246b2fc..1e7416e63d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -53,9 +53,6 @@ namespace osu.Game.Rulesets.Edit return; } - HitObjectMaskLayer hitObjectMaskLayer = new HitObjectMaskLayer(this); - SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield); - var layerBelowRuleset = new BorderLayer { RelativeSizeAxes = Axes.Both, @@ -63,12 +60,7 @@ namespace osu.Game.Rulesets.Edit }; var layerAboveRuleset = CreateLayerContainer(); - layerAboveRuleset.Children = new Drawable[] - { - selectionLayer, // Below object overlays for input - hitObjectMaskLayer, - selectionLayer.CreateProxy() // Proxy above object overlays for selections - }; + layerAboveRuleset.Child = new HitObjectMaskLayer(rulesetContainer.Playfield, this); layerContainers.Add(layerBelowRuleset); layerContainers.Add(layerAboveRuleset); @@ -110,11 +102,6 @@ namespace osu.Game.Rulesets.Edit } }; - selectionLayer.ObjectSelected += hitObjectMaskLayer.AddOverlay; - selectionLayer.ObjectDeselected += hitObjectMaskLayer.RemoveOverlay; - selectionLayer.SelectionCleared += hitObjectMaskLayer.RemoveSelectionOverlay; - selectionLayer.SelectionFinished += hitObjectMaskLayer.AddSelectionOverlay; - toolboxCollection.Items = CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) .Prepend(new RadioButton("Select", () => setCompositionTool(null))) @@ -149,11 +136,10 @@ namespace osu.Game.Rulesets.Edit public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null; /// - /// Creates a which outlines s - /// and handles all hitobject movement/pattern adjustments. + /// Creates a which outlines s + /// and handles hitobject pattern adjustments. /// - /// The overlays. - public virtual SelectionBox CreateSelectionOverlay(IReadOnlyList overlays) => new SelectionBox(overlays); + public virtual MaskSelection CreateMaskSelection() => new MaskSelection(); /// /// Creates a which provides a layer above or below the . diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index 051b42fec6..9f055ffc5d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -1,21 +1,146 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using osu.Framework; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Input; using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; namespace osu.Game.Rulesets.Edit { /// /// A mask placed above a adding editing functionality. /// - public class HitObjectMask : Container + public class HitObjectMask : CompositeDrawable, IStateful { + /// + /// Invoked when this has been selected. + /// + public event Action Selected; + + /// + /// Invoked when this has been deselected. + /// + public event Action Deselected; + + /// + /// Invoked when this has requested selection. + /// Will fire even if already selected. Does not actually perform selection. + /// + public event Action SelectionRequested; + + /// + /// Invoked when this has requested drag. + /// + public event Action DragRequested; + + /// + /// The which this applies to. + /// public readonly DrawableHitObject HitObject; + protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == SelectionState.Selected; + public override bool HandleMouseInput => ShouldBeAlive; + public override bool RemoveWhenNotAlive => false; + public HitObjectMask(DrawableHitObject hitObject) { HitObject = hitObject; + + AlwaysPresent = true; + Alpha = 0; } + + private SelectionState state; + + public event Action StateChanged; + + public SelectionState State + { + get => state; + set + { + if (state == value) return; + + state = value; + switch (state) + { + case SelectionState.Selected: + Show(); + Selected?.Invoke(this); + break; + case SelectionState.NotSelected: + Hide(); + Deselected?.Invoke(this); + break; + } + } + } + + /// + /// Selects this , causing it to become visible. + /// + public void Select() => State = SelectionState.Selected; + + /// + /// Deselects this , causing it to become invisible. + /// + public void Deselect() => State = SelectionState.NotSelected; + + public bool IsSelected => State == SelectionState.Selected; + + private bool selectionRequested; + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + selectionRequested = false; + + if (State == SelectionState.NotSelected) + { + SelectionRequested?.Invoke(this, state); + selectionRequested = true; + } + + return IsSelected; + } + + protected override bool OnClick(InputState state) + { + if (State == SelectionState.Selected && !selectionRequested) + { + selectionRequested = true; + SelectionRequested?.Invoke(this, state); + return true; + } + + return base.OnClick(state); + } + + protected override bool OnDragStart(InputState state) => true; + + protected override bool OnDrag(InputState state) + { + DragRequested?.Invoke(this, state); + return true; + } + + /// + /// The screen-space point that causes this to be selected. + /// + public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre; + + /// + /// The screen-space quad that outlines this for selections. + /// + public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; + } + + public enum SelectionState + { + NotSelected, + Selected } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 348364a2bf..fdfef14a88 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -6,14 +6,12 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Graphics.Primitives; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; -using OpenTK; using OpenTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables @@ -231,16 +229,6 @@ namespace osu.Game.Rulesets.Objects.Drawables protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) { } - - /// - /// The screen-space point that causes this to be selected in the Editor. - /// - public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre; - - /// - /// The screen-space quad that outlines this for selections in the Editor. - /// - public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; } public abstract class DrawableHitObject : DrawableHitObject diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs new file mode 100644 index 0000000000..51bb61b607 --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs @@ -0,0 +1,92 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +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; +using osu.Game.Rulesets.Edit; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + /// + /// A layer that handles and displays drag selection for a collection of s. + /// + public class DragLayer : CompositeDrawable + { + private readonly Action performSelection; + + /// + /// Invoked when the drag selection has finished. + /// + public event Action DragEnd; + + private Drawable box; + + /// + /// Creates a new . + /// + /// The selectable s. + public DragLayer(Action performSelection) + { + this.performSelection = performSelection; + + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + Alpha = 0; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = box = new Container + { + Masking = true, + BorderColour = Color4.White, + BorderThickness = MaskSelection.BORDER_RADIUS, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f + } + }; + } + + protected override bool OnDragStart(InputState state) + { + this.FadeIn(250, Easing.OutQuint); + return true; + } + + protected override bool OnDrag(InputState state) + { + var dragPosition = state.Mouse.NativeState.Position; + var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition; + + var dragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y); + + // We use AABBFloat instead of RectangleF since it handles negative sizes for us + var dragRectangle = dragQuad.AABBFloat; + + var topLeft = ToLocalSpace(dragRectangle.TopLeft); + var bottomRight = ToLocalSpace(dragRectangle.BottomRight); + + box.Position = topLeft; + box.Size = bottomRight - topLeft; + + performSelection?.Invoke(dragRectangle); + return true; + } + + protected override bool OnDragEnd(InputState state) + { + this.FadeOut(250, Easing.OutQuint); + DragEnd?.Invoke(); + return true; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 46b09e2c23..423cf0ed29 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -2,65 +2,92 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; namespace osu.Game.Screens.Edit.Screens.Compose.Layers { public class HitObjectMaskLayer : CompositeDrawable { + private readonly Playfield playfield; private readonly HitObjectComposer composer; - private readonly Container overlayContainer; - public HitObjectMaskLayer(HitObjectComposer composer) + private MaskContainer maskContainer; + + public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer) { + // we need the playfield as HitObjects may not be initialised until its BDL. + this.playfield = playfield; + this.composer = composer; + RelativeSizeAxes = Axes.Both; + } - InternalChild = overlayContainer = new Container { RelativeSizeAxes = Axes.Both }; + [BackgroundDependencyLoader] + private void load() + { + maskContainer = new MaskContainer(); + + var maskSelection = composer.CreateMaskSelection(); + + maskContainer.MaskSelected += maskSelection.HandleSelected; + maskContainer.MaskDeselected += maskSelection.HandleDeselected; + maskContainer.MaskSelectionRequested += maskSelection.HandleSelectionRequested; + maskContainer.MaskDragRequested += maskSelection.HandleDrag; + + maskSelection.DeselectAll = maskContainer.DeselectAll; + + var dragLayer = new DragLayer(maskContainer.Select); + dragLayer.DragEnd += () => maskSelection.UpdateVisibility(); + + InternalChildren = new Drawable[] + { + dragLayer, + maskSelection, + maskContainer, + dragLayer.CreateProxy() + }; + + foreach (var obj in playfield.HitObjects.Objects) + addMask(obj); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + maskContainer.DeselectAll(); + return true; } /// - /// Adds an overlay for a which adds movement support. + /// Adds a mask for a which adds movement support. /// - /// The to create an overlay for. - public void AddOverlay(DrawableHitObject hitObject) + /// The to create a mask for. + private void addMask(DrawableHitObject hitObject) { - var overlay = composer.CreateMaskFor(hitObject); - if (overlay == null) + var mask = composer.CreateMaskFor(hitObject); + if (mask == null) return; - overlayContainer.Add(overlay); + maskContainer.Add(mask); } /// - /// Removes the overlay for a . + /// Removes the mask for a . /// - /// The to remove the overlay for. - public void RemoveOverlay(DrawableHitObject hitObject) + /// The to remove the mask for. + private void removeMask(DrawableHitObject hitObject) { - var existing = overlayContainer.FirstOrDefault(h => h.HitObject == hitObject); - if (existing == null) + var mask = maskContainer.FirstOrDefault(h => h.HitObject == hitObject); + if (mask == null) return; - existing.Hide(); - existing.Expire(); - } - - private SelectionBox currentSelectionBox; - - public void AddSelectionOverlay() - { - if (overlayContainer.Count > 0) - AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer)); - } - - public void RemoveSelectionOverlay() - { - currentSelectionBox?.Hide(); - currentSelectionBox?.Expire(); + maskContainer.Remove(mask); } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs new file mode 100644 index 0000000000..b631628c9e --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -0,0 +1,123 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Rulesets.Edit; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + public class MaskContainer : Container + { + /// + /// Invoked when any is selected. + /// + public event Action MaskSelected; + + /// + /// Invoked when any is deselected. + /// + public event Action MaskDeselected; + + /// + /// Invoked when any requests selection. + /// + public event Action MaskSelectionRequested; + + /// + /// Invoked when any requests drag. + /// + public event Action MaskDragRequested; + + private IEnumerable aliveMasks => AliveInternalChildren.Cast(); + + public MaskContainer() + { + RelativeSizeAxes = Axes.Both; + } + + public override void Add(HitObjectMask drawable) + { + base.Add(drawable); + + drawable.Selected += onMaskSelected; + drawable.Deselected += onMaskDeselected; + drawable.SelectionRequested += onSelectionRequested; + drawable.DragRequested += onDragRequested; + } + + public override bool Remove(HitObjectMask drawable) + { + var result = base.Remove(drawable); + + if (result) + { + drawable.Selected -= onMaskSelected; + drawable.Deselected -= onMaskDeselected; + drawable.SelectionRequested -= onSelectionRequested; + drawable.DragRequested -= onDragRequested; + } + + return result; + } + + /// + /// Select all masks in a given rectangle selection area. + /// + /// The rectangle to perform a selection on in screen-space coordinates. + public void Select(RectangleF rect) + { + foreach (var mask in aliveMasks.ToList()) + { + if (mask.IsPresent && rect.Contains(mask.SelectionPoint)) + mask.Select(); + else + mask.Deselect(); + } + } + + /// + /// Deselects all selected s. + /// + public void DeselectAll() => aliveMasks.ToList().ForEach(m => m.Deselect()); + + private void onMaskSelected(HitObjectMask mask) + { + MaskSelected?.Invoke(mask); + ChangeChildDepth(mask, 1); + } + + private void onMaskDeselected(HitObjectMask mask) + { + MaskDeselected?.Invoke(mask); + ChangeChildDepth(mask, 0); + } + + private void onSelectionRequested(HitObjectMask mask, InputState state) => MaskSelectionRequested?.Invoke(mask, state); + private void onDragRequested(HitObjectMask mask, InputState state) => MaskDragRequested?.Invoke(mask, state); + + protected override int Compare(Drawable x, Drawable y) + { + if (!(x is HitObjectMask xMask) || !(y is HitObjectMask yMask)) + return base.Compare(x, y); + return Compare(xMask, yMask); + } + + public int Compare(HitObjectMask x, HitObjectMask y) + { + // dpeth is used to denote selected status (we always want selected masks to handle input first). + int d = x.Depth.CompareTo(y.Depth); + if (d != 0) + return d; + + // Put earlier hitobjects towards the end of the list, so they handle input first + int i = y.HitObject.HitObject.StartTime.CompareTo(x.HitObject.HitObject.StartTime); + return i == 0 ? CompareReverseChildID(x, y) : i; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs new file mode 100644 index 0000000000..76b8027b07 --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -0,0 +1,164 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Types; +using OpenTK; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + /// + /// A box which surrounds s and provides interactive handles, context menus etc. + /// + public class MaskSelection : CompositeDrawable + { + public const float BORDER_RADIUS = 2; + + private readonly List selectedMasks; + + private Drawable outline; + + public MaskSelection() + { + selectedMasks = new List(); + + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + Alpha = 0; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChild = outline = new Container + { + Masking = true, + BorderThickness = BORDER_RADIUS, + BorderColour = colours.Yellow, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + AlwaysPresent = true, + Alpha = 0 + } + }; + } + + #region User Input Handling + + public void HandleDrag(HitObjectMask m, InputState state) + { + // Todo: Various forms of snapping + + foreach (var mask in selectedMasks) + { + switch (mask.HitObject.HitObject) + { + case IHasEditablePosition editablePosition: + editablePosition.OffsetPosition(state.Mouse.Delta); + break; + } + } + } + + #endregion + + #region Selection Handling + + /// + /// Bind an action to deselect all selected masks. + /// + public Action DeselectAll { private get; set; } + + /// + /// Handle a mask becoming selected. + /// + /// The mask. + public void HandleSelected(HitObjectMask mask) => selectedMasks.Add(mask); + + /// + /// Handle a mask becoming deselected. + /// + /// The mask. + public void HandleDeselected(HitObjectMask mask) + { + selectedMasks.Remove(mask); + + // We don't want to update visibility if > 0, since we may be deselecting masks during drag-selection + if (selectedMasks.Count == 0) + UpdateVisibility(); + } + + /// + /// Handle a mask requesting selection. + /// + /// The mask. + public void HandleSelectionRequested(HitObjectMask mask, InputState state) + { + if (state.Keyboard.ControlPressed) + { + if (mask.IsSelected) + mask.Deselect(); + else + mask.Select(); + } + else + { + if (mask.IsSelected) + return; + + DeselectAll?.Invoke(); + mask.Select(); + } + + UpdateVisibility(); + } + + #endregion + + /// + /// Updates whether this is visible. + /// + internal void UpdateVisibility() + { + if (selectedMasks.Count > 0) + Show(); + else + Hide(); + } + + protected override void Update() + { + base.Update(); + + if (selectedMasks.Count == 0) + return; + + // Move the rectangle to cover the hitobjects + var topLeft = new Vector2(float.MaxValue, float.MaxValue); + var bottomRight = new Vector2(float.MinValue, float.MinValue); + + bool hasSelection = false; + + foreach (var mask in selectedMasks) + { + topLeft = Vector2.ComponentMin(topLeft, ToLocalSpace(mask.SelectionQuad.TopLeft)); + bottomRight = Vector2.ComponentMax(bottomRight, ToLocalSpace(mask.SelectionQuad.BottomRight)); + } + + topLeft -= new Vector2(5); + bottomRight += new Vector2(5); + + outline.Size = bottomRight - topLeft; + outline.Position = topLeft; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs deleted file mode 100644 index 0e5d824559..0000000000 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Types; -using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; - -namespace osu.Game.Screens.Edit.Screens.Compose.Layers -{ - /// - /// A box which surrounds s and provides interactive handles, context menus etc. - /// - public class SelectionBox : VisibilityContainer - { - private readonly IReadOnlyList overlays; - - public const float BORDER_RADIUS = 2; - - public SelectionBox(IReadOnlyList overlays) - { - this.overlays = overlays; - - Masking = true; - BorderThickness = BORDER_RADIUS; - - InternalChild = new Box - { - RelativeSizeAxes = Axes.Both, - AlwaysPresent = true, - Alpha = 0 - }; - - State = Visibility.Visible; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BorderColour = colours.Yellow; - } - - protected override void Update() - { - base.Update(); - - // Todo: We might need to optimise this - - // Move the rectangle to cover the hitobjects - var topLeft = new Vector2(float.MaxValue, float.MaxValue); - var bottomRight = new Vector2(float.MinValue, float.MinValue); - - foreach (var obj in overlays) - { - topLeft = Vector2.ComponentMin(topLeft, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.TopLeft)); - bottomRight = Vector2.ComponentMax(bottomRight, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.BottomRight)); - } - - topLeft -= new Vector2(5); - bottomRight += new Vector2(5); - - Size = bottomRight - topLeft; - Position = topLeft; - } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => overlays.Any(o => o.ReceiveMouseInputAt(screenSpacePos)); - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; - - protected override bool OnDragStart(InputState state) => true; - - protected override bool OnDrag(InputState state) - { - // Todo: Various forms of snapping - foreach (var hitObject in overlays.Select(o => o.HitObject.HitObject)) - { - switch (hitObject) - { - case IHasEditablePosition editablePosition: - editablePosition.OffsetPosition(state.Mouse.Delta); - break; - } - } - return true; - } - - protected override bool OnDragEnd(InputState state) => true; - - public override bool DisposeOnDeathRemoval => true; - - protected override void PopIn() => this.FadeIn(); - protected override void PopOut() => this.FadeOut(); - } -} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs deleted file mode 100644 index ab51385980..0000000000 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionLayer.cs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.UI; -using OpenTK; -using OpenTK.Graphics; -using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; - -namespace osu.Game.Screens.Edit.Screens.Compose.Layers -{ - public class SelectionLayer : CompositeDrawable - { - /// - /// Invoked when a is selected. - /// - public event Action ObjectSelected; - - /// - /// Invoked when a is deselected. - /// - public event Action ObjectDeselected; - - /// - /// Invoked when the selection has been cleared. - /// - public event Action SelectionCleared; - - /// - /// Invoked when the user has finished selecting all s. - /// - public event Action SelectionFinished; - - private readonly Playfield playfield; - - public SelectionLayer(Playfield playfield) - { - this.playfield = playfield; - - RelativeSizeAxes = Axes.Both; - } - - private DragBox dragBox; - - private readonly HashSet selectedHitObjects = new HashSet(); - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - DeselectAll(); - return true; - } - - protected override bool OnDragStart(InputState state) - { - AddInternal(dragBox = new DragBox()); - return true; - } - - protected override bool OnDrag(InputState state) - { - dragBox.Show(); - - var dragPosition = state.Mouse.NativeState.Position; - var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition; - - var screenSpaceDragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y); - - dragBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat); - selectQuad(screenSpaceDragQuad); - - return true; - } - - protected override bool OnDragEnd(InputState state) - { - dragBox.Hide(); - dragBox.Expire(); - - finishSelection(); - - return true; - } - - protected override bool OnClick(InputState state) - { - selectPoint(state.Mouse.NativeState.Position); - finishSelection(); - - return true; - } - - /// - /// Selects a . - /// - /// The to select. - public void Select(DrawableHitObject hitObject) - { - if (!select(hitObject)) - return; - - clearSelection(); - finishSelection(); - } - - /// - /// Selects a without performing capture updates. - /// - /// The to select. - /// Whether was selected. - private bool select(DrawableHitObject hitObject) - { - if (!selectedHitObjects.Add(hitObject)) - return false; - - ObjectSelected?.Invoke(hitObject); - return true; - } - - /// - /// Deselects a . - /// - /// The to deselect. - public void Deselect(DrawableHitObject hitObject) - { - if (!deselect(hitObject)) - return; - - clearSelection(); - finishSelection(); - } - - /// - /// Deselects a without performing capture updates. - /// - /// The to deselect. - /// Whether the was deselected. - private bool deselect(DrawableHitObject hitObject) - { - if (!selectedHitObjects.Remove(hitObject)) - return false; - - ObjectDeselected?.Invoke(hitObject); - return true; - } - - /// - /// Deselects all selected s. - /// - public void DeselectAll() - { - selectedHitObjects.ForEach(h => ObjectDeselected?.Invoke(h)); - selectedHitObjects.Clear(); - - clearSelection(); - } - - /// - /// Selects all hitobjects that are present within the area of a . - /// - /// The selection . - // Todo: If needed we can severely reduce allocations in this method - private void selectQuad(Quad screenSpaceQuad) - { - var expectedSelection = playfield.HitObjects.Objects.Where(h => h.IsAlive && h.IsPresent && screenSpaceQuad.Contains(h.SelectionPoint)).ToList(); - - var toRemove = selectedHitObjects.Except(expectedSelection).ToList(); - foreach (var obj in toRemove) - deselect(obj); - - expectedSelection.ForEach(h => select(h)); - } - - /// - /// Selects the top-most hitobject that is present under a specific point. - /// - /// The to select at. - private void selectPoint(Vector2 screenSpacePoint) - { - var target = playfield.HitObjects.Objects.Reverse().Where(h => h.IsAlive && h.IsPresent).FirstOrDefault(h => h.ReceiveMouseInputAt(screenSpacePoint)); - if (target == null) - return; - - select(target); - } - - private void clearSelection() => SelectionCleared?.Invoke(); - - private void finishSelection() - { - if (selectedHitObjects.Count == 0) - return; - SelectionFinished?.Invoke(); - } - - /// - /// A box that represents a drag selection. - /// - private class DragBox : VisibilityContainer - { - /// - /// Creates a new . - /// - public DragBox() - { - Masking = true; - BorderColour = Color4.White; - BorderThickness = SelectionBox.BORDER_RADIUS; - - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.1f - }; - } - - public void SetDragRectangle(RectangleF rectangle) - { - var topLeft = Parent.ToLocalSpace(rectangle.TopLeft); - var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight); - - Position = topLeft; - Size = bottomRight - topLeft; - } - - public override bool DisposeOnDeathRemoval => true; - - protected override void PopIn() => this.FadeIn(250, Easing.OutQuint); - protected override void PopOut() => this.FadeOut(250, Easing.OutQuint); - } - } -} diff --git a/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs b/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs new file mode 100644 index 0000000000..c2595231c9 --- /dev/null +++ b/osu.Game/Tests/Visual/ManualInputManagerTestCase.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing.Input; + +namespace osu.Game.Tests.Visual +{ + public abstract class ManualInputManagerTestCase : OsuTestCase + { + protected override Container Content => InputManager; + protected readonly ManualInputManager InputManager; + + protected ManualInputManagerTestCase() + { + base.Content.Add(InputManager = new ManualInputManager()); + ReturnUserInput(); + } + + /// + /// Returns input back to the user. + /// + protected void ReturnUserInput() + { + AddStep("Return user input", () => InputManager.UseParentState = true); + } + } +} From a2484fbf56b316dea4cf5ddc716410432c4f5216 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Apr 2018 13:37:02 +0900 Subject: [PATCH 434/537] Move back to DI-ing adjustable clock into SummaryTimeline --- osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs | 2 +- .../Edit/Components/Timelines/Summary/SummaryTimeline.cs | 9 +-------- osu.Game/Screens/Edit/Editor.cs | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs index 13e7fb8c7d..25ea3443ba 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); SummaryTimeline summaryTimeline; - Add(summaryTimeline = new SummaryTimeline(Clock) + Add(summaryTimeline = new SummaryTimeline { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index b368b92e42..0e80c13257 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -17,15 +17,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary /// public class SummaryTimeline : BottomBarContainer { - private readonly IAdjustableClock adjustableClock; - - public SummaryTimeline(IAdjustableClock adjustableClock) - { - this.adjustableClock = adjustableClock; - } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, IAdjustableClock adjustableClock) { TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index d63ee3fe75..e6edc9a6ff 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -129,7 +129,7 @@ namespace osu.Game.Screens.Edit Padding = new MarginPadding { Right = 10 }, Child = timeInfo = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, }, - timeline = new SummaryTimeline(clock) + timeline = new SummaryTimeline { RelativeSizeAxes = Axes.Both, }, From 6e3591041945eaac73519c615dc965514e1c0dac Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Apr 2018 14:06:36 +0900 Subject: [PATCH 435/537] Remove start/stop clock logic Beatmap track shouldn't be start/stopped anyway - the IAdjustableClock should be DI'd in to perform the functionality. --- osu.Game/Tests/Visual/EditorClockTestCase.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs index 5cc03a4553..bcdaa2f412 100644 --- a/osu.Game/Tests/Visual/EditorClockTestCase.cs +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -56,13 +56,6 @@ namespace osu.Game.Tests.Visual { base.Update(); - // We don't have any explicit way to start/stop the track, but want a relatively accurate IsRunning state - // The track's IsRunning state is used to determine our clock's IsRunning state - if (osuGame.Beatmap.Value.Track.IsRunning) - Clock.Start(); - else - Clock.Stop(); - Clock.ProcessFrame(); } From e007365916ea4d25b85926d192d4fe4321e3dbba Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Apr 2018 13:31:06 +0900 Subject: [PATCH 436/537] Save OAuth token to config on every token change --- osu.Game/Online/API/APIAccess.cs | 11 ++++++----- osu.Game/Online/API/OAuth.cs | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 798ed1b9b3..d33d4741d0 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -39,8 +39,8 @@ namespace osu.Game.Online.API public string Token { - get { return authentication.Token?.ToString(); } - set { authentication.Token = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); } + get { return authentication.Token.Value?.ToString(); } + set { authentication.Token.Value = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); } } protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); @@ -59,9 +59,13 @@ namespace osu.Game.Online.API ProvidedUsername = config.Get(OsuSetting.Username); Token = config.Get(OsuSetting.Token); + authentication.Token.ValueChanged += onTokenChanged; + Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } + private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? Token : string.Empty); + private readonly List components = new List(); internal new void Schedule(Action action) => base.Schedule(action); @@ -306,9 +310,6 @@ namespace osu.Game.Online.API { base.Dispose(isDisposing); - config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? Token : string.Empty); - config.Save(); - flushQueue(); cancellationToken.Cancel(); } diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs index df8af7b8dc..eec6f0648b 100644 --- a/osu.Game/Online/API/OAuth.cs +++ b/osu.Game/Online/API/OAuth.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Diagnostics; +using osu.Framework.Configuration; using osu.Framework.IO.Network; namespace osu.Game.Online.API @@ -12,7 +13,7 @@ namespace osu.Game.Online.API private readonly string clientSecret; private readonly string endpoint; - public OAuthToken Token; + public readonly Bindable Token = new Bindable(); internal OAuth(string clientId, string clientSecret, string endpoint) { @@ -47,7 +48,7 @@ namespace osu.Game.Online.API return false; } - Token = req.ResponseObject; + Token.Value = req.ResponseObject; return true; } } @@ -66,14 +67,14 @@ namespace osu.Game.Online.API { req.Perform(); - Token = req.ResponseObject; + Token.Value = req.ResponseObject; return true; } } catch { //todo: potentially only kill the refresh token on certain exception types. - Token = null; + Token.Value = null; return false; } } @@ -95,15 +96,15 @@ namespace osu.Game.Online.API if (accessTokenValid) return true; // if not, let's try using our refresh token to request a new access token. - if (!string.IsNullOrEmpty(Token?.RefreshToken)) + if (!string.IsNullOrEmpty(Token.Value?.RefreshToken)) // ReSharper disable once PossibleNullReferenceException - AuthenticateWithRefresh(Token.RefreshToken); + AuthenticateWithRefresh(Token.Value.RefreshToken); return accessTokenValid; } } - private bool accessTokenValid => Token?.IsValid ?? false; + private bool accessTokenValid => Token.Value?.IsValid ?? false; internal bool HasValidAccessToken => RequestAccessToken() != null; @@ -111,12 +112,12 @@ namespace osu.Game.Online.API { if (!ensureAccessToken()) return null; - return Token.AccessToken; + return Token.Value.AccessToken; } internal void Clear() { - Token = null; + Token.Value = null; } private class AccessTokenRequestRefresh : AccessTokenRequest From baae4427ffd785d8045e87331686a0e8963e4d07 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Apr 2018 14:30:28 +0900 Subject: [PATCH 437/537] Move string-token property to OAuth --- osu.Game/Online/API/APIAccess.cs | 12 +++--------- osu.Game/Online/API/OAuth.cs | 6 ++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index d33d4741d0..946d13c02a 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -37,13 +37,7 @@ namespace osu.Game.Online.API public Bindable LocalUser { get; } = new Bindable(createGuestUser()); - public string Token - { - get { return authentication.Token.Value?.ToString(); } - set { authentication.Token.Value = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); } - } - - protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); + protected bool HasLogin => authentication.Token.Value != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); @@ -57,14 +51,14 @@ namespace osu.Game.Online.API log = Logger.GetLogger(LoggingTarget.Network); ProvidedUsername = config.Get(OsuSetting.Username); - Token = config.Get(OsuSetting.Token); + authentication.TokenString = config.Get(OsuSetting.Token); authentication.Token.ValueChanged += onTokenChanged; Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } - private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? Token : string.Empty); + private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty); private readonly List components = new List(); diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs index eec6f0648b..af01fc99a9 100644 --- a/osu.Game/Online/API/OAuth.cs +++ b/osu.Game/Online/API/OAuth.cs @@ -15,6 +15,12 @@ namespace osu.Game.Online.API public readonly Bindable Token = new Bindable(); + public string TokenString + { + get => Token.Value?.ToString(); + set => Token.Value = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); + } + internal OAuth(string clientId, string clientSecret, string endpoint) { Debug.Assert(clientId != null); From 461e063f1946e8459697fe7c1a4a468cf9de0f31 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 11 Apr 2018 22:50:39 -0700 Subject: [PATCH 438/537] Rename RankStatus to BeatmapSearchCategory --- osu.Game/Beatmaps/{RankStatus.cs => BeatmapSearchCategory.cs} | 2 +- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 4 ++-- osu.Game/Overlays/Direct/FilterControl.cs | 2 +- osu.Game/Overlays/DirectOverlay.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game/Beatmaps/{RankStatus.cs => BeatmapSearchCategory.cs} (89%) diff --git a/osu.Game/Beatmaps/RankStatus.cs b/osu.Game/Beatmaps/BeatmapSearchCategory.cs similarity index 89% rename from osu.Game/Beatmaps/RankStatus.cs rename to osu.Game/Beatmaps/BeatmapSearchCategory.cs index 8792c088af..fdd284376f 100644 --- a/osu.Game/Beatmaps/RankStatus.cs +++ b/osu.Game/Beatmaps/BeatmapSearchCategory.cs @@ -5,7 +5,7 @@ using System.ComponentModel; namespace osu.Game.Beatmaps { - public enum RankStatus + public enum BeatmapSearchCategory { Any = 7, [Description("Ranked & Approved")] diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index c54d0ea556..2482aae170 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -13,12 +13,12 @@ namespace osu.Game.Online.API.Requests { private readonly string query; private readonly RulesetInfo ruleset; - private readonly RankStatus rankStatus; + private readonly BeatmapSearchCategory rankStatus; private readonly DirectSortCriteria sortCriteria; private readonly SortDirection direction; private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, RankStatus rankStatus = RankStatus.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending) + public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory rankStatus = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending) { this.query = System.Uri.EscapeDataString(query); this.ruleset = ruleset; diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 84a09547aa..e861326b27 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -15,7 +15,7 @@ using osu.Game.Rulesets; namespace osu.Game.Overlays.Direct { - public class FilterControl : SearchableListFilterControl + public class FilterControl : SearchableListFilterControl { public readonly Bindable Ruleset = new Bindable(); private FillFlowContainer modeButtons; diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 8d8a4aebaa..208b237576 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -22,7 +22,7 @@ using OpenTK.Graphics; namespace osu.Game.Overlays { - public class DirectOverlay : SearchableListOverlay + public class DirectOverlay : SearchableListOverlay { private const float panel_padding = 10f; @@ -40,7 +40,7 @@ namespace osu.Game.Overlays protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265"); protected override SearchableListHeader CreateHeader() => new Header(); - protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); + protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); private IEnumerable beatmapSets; From 0ca703beaa77c7283770dcb89cd7c0f6360ca4bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Apr 2018 21:04:45 +0900 Subject: [PATCH 439/537] Add some missing xmldoc --- osu.Game/Screens/Edit/EditorClock.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index a084096c3b..abb677cd8d 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -10,6 +10,9 @@ using osu.Game.Screens.Edit.Screens.Compose; namespace osu.Game.Screens.Edit { + /// + /// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor. + /// public class EditorClock : DecoupleableInterpolatingFramedClock { public ControlPointInfo ControlPointInfo; @@ -23,6 +26,11 @@ namespace osu.Game.Screens.Edit ControlPointInfo = controlPointInfo; } + /// + /// Seek to the closest valid snap value. + /// + /// The raw position which should be seeked around. + /// Whether the seek could be performed. public bool SeekSnapped(double position) { var timingPoint = ControlPointInfo.TimingPointAt(position); From 127f0d7b01373484b9d2a2caa280d29875a1f9ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Apr 2018 21:17:17 +0900 Subject: [PATCH 440/537] Fix smoogipoo's comments --- osu.Game/Screens/Edit/EditorClock.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index abb677cd8d..874fd186f8 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Edit } /// - /// Seek to the closest valid snap value. + /// Seek to the closest snappable beat from a time. /// /// The raw position which should be seeked around. /// Whether the seek could be performed. @@ -53,15 +53,15 @@ namespace osu.Game.Screens.Edit } /// - /// Seeks the current time one beat-snapped beat-length backwards. + /// Seeks backwards by one beat length. /// - /// Whether to snap to the closest beat. + /// Whether to snap to the closest beat after seeking. public void SeekBackward(bool snapped = false) => seek(-1, snapped); /// - /// Seeks the current time one beat-snapped beat-length forwards. + /// Seeks forwards by one beat length. /// - /// Whether to snap to the closest beat. + /// Whether to snap to the closest beat after seeking. public void SeekForward(bool snapped = false) => seek(1, snapped); private void seek(int direction, bool snapped) From 7c3441e2d02473a1c6a21b916c619aa2cfa7a796 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Thu, 12 Apr 2018 17:06:35 +0300 Subject: [PATCH 441/537] ActiveInputHandlers -> IgnoredInputHandler --- osu.Game/OsuGame.cs | 2 +- .../Settings/Sections/Input/MouseSettings.cs | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2f6f8ff348..062946550e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -439,7 +439,7 @@ namespace osu.Game sensitivity.Value = 1; sensitivity.Disabled = true; - frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); + frameworkConfig.Set(FrameworkSetting.IgnoredInputHandler, string.Empty); frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault(); return true; case GlobalAction.ToggleToolbar: diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index c368b8fea7..e2c947206c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input protected override string Header => "Mouse"; private readonly BindableBool rawInputToggle = new BindableBool(); - private Bindable activeInputHandlers; + private Bindable ignoredInputHandler; private SensitivitySetting sensitivity; [BackgroundDependencyLoader] @@ -61,20 +61,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input const string raw_mouse_handler = @"OpenTKRawMouseHandler"; const string standard_mouse_handler = @"OpenTKMouseHandler"; - activeInputHandlers.Value = enabled ? - activeInputHandlers.Value.Replace(standard_mouse_handler, raw_mouse_handler) : - activeInputHandlers.Value.Replace(raw_mouse_handler, standard_mouse_handler); + ignoredInputHandler.Value = enabled ? standard_mouse_handler : raw_mouse_handler; }; - activeInputHandlers = config.GetBindable(FrameworkSetting.ActiveInputHandlers); - activeInputHandlers.ValueChanged += handlers => + ignoredInputHandler = config.GetBindable(FrameworkSetting.IgnoredInputHandler); + ignoredInputHandler.ValueChanged += handler => { - bool raw = handlers.Contains("Raw"); + bool raw = !handler.Contains("Raw"); rawInputToggle.Value = raw; sensitivity.Bindable.Disabled = !raw; }; - activeInputHandlers.TriggerChange(); + ignoredInputHandler.TriggerChange(); } private class SensitivitySetting : SettingsSlider From f18594887bce516cb975a0da572a336d7f403d3a Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 12 Apr 2018 07:49:23 -0700 Subject: [PATCH 442/537] Move enum to SearchBeatmapSetsRequest --- osu.Game/Beatmaps/BeatmapSearchCategory.cs | 22 ------------------- .../API/Requests/SearchBeatmapSetsRequest.cs | 16 ++++++++++++++ osu.Game/Overlays/Direct/FilterControl.cs | 1 + 3 files changed, 17 insertions(+), 22 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapSearchCategory.cs diff --git a/osu.Game/Beatmaps/BeatmapSearchCategory.cs b/osu.Game/Beatmaps/BeatmapSearchCategory.cs deleted file mode 100644 index fdd284376f..0000000000 --- a/osu.Game/Beatmaps/BeatmapSearchCategory.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Beatmaps -{ - public enum BeatmapSearchCategory - { - Any = 7, - [Description("Ranked & Approved")] - RankedApproved = 0, - Approved = 1, - Loved = 8, - Favourites = 2, - Qualified = 3, - Pending = 4, - Graveyard = 5, - [Description("My Maps")] - MyMaps = 6, - } -} diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 2482aae170..52ed7218ad 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.ComponentModel; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.Direct; @@ -30,4 +31,19 @@ namespace osu.Game.Online.API.Requests // ReSharper disable once ImpureMethodCallOnReadonlyValueField protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; } + + public enum BeatmapSearchCategory + { + Any = 7, + [Description("Ranked & Approved")] + RankedApproved = 0, + Approved = 1, + Loved = 8, + Favourites = 2, + Qualified = 3, + Pending = 4, + Graveyard = 5, + [Description("My Maps")] + MyMaps = 6, + } } diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index e861326b27..ddbac10ac8 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests; using osu.Game.Overlays.SearchableList; using osu.Game.Rulesets; From df4b64effcc5418ce11940d7f9754afeaaafa883 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 12 Apr 2018 08:03:19 -0700 Subject: [PATCH 443/537] Rename rankStatus to searchCategory --- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 52ed7218ad..1abb844c8d 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -14,22 +14,22 @@ namespace osu.Game.Online.API.Requests { private readonly string query; private readonly RulesetInfo ruleset; - private readonly BeatmapSearchCategory rankStatus; + private readonly BeatmapSearchCategory searchCategory; private readonly DirectSortCriteria sortCriteria; private readonly SortDirection direction; private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory rankStatus = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending) + public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending) { this.query = System.Uri.EscapeDataString(query); this.ruleset = ruleset; - this.rankStatus = rankStatus; + this.searchCategory = searchCategory; this.sortCriteria = sortCriteria; this.direction = direction; } // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; + protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; } public enum BeatmapSearchCategory From 463b189d2fcc1730b7bc276e50aa69c408c3a691 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 12 Apr 2018 08:13:24 -0700 Subject: [PATCH 444/537] Remove unused using directives --- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 1 - osu.Game/Overlays/Direct/FilterControl.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 1abb844c8d..ed59ed53c2 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.ComponentModel; -using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.Direct; using osu.Game.Rulesets; diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index ddbac10ac8..286eb8ecef 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests; From 61d6f52a5392f01280222c55c421848cda7cafdc Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 12 Apr 2018 08:26:25 -0700 Subject: [PATCH 445/537] Lowercase back button --- osu.Game/Graphics/UserInterface/BackButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index 3f348dc19d..b5455d834a 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -10,7 +10,7 @@ namespace osu.Game.Graphics.UserInterface { public BackButton() { - Text = @"Back"; + Text = @"back"; Icon = FontAwesome.fa_osu_left_o; Anchor = Anchor.BottomLeft; Origin = Anchor.BottomLeft; From 9acea6eab0afa9d8262396e59accf58009a7f10f Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Thu, 12 Apr 2018 19:33:30 +0300 Subject: [PATCH 446/537] Order beatmap difficulty icons correctly --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 2 +- osu.Game/Overlays/Direct/DirectPanel.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index b09e151ebc..1781e606f9 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.BeatmapSet Beatmap.Value = BeatmapSet.Beatmaps.First(); plays.Value = BeatmapSet.OnlineInfo.PlayCount; favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; - difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Select(b => new DifficultySelectorButton(b) + difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b) { State = DifficultySelectorState.NotSelected, OnHovered = beatmap => diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index cba63b4a49..6f6bf2d868 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using OpenTK; @@ -208,7 +209,7 @@ namespace osu.Game.Overlays.Direct { var icons = new List(); - foreach (var b in SetInfo.Beatmaps) + foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) icons.Add(new DifficultyIcon(b)); return icons; From 128446e51d014f8c87281843ac8ff041bd80b424 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 12 Apr 2018 16:46:46 -0700 Subject: [PATCH 447/537] Remove composer from player loader --- osu.Game/Screens/Play/PlayerLoader.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6d55cdb9ca..23d7f7b5b6 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -309,11 +309,6 @@ namespace osu.Game.Screens.Play Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, }, - new MetadataLine("Composer", string.Empty) - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - }, new MetadataLine("Mapper", metadata.AuthorString) { Origin = Anchor.TopCentre, From ebe36f061292b658cc6eb87dc1e5720fe53f2948 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Apr 2018 17:27:10 +0900 Subject: [PATCH 448/537] Instantly hide cursor for required frames while taking screenshot --- .../Cursor/CursorOverrideContainer.cs | 20 ++++++------ osu.Game/Graphics/Cursor/MenuCursor.cs | 3 ++ osu.Game/Graphics/ScreenshotManager.cs | 32 +++++++++++++++---- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs index 73fa065919..58d566c34b 100644 --- a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs +++ b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs @@ -21,16 +21,22 @@ namespace osu.Game.Graphics.Cursor /// Whether any cursors can be displayed. /// public bool CanShowCursor = true; - public bool ShowMenuCursor = true; - public CursorContainer Cursor { get; } + public bool ShowMenuCursor + { + get => cursor.ShowCursor; + set => cursor.ShowCursor = value; + } + + private readonly MenuCursor cursor; + public CursorContainer Cursor => cursor; public bool ProvidingUserCursor => true; public CursorOverrideContainer() { AddRangeInternal(new Drawable[] { - Cursor = new MenuCursor { State = Visibility.Hidden }, + cursor = new MenuCursor { State = Visibility.Hidden }, content = new Container { RelativeSizeAxes = Axes.Both } }); } @@ -54,14 +60,6 @@ namespace osu.Game.Graphics.Cursor return; } - if (currentTarget?.Cursor is MenuCursor) - { - if (ShowMenuCursor && currentTarget?.Cursor.State == Visibility.Hidden) - currentTarget?.Cursor?.Show(); - else if (!ShowMenuCursor && currentTarget?.Cursor.State == Visibility.Visible) - currentTarget?.Cursor?.Hide(); - } - var newTarget = inputManager.HoveredDrawables.OfType().FirstOrDefault(t => t.ProvidingUserCursor) ?? this; if (currentTarget == newTarget) diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index bdee7d289d..4edac3e050 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -18,6 +18,9 @@ namespace osu.Game.Graphics.Cursor { public class MenuCursor : CursorContainer { + public bool ShowCursor = true; + public override bool IsPresent => ShowCursor && base.IsPresent; + protected override Drawable CreateCursor() => new Cursor(); private Bindable cursorRotate; diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index e918ff016e..0d77af8f81 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Platform; +using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; @@ -63,20 +64,31 @@ namespace osu.Game.Graphics public bool OnReleased(GlobalAction action) => false; - public async void TakeScreenshotAsync() + private volatile int screenShotTasks; + + public async Task TakeScreenshotAsync() => Task.Run(async () => { + Interlocked.Increment(ref screenShotTasks); + if (!captureMenuCursor.Value) { cursorOverrideContainer.ShowMenuCursor = false; - await Task.Run(() => - { - while (cursorOverrideContainer.Cursor.ActiveCursor.Alpha > 0) - Thread.Sleep(1); - }); + + // We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value + const int frames_to_wait = 3; + + int framesWaited = 0; + ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => framesWaited++, 0, true); + while (framesWaited < frames_to_wait) + Thread.Sleep(10); + + waitDelegate.Cancel(); } using (var bitmap = await host.TakeScreenshotAsync()) { + Interlocked.Decrement(ref screenShotTasks); + var fileName = getFileName(); if (fileName == null) return; @@ -104,8 +116,14 @@ namespace osu.Game.Graphics } }); } + }); - cursorOverrideContainer.ShowMenuCursor = true; + protected override void Update() + { + base.Update(); + + if (Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0) + cursorOverrideContainer.ShowMenuCursor = true; } private string getFileName() From 826a8552e598dc55cc59d11982fdcda470011f70 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Apr 2018 17:30:02 +0900 Subject: [PATCH 449/537] Reword options item to include "screenshot" --- osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index e124d4cf7e..3060cfdea9 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, new SettingsCheckbox { - LabelText = "Capture menu cursor", + LabelText = "Show menu cursor in screenshots", Bindable = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor) } }; From 2e5bbe707499549c225b7a5f55f463fb535835ae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Apr 2018 17:36:38 +0900 Subject: [PATCH 450/537] Don't expose CursorOverrideContainer via DI + internalise access --- .../Graphics/Cursor/CursorOverrideContainer.cs | 16 +++++----------- osu.Game/Graphics/ScreenshotManager.cs | 14 +++++++++----- osu.Game/OsuGame.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs index 58d566c34b..810847349a 100644 --- a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs +++ b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs @@ -20,23 +20,17 @@ namespace osu.Game.Graphics.Cursor /// /// Whether any cursors can be displayed. /// - public bool CanShowCursor = true; - - public bool ShowMenuCursor - { - get => cursor.ShowCursor; - set => cursor.ShowCursor = value; - } - - private readonly MenuCursor cursor; - public CursorContainer Cursor => cursor; + internal bool CanShowCursor = true; + internal readonly MenuCursor MenuCursor; + + public CursorContainer Cursor => MenuCursor; public bool ProvidingUserCursor => true; public CursorOverrideContainer() { AddRangeInternal(new Drawable[] { - cursor = new MenuCursor { State = Visibility.Hidden }, + MenuCursor = new MenuCursor { State = Visibility.Hidden }, content = new Container { RelativeSizeAxes = Axes.Both } }); } diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 0d77af8f81..1f4b5cee3e 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -33,15 +33,19 @@ namespace osu.Game.Graphics private NotificationOverlay notificationOverlay; private SampleChannel shutter; - private CursorOverrideContainer cursorOverrideContainer; + private readonly MenuCursor menuCursor; + + public ScreenshotManager(MenuCursor menuCursor) + { + this.menuCursor = menuCursor; + } [BackgroundDependencyLoader] - private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio, CursorOverrideContainer cursorOverrideContainer) + private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio) { this.host = host; this.storage = storage.GetStorageForDirectory(@"screenshots"); this.notificationOverlay = notificationOverlay; - this.cursorOverrideContainer = cursorOverrideContainer; screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); captureMenuCursor = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor); @@ -72,7 +76,7 @@ namespace osu.Game.Graphics if (!captureMenuCursor.Value) { - cursorOverrideContainer.ShowMenuCursor = false; + menuCursor.ShowCursor = false; // We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value const int frames_to_wait = 3; @@ -123,7 +127,7 @@ namespace osu.Game.Graphics base.Update(); if (Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0) - cursorOverrideContainer.ShowMenuCursor = true; + menuCursor.ShowCursor = true; } private string getFileName() diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 89447b8ed6..b95def9c73 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -239,7 +239,7 @@ namespace osu.Game loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); - loadComponentSingleFile(new ScreenshotManager(), Add); + loadComponentSingleFile(new ScreenshotManager(CursorOverrideContainer.MenuCursor), Add); //overlay elements loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ac5e300f16..54a279e977 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -214,7 +214,7 @@ namespace osu.Game GlobalActionContainer globalBinding; - dependencies.Cache(CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }); + CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) { RelativeSizeAxes = Axes.Both, From 0235eba9deef9a4e00b7214ff5ea94174b384220 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Apr 2018 17:50:59 +0900 Subject: [PATCH 451/537] Make TakeScreenshotAsync await on the internal task --- osu.Game/Graphics/Cursor/CursorOverrideContainer.cs | 2 +- osu.Game/Graphics/ScreenshotManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs index 810847349a..30e9b36252 100644 --- a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs +++ b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Graphics.Cursor /// internal bool CanShowCursor = true; internal readonly MenuCursor MenuCursor; - + public CursorContainer Cursor => MenuCursor; public bool ProvidingUserCursor => true; diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 1f4b5cee3e..d7746647c4 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics private volatile int screenShotTasks; - public async Task TakeScreenshotAsync() => Task.Run(async () => + public async Task TakeScreenshotAsync() => await Task.Run(async () => { Interlocked.Increment(ref screenShotTasks); From e3cd0ef20045dfd16874760d9215b1515878614e Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Fri, 13 Apr 2018 11:09:49 +0200 Subject: [PATCH 452/537] Add right click scrolling in song select (and its option) --- osu.Game/Configuration/OsuConfigManager.cs | 5 +++- .../Sections/Gameplay/SongSelectSettings.cs | 5 ++++ osu.Game/Screens/Select/BeatmapCarousel.cs | 29 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 70260b349e..a3025a65f4 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -84,6 +84,8 @@ namespace osu.Game.Configuration Set(OsuSetting.Version, string.Empty); Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); + + Set(OsuSetting.SelectScrollRightClick, false); } public OsuConfigManager(Storage storage) : base(storage) @@ -128,6 +130,7 @@ namespace osu.Game.Configuration ShowConvertedBeatmaps, SpeedChangeVisualisation, Skin, - ScreenshotFormat + ScreenshotFormat, + SelectScrollRightClick } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index 4bbd87c7e6..e8d65a1ed3 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -17,6 +17,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { Children = new Drawable[] { + new SettingsCheckbox + { + LabelText = "Right click to scroll", + Bindable = config.GetBindable(OsuSetting.SelectScrollRightClick), + }, new SettingsCheckbox { LabelText = "Show converted beatmaps", diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 15f4d5cf96..5c3518b46c 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -97,6 +97,9 @@ namespace osu.Game.Screens.Select private readonly Container scrollableContent; + + public Bindable RightClickScrollingEnabled = new Bindable(); + public Bindable RandomAlgorithm = new Bindable(); private readonly List previouslyVisitedRandomSets = new List(); private readonly Stack randomSelectedBeatmaps = new Stack(); @@ -121,6 +124,7 @@ namespace osu.Game.Screens.Select private void load(OsuConfigManager config) { config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); + config.BindWith(OsuSetting.SelectScrollRightClick, RightClickScrollingEnabled); } public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) @@ -398,6 +402,31 @@ namespace osu.Game.Screens.Select return true; } + private bool rightClickScrolling = false; + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + bool result = base.OnMouseDown(state, args); + + if (RightClickScrollingEnabled.Value && !result && args.Button == MouseButton.Right) + { + rightClickScrolling = true; + return true; + } + + return result; + } + + protected override bool OnMouseMove(InputState state) + { + if (state.Mouse.Buttons.Contains(MouseButton.Right) && rightClickScrolling) + ScrollTo((state.Mouse.Position.Y / DrawHeight) * scrollableContent.Height); + else + rightClickScrolling = false; + + return base.OnMouseMove(state); + } + protected override void Update() { base.Update(); From f99503b60c5b62c0504e83c5e74e503938b9f56f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Apr 2018 16:09:09 +0900 Subject: [PATCH 453/537] Add new gitattributes --- .gitattributes | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/.gitattributes b/.gitattributes index f9cb7c5c9a..baf69b41d1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,19 +1,23 @@ -# This won't normalise line endings, but it will ensure that merge drivers use CRLF -* -text eol=crlf - -# Currently in-use binary file extensions -*.blend binary -*.bmp binary -*.dll binary -*.exe binary -*.icns binary -*.ico binary -*.jpg binary -*.osz2 binary -*.pdn binary -*.psd binary -*.PSD binary -*.tga binary -*.ttf binary -*.wav binary -*.xnb binary +# Autodetect text files and ensure that we normalise their +# line endings to lf internally. When checked out they may +# use different line endings. +* text=auto + +# Check out with crlf (Windows) line endings +*.sln text eol=crlf +*.csproj text eol=crlf +*.cs text diff=csharp eol=crlf +*.resx text eol=crlf +*.vsixmanifest text eol=crlf +packages.config text eol=crlf +App.config text eol=crlf +*.bat text eol=crlf +*.cmd text eol=crlf +*.snippet text eol=crlf + +# Check out with lf (UNIX) line endings +*.sh text eol=lf +.gitignore text eol=lf +.gitattributes text eol=lf +*.md text eol=lf +.travis.yml text eol=lf \ No newline at end of file From 32a74f95a5c80a0ed18e693f13a47522099df5c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Apr 2018 18:19:50 +0900 Subject: [PATCH 454/537] Normalize all the line endings --- .gitignore | 518 ++--- .travis.yml | 2 +- osu.Desktop.Deploy/App.config | 40 +- osu.Desktop.Deploy/GitHubObject.cs | 32 +- osu.Desktop.Deploy/GitHubRelease.cs | 56 +- osu.Desktop.Deploy/Program.cs | 876 +++---- osu.Desktop/OsuGameDesktop.cs | 240 +- osu.Desktop/Overlays/SquirrelUpdateManager.cs | 328 +-- osu.Desktop/Overlays/VersionManager.cs | 284 +-- osu.Desktop/Program.cs | 132 +- .../CatchBeatmapConversionTest.cs | 134 +- .../TestCaseAutoJuiceStream.cs | 124 +- .../TestCaseBananaShower.cs | 94 +- .../TestCaseCatchPlayer.cs | 30 +- .../TestCaseCatchStacker.cs | 72 +- .../TestCaseCatcherArea.cs | 122 +- .../TestCaseFruitObjects.cs | 148 +- .../TestCaseHyperdash.cs | 60 +- .../TestCasePerformancePoints.cs | 32 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 18 +- .../Beatmaps/CatchBeatmapConverter.cs | 136 +- .../Beatmaps/CatchBeatmapProcessor.cs | 144 +- .../CatchDifficultyCalculator.cs | 42 +- osu.Game.Rulesets.Catch/CatchInputManager.cs | 54 +- osu.Game.Rulesets.Catch/CatchRuleset.cs | 226 +- .../Judgements/CatchJudgement.cs | 24 +- .../Mods/CatchModAutoplay.cs | 48 +- .../Mods/CatchModDaycore.cs | 24 +- .../Mods/CatchModDoubleTime.cs | 24 +- osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs | 24 +- .../Mods/CatchModFlashlight.cs | 24 +- .../Mods/CatchModHalfTime.cs | 24 +- .../Mods/CatchModHardRock.cs | 26 +- .../Mods/CatchModHidden.cs | 26 +- .../Mods/CatchModNightcore.cs | 24 +- .../Mods/CatchModNoFail.cs | 22 +- .../Mods/CatchModPerfect.cs | 22 +- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 24 +- .../Mods/CatchModSuddenDeath.cs | 22 +- .../Objects/BananaShower.cs | 96 +- .../Objects/CatchHitObject.cs | 120 +- .../Objects/Drawable/DrawableBananaShower.cs | 88 +- .../Drawable/DrawableCatchHitObject.cs | 186 +- .../Objects/Drawable/DrawableDroplet.cs | 86 +- .../Objects/Drawable/DrawableFruit.cs | 610 ++--- .../Objects/Drawable/DrawableJuiceStream.cs | 82 +- .../Objects/Drawable/Pieces/Pulp.cs | 84 +- osu.Game.Rulesets.Catch/Objects/Droplet.cs | 18 +- osu.Game.Rulesets.Catch/Objects/Fruit.cs | 18 +- .../Objects/JuiceStream.cs | 314 +-- .../Objects/TinyDroplet.cs | 18 +- .../Properties/AssemblyInfo.cs | 22 +- .../Replays/CatchAutoGenerator.cs | 242 +- .../Replays/CatchFramedReplayInputHandler.cs | 120 +- .../Replays/CatchReplayFrame.cs | 68 +- .../Scoring/CatchScoreProcessor.cs | 88 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 140 +- .../UI/CatchRulesetContainer.cs | 118 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 832 +++---- .../ManiaBeatmapConversionTest.cs | 120 +- .../TestCaseAutoGeneration.cs | 358 +-- .../TestCaseManiaHitObjects.cs | 192 +- .../TestCaseManiaPlayfield.cs | 386 ++-- .../TestCasePerformancePoints.cs | 32 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 18 +- .../Beatmaps/ManiaBeatmap.cs | 66 +- .../Beatmaps/ManiaBeatmapConverter.cs | 450 ++-- .../Legacy/DistanceObjectPatternGenerator.cs | 982 ++++---- .../Legacy/EndTimeObjectPatternGenerator.cs | 204 +- .../Legacy/HitObjectPatternGenerator.cs | 802 +++---- .../Patterns/Legacy/PatternGenerator.cs | 246 +- .../Beatmaps/Patterns/Legacy/PatternType.cs | 130 +- .../Beatmaps/Patterns/Pattern.cs | 114 +- .../Beatmaps/Patterns/PatternGenerator.cs | 100 +- .../Beatmaps/StageDefinition.cs | 50 +- .../Configuration/ManiaConfigManager.cs | 68 +- .../Judgements/HoldNoteTailJudgement.cs | 54 +- .../Judgements/HoldNoteTickJudgement.cs | 28 +- .../Judgements/ManiaJudgement.cs | 58 +- .../ManiaDifficultyCalculator.cs | 292 +-- osu.Game.Rulesets.Mania/ManiaInputManager.cs | 128 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 624 ++--- .../MathUtils/FastRandom.cs | 182 +- .../Mods/IPlayfieldTypeMod.cs | 30 +- osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs | 58 +- .../Mods/ManiaModAutoplay.cs | 50 +- .../Mods/ManiaModDaycore.cs | 24 +- .../Mods/ManiaModDoubleTime.cs | 24 +- .../Mods/ManiaModDualStages.cs | 104 +- osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs | 24 +- .../Mods/ManiaModFadeIn.cs | 42 +- .../Mods/ManiaModFlashlight.cs | 28 +- .../Mods/ManiaModHalfTime.cs | 24 +- .../Mods/ManiaModHardRock.cs | 24 +- .../Mods/ManiaModHidden.cs | 30 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs | 26 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs | 26 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs | 26 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs | 26 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs | 26 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs | 26 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs | 26 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs | 26 +- osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs | 26 +- .../Mods/ManiaModMirror.cs | 56 +- .../Mods/ManiaModNightcore.cs | 24 +- .../Mods/ManiaModNoFail.cs | 22 +- .../Mods/ManiaModPerfect.cs | 22 +- .../Mods/ManiaModRandom.cs | 62 +- .../Mods/ManiaModSuddenDeath.cs | 22 +- osu.Game.Rulesets.Mania/Objects/BarLine.cs | 42 +- .../Objects/Drawables/DrawableBarLine.cs | 148 +- .../Objects/Drawables/DrawableHoldNote.cs | 508 ++--- .../Objects/Drawables/DrawableHoldNoteTick.cs | 222 +- .../Drawables/DrawableManiaHitObject.cs | 62 +- .../Objects/Drawables/DrawableNote.cs | 190 +- .../Objects/Drawables/Pieces/BodyPiece.cs | 264 +-- .../Objects/Drawables/Pieces/GlowPiece.cs | 130 +- .../Objects/Drawables/Pieces/LaneGlowPiece.cs | 170 +- .../Objects/Drawables/Pieces/NotePiece.cs | 118 +- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 234 +- .../Objects/HoldNoteTick.cs | 24 +- .../Objects/ManiaHitObject.cs | 46 +- .../Objects/ManiaHitObjectDifficulty.cs | 226 +- osu.Game.Rulesets.Mania/Objects/Note.cs | 24 +- .../Objects/Types/IHasColumn.cs | 32 +- .../Properties/AssemblyInfo.cs | 22 +- .../Replays/ManiaAutoGenerator.cs | 196 +- .../Replays/ManiaFramedReplayInputHandler.cs | 44 +- .../Replays/ManiaReplayFrame.cs | 118 +- .../Scoring/ManiaScoreProcessor.cs | 346 +-- osu.Game.Rulesets.Mania/UI/Column.cs | 552 ++--- .../UI/DrawableManiaJudgement.cs | 84 +- osu.Game.Rulesets.Mania/UI/HitExplosion.cs | 132 +- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 188 +- .../UI/ManiaRulesetContainer.cs | 222 +- osu.Game.Rulesets.Mania/UI/ManiaStage.cs | 442 ++-- .../OsuBeatmapConversionTest.cs | 140 +- osu.Game.Rulesets.Osu.Tests/TestCaseEditor.cs | 34 +- .../TestCaseGameplayCursor.cs | 66 +- .../TestCaseHitCircle.cs | 230 +- .../TestCaseHitCircleHidden.cs | 44 +- .../TestCasePerformancePoints.cs | 32 +- osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs | 626 ++--- .../TestCaseSliderHidden.cs | 44 +- .../TestCaseSpinner.cs | 176 +- .../TestCaseSpinnerHidden.cs | 44 +- .../osu.Game.Rulesets.Osu.Tests.csproj | 18 +- .../Beatmaps/OsuBeatmapConverter.cs | 128 +- .../Beatmaps/OsuBeatmapProcessor.cs | 332 +-- .../Selection/Overlays/HitCircleMask.cs | 74 +- .../Selection/Overlays/SliderCircleMask.cs | 122 +- .../Layers/Selection/Overlays/SliderMask.cs | 134 +- .../Edit/OsuEditPlayfield.cs | 26 +- .../Edit/OsuEditRulesetContainer.cs | 50 +- .../Edit/OsuHitObjectComposer.cs | 98 +- .../Judgements/OsuJudgement.cs | 74 +- .../Judgements/OsuSliderTailJudgement.cs | 26 +- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 40 +- osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs | 52 +- osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs | 24 +- .../Mods/OsuModDoubleTime.cs | 24 +- osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs | 24 +- .../Mods/OsuModFlashlight.cs | 24 +- osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs | 24 +- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 80 +- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 174 +- osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs | 24 +- osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs | 28 +- osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs | 22 +- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 30 +- osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 40 +- .../Mods/OsuModSuddenDeath.cs | 28 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 34 +- .../Connections/ConnectionRenderer.cs | 42 +- .../Drawables/Connections/FollowPoint.cs | 92 +- .../Connections/FollowPointRenderer.cs | 228 +- .../Objects/Drawables/DrawableHitCircle.cs | 310 +-- .../Objects/Drawables/DrawableOsuHitObject.cs | 144 +- .../Objects/Drawables/DrawableOsuJudgement.cs | 54 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 200 +- .../Objects/Drawables/DrawableSlider.cs | 360 +-- .../Objects/Drawables/DrawableSliderHead.cs | 64 +- .../Objects/Drawables/DrawableSliderTail.cs | 76 +- .../Objects/Drawables/DrawableSliderTick.cs | 150 +- .../Objects/Drawables/DrawableSpinner.cs | 446 ++-- .../Objects/Drawables/IRequireTracking.cs | 26 +- .../Objects/Drawables/ITrackSnaking.cs | 30 +- .../Drawables/Pieces/ApproachCircle.cs | 58 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 86 +- .../Drawables/Pieces/DefaultCirclePiece.cs | 70 +- .../Objects/Drawables/Pieces/ExplodePiece.cs | 62 +- .../Objects/Drawables/Pieces/FlashPiece.cs | 70 +- .../Objects/Drawables/Pieces/GlowPiece.cs | 70 +- .../Objects/Drawables/Pieces/NumberPiece.cs | 114 +- .../Objects/Drawables/Pieces/RingPiece.cs | 82 +- .../Objects/Drawables/Pieces/SliderBall.cs | 296 +-- .../Objects/Drawables/Pieces/SliderBody.cs | 466 ++-- .../Drawables/Pieces/SpinnerBackground.cs | 112 +- .../Objects/Drawables/Pieces/SpinnerDisc.cs | 254 +-- .../Drawables/Pieces/SpinnerSpmCounter.cs | 160 +- .../Objects/Drawables/Pieces/SpinnerTicks.cs | 114 +- .../Drawables/Pieces/TrianglesPiece.cs | 52 +- osu.Game.Rulesets.Osu/Objects/HitCircle.cs | 18 +- .../Objects/ISliderProgress.cs | 28 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 150 +- osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs | 50 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 372 +-- osu.Game.Rulesets.Osu/Objects/SliderCircle.cs | 38 +- osu.Game.Rulesets.Osu/Objects/SliderTick.cs | 60 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 66 +- .../OsuDifficulty/OsuDifficultyCalculator.cs | 156 +- .../Preprocessing/OsuDifficultyBeatmap.cs | 188 +- .../Preprocessing/OsuDifficultyHitObject.cs | 232 +- .../OsuDifficulty/Skills/Aim.cs | 38 +- .../OsuDifficulty/Skills/Skill.cs | 200 +- .../OsuDifficulty/Skills/Speed.cs | 78 +- .../OsuDifficulty/Utils/History.cs | 172 +- osu.Game.Rulesets.Osu/OsuInputManager.cs | 54 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 314 +-- .../Properties/AssemblyInfo.cs | 22 +- .../Replays/OsuAutoGenerator.cs | 670 +++--- .../Replays/OsuAutoGeneratorBase.cs | 192 +- .../Replays/OsuReplayFrame.cs | 72 +- .../Replays/OsuReplayInputHandler.cs | 90 +- .../Scoring/OsuPerformanceCalculator.cs | 398 ++-- .../Scoring/OsuScoreProcessor.cs | 218 +- .../UI/Cursor/CursorTrail.cs | 546 ++--- .../UI/Cursor/GameplayCursor.cs | 378 +-- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 178 +- .../UI/OsuRulesetContainer.cs | 124 +- osu.Game.Rulesets.Osu/UI/OsuSettings.cs | 66 +- .../TaikoBeatmapConversionTest.cs | 146 +- .../TestCaseInputDrum.cs | 88 +- .../TestCasePerformancePoints.cs | 32 +- .../TestCaseTaikoPlayfield.cs | 480 ++-- .../osu.Game.Rulesets.Taiko.Tests.csproj | 18 +- .../Audio/DrumSampleMapping.cs | 122 +- .../Beatmaps/TaikoBeatmapConverter.cs | 400 ++-- .../Judgements/TaikoDrumRollTickJudgement.cs | 46 +- .../Judgements/TaikoJudgement.cs | 62 +- .../Judgements/TaikoStrongHitJudgement.cs | 30 +- .../Mods/TaikoModAutoplay.cs | 48 +- .../Mods/TaikoModDaycore.cs | 24 +- .../Mods/TaikoModDoubleTime.cs | 24 +- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 24 +- .../Mods/TaikoModFlashlight.cs | 24 +- .../Mods/TaikoModHalfTime.cs | 24 +- .../Mods/TaikoModHardRock.cs | 26 +- .../Mods/TaikoModHidden.cs | 26 +- .../Mods/TaikoModNightcore.cs | 24 +- .../Mods/TaikoModNoFail.cs | 22 +- .../Mods/TaikoModPerfect.cs | 22 +- osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs | 24 +- .../Mods/TaikoModSuddenDeath.cs | 22 +- osu.Game.Rulesets.Taiko/Objects/BarLine.cs | 18 +- osu.Game.Rulesets.Taiko/Objects/CentreHit.cs | 18 +- .../Objects/Drawables/DrawableBarLine.cs | 128 +- .../Objects/Drawables/DrawableBarLineMajor.cs | 134 +- .../Objects/Drawables/DrawableCentreHit.cs | 52 +- .../Drawables/DrawableCentreHitStrong.cs | 52 +- .../Objects/Drawables/DrawableDrumRoll.cs | 214 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 106 +- .../Objects/Drawables/DrawableHit.cs | 240 +- .../Objects/Drawables/DrawableHitStrong.cs | 178 +- .../Objects/Drawables/DrawableRimHit.cs | 52 +- .../Objects/Drawables/DrawableRimHitStrong.cs | 52 +- .../Objects/Drawables/DrawableSwell.cs | 450 ++-- .../Drawables/DrawableTaikoHitObject.cs | 104 +- .../Drawables/Pieces/CentreHitSymbolPiece.cs | 72 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 316 +-- .../Drawables/Pieces/ElongatedCirclePiece.cs | 60 +- .../Drawables/Pieces/RimHitSymbolPiece.cs | 78 +- .../Drawables/Pieces/SwellSymbolPiece.cs | 72 +- .../Objects/Drawables/Pieces/TaikoPiece.cs | 82 +- .../Objects/Drawables/Pieces/TickPiece.cs | 130 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 164 +- .../Objects/DrumRollTick.cs | 48 +- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 18 +- osu.Game.Rulesets.Taiko/Objects/RimHit.cs | 18 +- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 38 +- .../Objects/TaikoHitObject.cs | 62 +- .../Objects/TaikoHitObjectDifficulty.cs | 254 +-- .../Properties/AssemblyInfo.cs | 22 +- .../Replays/TaikoAutoGenerator.cs | 260 +-- .../Replays/TaikoFramedReplayInputHandler.cs | 44 +- .../Replays/TaikoReplayFrame.cs | 68 +- .../Scoring/TaikoScoreProcessor.cs | 288 +-- .../TaikoDifficultyCalculator.cs | 276 +-- osu.Game.Rulesets.Taiko/TaikoInputManager.cs | 58 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 230 +- .../UI/DrawableTaikoJudgement.cs | 100 +- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 160 +- osu.Game.Rulesets.Taiko/UI/HitTarget.cs | 184 +- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 386 ++-- .../UI/KiaiHitExplosion.cs | 136 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 534 ++--- .../UI/TaikoRulesetContainer.cs | 278 +-- .../Formats/LegacyBeatmapDecoderTest.cs | 430 ++-- .../Formats/LegacyStoryboardDecoderTest.cs | 180 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 314 +-- .../Beatmaps/IO/ImportBeatmapTest.cs | 594 ++--- .../Beatmaps/IO/OszArchiveReaderTest.cs | 166 +- osu.Game.Tests/Chat/MessageFormatterTests.cs | 494 ++-- osu.Game.Tests/Resources/Resource.cs | 40 +- osu.Game.Tests/Visual/TestCaseAllPlayers.cs | 24 +- osu.Game.Tests/Visual/TestCaseAutoplay.cs | 42 +- .../Visual/TestCaseBeatDivisorControl.cs | 56 +- .../Visual/TestCaseBeatSyncedContainer.cs | 410 ++-- .../Visual/TestCaseBeatmapCarousel.cs | 1062 ++++----- .../Visual/TestCaseBeatmapDetailArea.cs | 50 +- .../Visual/TestCaseBeatmapDetails.cs | 230 +- .../Visual/TestCaseBeatmapInfoWedge.cs | 326 +-- .../Visual/TestCaseBeatmapOptionsOverlay.cs | 58 +- .../Visual/TestCaseBeatmapScoresContainer.cs | 626 ++--- .../Visual/TestCaseBeatmapSetOverlay.cs | 758 +++---- osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs | 92 +- osu.Game.Tests/Visual/TestCaseBreakOverlay.cs | 182 +- osu.Game.Tests/Visual/TestCaseButtonSystem.cs | 70 +- osu.Game.Tests/Visual/TestCaseChatDisplay.cs | 42 +- osu.Game.Tests/Visual/TestCaseChatLink.cs | 438 ++-- osu.Game.Tests/Visual/TestCaseContextMenu.cs | 196 +- osu.Game.Tests/Visual/TestCaseCursors.cs | 522 ++--- .../Visual/TestCaseDialogOverlay.cs | 150 +- osu.Game.Tests/Visual/TestCaseDirect.cs | 446 ++-- osu.Game.Tests/Visual/TestCaseDrawableRoom.cs | 264 +-- osu.Game.Tests/Visual/TestCaseDrawings.cs | 166 +- .../Visual/TestCaseEditorCompose.cs | 60 +- .../TestCaseEditorComposeRadioButtons.cs | 86 +- .../Visual/TestCaseEditorComposeTimeline.cs | 96 +- .../Visual/TestCaseEditorMenuBar.cs | 196 +- .../Visual/TestCaseEditorSeekSnapping.cs | 874 +++---- .../Visual/TestCaseEditorSummaryTimeline.cs | 74 +- .../Visual/TestCaseGameplayMenuOverlay.cs | 516 ++--- osu.Game.Tests/Visual/TestCaseGraph.cs | 78 +- .../Visual/TestCaseHistoricalSection.cs | 98 +- .../Visual/TestCaseHitObjectComposer.cs | 144 +- osu.Game.Tests/Visual/TestCaseIconButton.cs | 226 +- .../Visual/TestCaseIntroSequence.cs | 108 +- .../Visual/TestCaseKeyConfiguration.cs | 50 +- osu.Game.Tests/Visual/TestCaseKeyCounter.cs | 82 +- osu.Game.Tests/Visual/TestCaseLeaderboard.cs | 556 ++--- osu.Game.Tests/Visual/TestCaseMedalOverlay.cs | 70 +- osu.Game.Tests/Visual/TestCaseMods.cs | 466 ++-- .../Visual/TestCaseMusicController.cs | 84 +- .../Visual/TestCaseNotificationOverlay.cs | 332 +-- .../Visual/TestCaseOnScreenDisplay.cs | 92 +- osu.Game.Tests/Visual/TestCaseOsuGame.cs | 82 +- .../Visual/TestCasePlaySongSelect.cs | 346 +-- .../Visual/TestCasePlaybackControl.cs | 84 +- osu.Game.Tests/Visual/TestCasePopupDialog.cs | 78 +- osu.Game.Tests/Visual/TestCaseRankGraph.cs | 250 +- osu.Game.Tests/Visual/TestCaseReplay.cs | 70 +- .../Visual/TestCaseReplaySettingsOverlay.cs | 104 +- osu.Game.Tests/Visual/TestCaseResults.cs | 144 +- .../Visual/TestCaseRoomInspector.cs | 286 +-- osu.Game.Tests/Visual/TestCaseScoreCounter.cs | 212 +- .../Visual/TestCaseScrollingHitObjects.cs | 372 +-- osu.Game.Tests/Visual/TestCaseSettings.cs | 82 +- osu.Game.Tests/Visual/TestCaseSkipButton.cs | 38 +- osu.Game.Tests/Visual/TestCaseSocial.cs | 190 +- osu.Game.Tests/Visual/TestCaseSongProgress.cs | 128 +- osu.Game.Tests/Visual/TestCaseStoryboard.cs | 182 +- osu.Game.Tests/Visual/TestCaseTabControl.cs | 84 +- osu.Game.Tests/Visual/TestCaseTextAwesome.cs | 110 +- osu.Game.Tests/Visual/TestCaseToolbar.cs | 82 +- .../Visual/TestCaseTwoLayerButton.cs | 34 +- osu.Game.Tests/Visual/TestCaseUserPanel.cs | 110 +- osu.Game.Tests/Visual/TestCaseUserProfile.cs | 206 +- .../TestCaseUserProfileRecentSection.cs | 322 +-- osu.Game.Tests/Visual/TestCaseUserRanks.cs | 94 +- osu.Game.Tests/Visual/TestCaseVolumePieces.cs | 60 +- osu.Game.Tests/Visual/TestCaseWaveform.cs | 180 +- osu.Game/Audio/SampleInfo.cs | 72 +- osu.Game/Beatmaps/Beatmap.cs | 176 +- osu.Game/Beatmaps/BeatmapConverter.cs | 224 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 128 +- osu.Game/Beatmaps/BeatmapInfo.cs | 294 +-- osu.Game/Beatmaps/BeatmapManager.cs | 710 +++--- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 250 +- osu.Game/Beatmaps/BeatmapMetadata.cs | 196 +- osu.Game/Beatmaps/BeatmapMetrics.cs | 62 +- osu.Game/Beatmaps/BeatmapOnlineInfo.cs | 72 +- osu.Game/Beatmaps/BeatmapProcessor.cs | 98 +- osu.Game/Beatmaps/BeatmapSetFileInfo.cs | 50 +- osu.Game/Beatmaps/BeatmapSetInfo.cs | 80 +- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 164 +- osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs | 34 +- osu.Game/Beatmaps/BeatmapStatistic.cs | 28 +- osu.Game/Beatmaps/BeatmapStore.cs | 194 +- .../Beatmaps/ControlPoints/ControlPoint.cs | 38 +- .../ControlPoints/ControlPointInfo.cs | 240 +- .../ControlPoints/DifficultyControlPoint.cs | 42 +- .../ControlPoints/EffectControlPoint.cs | 36 +- .../ControlPoints/SampleControlPoint.cs | 68 +- .../ControlPoints/TimingControlPoint.cs | 54 +- osu.Game/Beatmaps/DifficultyCalculator.cs | 128 +- .../Drawables/BeatmapBackgroundSprite.cs | 58 +- .../Beatmaps/Drawables/BeatmapSetCover.cs | 108 +- .../Drawables/BeatmapSetOnlineStatusPill.cs | 108 +- .../Drawables/DifficultyColouredContainer.cs | 158 +- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 96 +- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 148 +- osu.Game/Beatmaps/Formats/Decoder.cs | 160 +- osu.Game/Beatmaps/Formats/IHasComboColours.cs | 26 +- .../Beatmaps/Formats/IHasCustomColours.cs | 26 +- .../Beatmaps/Formats/JsonBeatmapDecoder.cs | 54 +- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 778 +++---- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 328 +-- .../Formats/LegacyStoryboardDecoder.cs | 612 ++--- osu.Game/Beatmaps/IBeatmapConverter.cs | 50 +- osu.Game/Beatmaps/Legacy/LegacyBeatmap.cs | 42 +- osu.Game/Beatmaps/RankStatus.cs | 46 +- osu.Game/Beatmaps/Timing/BreakPeriod.cs | 66 +- osu.Game/Beatmaps/Timing/TimeSignatures.cs | 22 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 432 ++-- .../Configuration/DatabasedConfigManager.cs | 142 +- osu.Game/Configuration/DatabasedSetting.cs | 102 +- osu.Game/Configuration/OsuConfigManager.cs | 266 +-- .../Configuration/RandomSelectAlgorithm.cs | 30 +- osu.Game/Configuration/RankingType.cs | 36 +- osu.Game/Configuration/ReleaseStream.cs | 26 +- osu.Game/Configuration/ScoreMeterType.cs | 24 +- osu.Game/Configuration/ScreenshotFormat.cs | 30 +- osu.Game/Configuration/SettingsStore.cs | 82 +- .../SpeedChangeVisualisationMethod.cs | 30 +- osu.Game/Database/ArchiveModelManager.cs | 696 +++--- osu.Game/Database/DatabaseBackedStore.cs | 104 +- osu.Game/Database/DatabaseContextFactory.cs | 188 +- osu.Game/Database/DatabaseWriteUsage.cs | 92 +- osu.Game/Database/ICanAcceptFiles.cs | 44 +- osu.Game/Database/IDatabaseContextFactory.cs | 40 +- osu.Game/Database/IHasFiles.cs | 36 +- osu.Game/Database/IHasPrimaryKey.cs | 20 +- osu.Game/Database/INamedFileInfo.cs | 32 +- osu.Game/Database/ISoftDelete.cs | 32 +- .../Database/MutableDatabaseBackedStore.cs | 298 +-- osu.Game/Database/OsuDbContext.cs | 418 ++-- osu.Game/Database/SingletonContextFactory.cs | 38 +- osu.Game/Graphics/Backgrounds/Background.cs | 86 +- osu.Game/Graphics/Backgrounds/Triangles.cs | 560 ++--- .../Containers/BeatSyncedContainer.cs | 172 +- .../Containers/ConstrainedIconContainer.cs | 124 +- .../Graphics/Containers/LinkFlowContainer.cs | 208 +- .../Containers/OsuClickableContainer.cs | 84 +- .../Containers/OsuFocusedOverlayContainer.cs | 146 +- .../Graphics/Containers/OsuHoverContainer.cs | 90 +- .../Graphics/Containers/OsuScrollContainer.cs | 150 +- .../Containers/OsuTextFlowContainer.cs | 42 +- .../Graphics/Containers/ParallaxContainer.cs | 156 +- .../ReverseChildIDFillFlowContainer.cs | 26 +- .../Graphics/Containers/SectionsContainer.cs | 388 ++-- .../Cursor/CursorOverrideContainer.cs | 134 +- osu.Game/Graphics/Cursor/IProvideCursor.cs | 52 +- osu.Game/Graphics/Cursor/MenuCursor.cs | 314 +-- .../Cursor/OsuContextMenuContainer.cs | 28 +- .../Graphics/Cursor/OsuTooltipContainer.cs | 210 +- osu.Game/Graphics/DrawableDate.cs | 130 +- osu.Game/Graphics/IHasAccentColour.cs | 76 +- osu.Game/Graphics/OsuColour.cs | 192 +- osu.Game/Graphics/ScreenshotManager.cs | 220 +- osu.Game/Graphics/SpriteIcon.cs | 2018 ++++++++--------- osu.Game/Graphics/Sprites/OsuSpriteText.cs | 126 +- .../Graphics/Textures/LargeTextureStore.cs | 36 +- osu.Game/Graphics/UserInterface/BackButton.cs | 52 +- osu.Game/Graphics/UserInterface/Bar.cs | 276 +-- osu.Game/Graphics/UserInterface/BarGraph.cs | 154 +- .../UserInterface/BreadcrumbControl.cs | 188 +- .../Graphics/UserInterface/DialogButton.cs | 568 ++--- .../Graphics/UserInterface/FocusedTextBox.cs | 116 +- .../UserInterface/HoverClickSounds.cs | 72 +- .../Graphics/UserInterface/HoverSounds.cs | 106 +- osu.Game/Graphics/UserInterface/IconButton.cs | 360 +-- osu.Game/Graphics/UserInterface/LineGraph.cs | 236 +- .../UserInterface/LoadingAnimation.cs | 92 +- .../Graphics/UserInterface/MenuItemType.cs | 24 +- osu.Game/Graphics/UserInterface/Nub.cs | 284 +-- osu.Game/Graphics/UserInterface/OsuButton.cs | 36 +- .../Graphics/UserInterface/OsuCheckbox.cs | 238 +- .../Graphics/UserInterface/OsuContextMenu.cs | 78 +- .../Graphics/UserInterface/OsuDropdown.cs | 510 ++--- .../Graphics/UserInterface/OsuEnumDropdown.cs | 64 +- osu.Game/Graphics/UserInterface/OsuMenu.cs | 340 +-- .../Graphics/UserInterface/OsuMenuItem.cs | 50 +- .../UserInterface/OsuPasswordTextBox.cs | 238 +- .../Graphics/UserInterface/OsuSliderBar.cs | 436 ++-- .../Graphics/UserInterface/OsuTabControl.cs | 572 ++--- .../UserInterface/OsuTabControlCheckbox.cs | 272 +-- osu.Game/Graphics/UserInterface/OsuTextBox.cs | 126 +- .../Graphics/UserInterface/PageTabControl.cs | 196 +- .../UserInterface/PercentageCounter.cs | 86 +- .../Graphics/UserInterface/ProgressBar.cs | 134 +- .../Graphics/UserInterface/RollingCounter.cs | 364 +-- .../Graphics/UserInterface/ScoreCounter.cs | 108 +- .../Graphics/UserInterface/SearchTextBox.cs | 146 +- .../UserInterface/SimpleComboCounter.cs | 70 +- .../Graphics/UserInterface/StarCounter.cs | 308 +-- .../Graphics/UserInterface/TriangleButton.cs | 216 +- .../Graphics/UserInterface/TwoLayerButton.cs | 508 ++--- osu.Game/IO/Archives/ArchiveReader.cs | 94 +- .../IO/Archives/LegacyFilesystemReader.cs | 68 +- osu.Game/IO/Archives/ZipArchiveReader.cs | 100 +- osu.Game/IO/FileInfo.cs | 40 +- osu.Game/IO/FileStore.cs | 228 +- osu.Game/IO/Legacy/ILegacySerializable.cs | 22 +- osu.Game/IO/Legacy/SerializationReader.cs | 554 ++--- osu.Game/IO/Legacy/SerializationWriter.cs | 524 ++--- .../Converters/TypedListConverter.cs | 200 +- .../Converters/Vector2Converter.cs | 76 +- .../IO/Serialization/IJsonSerializable.cs | 74 +- .../IO/Serialization/KeyContractResolver.cs | 32 +- osu.Game/IPC/ArchiveImportIPCChannel.cs | 98 +- osu.Game/IPC/ScoreIPCChannel.cs | 92 +- .../Input/Bindings/DatabasedKeyBinding.cs | 68 +- .../Bindings/DatabasedKeyBindingContainer.cs | 132 +- .../Input/Bindings/GlobalActionContainer.cs | 162 +- osu.Game/Input/Handlers/ReplayInputHandler.cs | 96 +- osu.Game/Input/KeyBindingStore.cs | 172 +- .../20171019041408_InitialCreate.Designer.cs | 586 ++--- .../20171019041408_InitialCreate.cs | 622 ++--- ...025071459_AddMissingIndexRules.Designer.cs | 598 ++--- .../20171025071459_AddMissingIndexRules.cs | 160 +- ...eatmapOnlineIDUniqueConstraint.Designer.cs | 604 ++--- ...5731_AddBeatmapOnlineIDUniqueConstraint.cs | 50 +- ...034410_AddRulesetInfoShortName.Designer.cs | 614 ++--- .../20171209034410_AddRulesetInfoShortName.cs | 70 +- .../20180125143340_Settings.Designer.cs | 658 +++--- .../Migrations/20180125143340_Settings.cs | 114 +- .../20180131154205_AddMuteBinding.cs | 50 +- .../20180219060912_AddSkins.Designer.cs | 758 +++---- .../Migrations/20180219060912_AddSkins.cs | 146 +- .../Migrations/OsuDbContextModelSnapshot.cs | 748 +++--- osu.Game/Online/API/APIAccess.cs | 668 +++--- osu.Game/Online/API/APIDownloadRequest.cs | 66 +- osu.Game/Online/API/APIRequest.cs | 242 +- osu.Game/Online/API/DummyAPIAccess.cs | 62 +- osu.Game/Online/API/IAPIProvider.cs | 66 +- osu.Game/Online/API/IOnlineComponent.cs | 20 +- osu.Game/Online/API/OAuth.cs | 370 +-- osu.Game/Online/API/OAuthToken.cs | 126 +- .../API/Requests/APIResponseBeatmapSet.cs | 290 +-- .../API/Requests/DownloadBeatmapSetRequest.cs | 54 +- .../API/Requests/GetBeatmapDetailsRequest.cs | 92 +- .../API/Requests/GetBeatmapSetRequest.cs | 34 +- .../Online/API/Requests/GetFriendsRequest.cs | 26 +- .../Online/API/Requests/GetMessagesRequest.cs | 70 +- .../Online/API/Requests/GetScoresRequest.cs | 332 +-- .../API/Requests/GetUserBeatmapsRequest.cs | 66 +- .../GetUserMostPlayedBeatmapsRequest.cs | 96 +- .../GetUserRecentActivitiesRequest.cs | 260 +-- .../Online/API/Requests/GetUserRequest.cs | 38 +- .../API/Requests/GetUserScoresRequest.cs | 62 +- .../Online/API/Requests/GetUsersRequest.cs | 40 +- .../API/Requests/ListChannelsRequest.cs | 26 +- .../Online/API/Requests/PostMessageRequest.cs | 68 +- .../API/Requests/SearchBeatmapSetsRequest.cs | 66 +- osu.Game/Online/Chat/Channel.cs | 210 +- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 118 +- osu.Game/Online/Chat/ErrorMessage.cs | 26 +- osu.Game/Online/Chat/InfoMessage.cs | 50 +- osu.Game/Online/Chat/LocalEchoMessage.cs | 24 +- osu.Game/Online/Chat/Message.cs | 166 +- osu.Game/Online/Chat/MessageFormatter.cs | 556 ++--- osu.Game/Online/Multiplayer/GameType.cs | 292 +-- osu.Game/Online/Multiplayer/Room.cs | 40 +- osu.Game/Online/Multiplayer/RoomStatus.cs | 52 +- osu.Game/OsuGame.cs | 1102 ++++----- osu.Game/OsuGameBase.cs | 520 ++--- osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 262 +-- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 260 +-- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 624 ++--- osu.Game/Overlays/BeatmapSet/Details.cs | 240 +- .../Overlays/BeatmapSet/DownloadButton.cs | 118 +- .../Overlays/BeatmapSet/FavouriteButton.cs | 160 +- osu.Game/Overlays/BeatmapSet/Header.cs | 540 ++--- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 56 +- osu.Game/Overlays/BeatmapSet/Info.cs | 390 ++-- osu.Game/Overlays/BeatmapSet/PreviewButton.cs | 216 +- .../BeatmapSet/Scores/ClickableUsername.cs | 124 +- .../BeatmapSet/Scores/DrawableScore.cs | 284 +-- .../BeatmapSet/Scores/DrawableTopScore.cs | 486 ++-- .../BeatmapSet/Scores/ScoresContainer.cs | 230 +- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 230 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 314 +-- osu.Game/Overlays/Chat/ChannelListItem.cs | 382 ++-- osu.Game/Overlays/Chat/ChannelSection.cs | 126 +- .../Overlays/Chat/ChannelSelectionOverlay.cs | 370 +-- osu.Game/Overlays/Chat/ChatLine.cs | 512 ++--- osu.Game/Overlays/Chat/ChatTabControl.cs | 668 +++--- osu.Game/Overlays/Chat/DrawableChannel.cs | 268 +-- osu.Game/Overlays/ChatOverlay.cs | 1116 ++++----- osu.Game/Overlays/Dialog/PopupDialog.cs | 494 ++-- osu.Game/Overlays/Dialog/PopupDialogButton.cs | 36 +- .../Dialog/PopupDialogCancelButton.cs | 34 +- .../Overlays/Dialog/PopupDialogOkButton.cs | 34 +- osu.Game/Overlays/DialogOverlay.cs | 162 +- osu.Game/Overlays/Direct/DirectGridPanel.cs | 474 ++-- osu.Game/Overlays/Direct/DirectListPanel.cs | 338 +-- osu.Game/Overlays/Direct/DirectPanel.cs | 556 ++--- osu.Game/Overlays/Direct/DownloadButton.cs | 106 +- osu.Game/Overlays/Direct/FilterControl.cs | 234 +- osu.Game/Overlays/Direct/Header.cs | 76 +- osu.Game/Overlays/Direct/IconPill.cs | 86 +- osu.Game/Overlays/Direct/PlayButton.cs | 426 ++-- osu.Game/Overlays/DirectOverlay.cs | 696 +++--- .../KeyBinding/GlobalKeyBindingsSection.cs | 86 +- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 748 +++--- .../KeyBinding/KeyBindingsSubsection.cs | 150 +- .../KeyBinding/RulesetBindingsSection.cs | 54 +- .../KeyBinding/VariantBindingsSubsection.cs | 48 +- osu.Game/Overlays/KeyBindingOverlay.cs | 62 +- osu.Game/Overlays/LoginOverlay.cs | 184 +- osu.Game/Overlays/MainSettings.cs | 302 +-- osu.Game/Overlays/MedalOverlay.cs | 618 ++--- .../Overlays/MedalSplash/DrawableMedal.cs | 392 ++-- .../Mods/DifficultyIncreaseSection.cs | 54 +- .../Mods/DifficultyReductionSection.cs | 54 +- osu.Game/Overlays/Mods/ModButton.cs | 552 ++--- osu.Game/Overlays/Mods/ModButtonEmpty.cs | 40 +- osu.Game/Overlays/Mods/ModSection.cs | 304 +-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 766 +++---- osu.Game/Overlays/Mods/SpecialSection.cs | 54 +- .../Overlays/Music/CollectionsDropdown.cs | 148 +- osu.Game/Overlays/Music/FilterControl.cs | 152 +- osu.Game/Overlays/Music/PlaylistItem.cs | 364 +-- osu.Game/Overlays/Music/PlaylistList.cs | 510 ++--- osu.Game/Overlays/Music/PlaylistOverlay.cs | 354 +-- osu.Game/Overlays/MusicController.cs | 894 ++++---- osu.Game/Overlays/NotificationOverlay.cs | 366 +-- .../Notifications/IHasCompletionTarget.cs | 24 +- .../Overlays/Notifications/Notification.cs | 526 ++--- .../Notifications/NotificationSection.cs | 348 +-- .../ProgressCompletionNotification.cs | 46 +- .../Notifications/ProgressNotification.cs | 474 ++-- .../Notifications/SimpleNotification.cs | 186 +- osu.Game/Overlays/OnScreenDisplay.cs | 582 ++--- osu.Game/Overlays/Profile/ProfileHeader.cs | 998 ++++---- osu.Game/Overlays/Profile/ProfileSection.cs | 158 +- osu.Game/Overlays/Profile/RankGraph.cs | 438 ++-- .../Overlays/Profile/Sections/AboutSection.cs | 24 +- .../Sections/BeatmapMetadataContainer.cs | 126 +- .../Beatmaps/PaginatedBeatmapContainer.cs | 144 +- .../Profile/Sections/BeatmapsSection.cs | 52 +- .../Profile/Sections/DrawableProfileRow.cs | 248 +- .../Historical/DrawableMostPlayedRow.cs | 210 +- .../PaginatedMostPlayedBeatmapContainer.cs | 102 +- .../Profile/Sections/HistoricalSection.cs | 52 +- .../Profile/Sections/Kudosu/KudosuInfo.cs | 270 +-- .../Profile/Sections/KudosuSection.cs | 44 +- .../Profile/Sections/MedalsSection.cs | 24 +- .../Profile/Sections/PaginatedContainer.cs | 220 +- .../Ranks/DrawablePerformanceScore.cs | 98 +- .../Sections/Ranks/DrawableProfileScore.cs | 140 +- .../Sections/Ranks/DrawableTotalScore.cs | 62 +- .../Sections/Ranks/PaginatedScoreContainer.cs | 146 +- .../Sections/Ranks/ScoreModsContainer.cs | 42 +- .../Overlays/Profile/Sections/RanksSection.cs | 48 +- .../Sections/Recent/DrawableRecentActivity.cs | 324 +-- .../Profile/Sections/Recent/MedalIcon.cs | 76 +- .../PaginatedRecentActivityContainer.cs | 96 +- .../Profile/Sections/RecentSection.cs | 44 +- osu.Game/Overlays/Profile/SupporterIcon.cs | 128 +- .../SearchableList/DisplayStyleControl.cs | 204 +- .../SearchableList/HeaderTabControl.cs | 56 +- .../SearchableListFilterControl.cs | 268 +-- .../SearchableList/SearchableListHeader.cs | 166 +- .../SearchableList/SearchableListOverlay.cs | 246 +- .../SearchableList/SlimEnumDropdown.cs | 86 +- .../Settings/DangerousSettingsButton.cs | 46 +- .../Sections/Audio/AudioDevicesSettings.cs | 146 +- .../Sections/Audio/MainMenuSettings.cs | 62 +- .../Settings/Sections/Audio/OffsetSettings.cs | 76 +- .../Settings/Sections/Audio/VolumeSettings.cs | 54 +- .../Settings/Sections/AudioSection.cs | 52 +- .../Settings/Sections/Debug/GCSettings.cs | 68 +- .../Sections/Debug/GeneralSettings.cs | 74 +- .../Settings/Sections/DebugSection.cs | 48 +- .../Sections/Gameplay/GeneralSettings.cs | 88 +- .../Sections/Gameplay/ScrollingSettings.cs | 52 +- .../Sections/Gameplay/SongSelectSettings.cs | 100 +- .../Settings/Sections/GameplaySection.cs | 78 +- .../Sections/General/LanguageSettings.cs | 54 +- .../Sections/General/LoginSettings.cs | 758 +++---- .../Sections/General/UpdateSettings.cs | 66 +- .../Settings/Sections/GeneralSection.cs | 48 +- .../Sections/Graphics/DetailSettings.cs | 74 +- .../Sections/Graphics/LayoutSettings.cs | 152 +- .../Sections/Graphics/MainMenuSettings.cs | 52 +- .../Sections/Graphics/RendererSettings.cs | 70 +- .../Settings/Sections/GraphicsSection.cs | 52 +- .../Sections/Input/KeyboardSettings.cs | 48 +- .../Settings/Sections/Input/MouseSettings.cs | 290 +-- .../Settings/Sections/InputSection.cs | 48 +- .../Maintenance/DeleteAllBeatmapsDialog.cs | 64 +- .../Sections/Maintenance/GeneralSettings.cs | 146 +- .../Settings/Sections/MaintenanceSection.cs | 50 +- .../Settings/Sections/OnlineSection.cs | 42 +- .../Overlays/Settings/Sections/SkinSection.cs | 164 +- osu.Game/Overlays/Settings/SettingsButton.cs | 34 +- .../Overlays/Settings/SettingsCheckbox.cs | 42 +- .../Overlays/Settings/SettingsDropdown.cs | 74 +- .../Overlays/Settings/SettingsEnumDropdown.cs | 34 +- osu.Game/Overlays/Settings/SettingsFooter.cs | 146 +- osu.Game/Overlays/Settings/SettingsHeader.cs | 128 +- osu.Game/Overlays/Settings/SettingsItem.cs | 422 ++-- osu.Game/Overlays/Settings/SettingsLabel.cs | 40 +- osu.Game/Overlays/Settings/SettingsSection.cs | 182 +- osu.Game/Overlays/Settings/SettingsSlider.cs | 72 +- .../Overlays/Settings/SettingsSubsection.cs | 124 +- osu.Game/Overlays/Settings/SettingsTextBox.cs | 26 +- osu.Game/Overlays/Settings/Sidebar.cs | 280 +-- osu.Game/Overlays/Settings/SidebarButton.cs | 256 +-- osu.Game/Overlays/SettingsOverlay.cs | 448 ++-- osu.Game/Overlays/Social/FilterControl.cs | 64 +- osu.Game/Overlays/Social/Header.cs | 130 +- osu.Game/Overlays/Social/SocialGridPanel.cs | 30 +- osu.Game/Overlays/Social/SocialListPanel.cs | 32 +- osu.Game/Overlays/Social/SocialPanel.cs | 120 +- osu.Game/Overlays/SocialOverlay.cs | 426 ++-- osu.Game/Overlays/Toolbar/Toolbar.cs | 270 +-- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 398 ++-- .../Overlays/Toolbar/ToolbarChatButton.cs | 44 +- .../Overlays/Toolbar/ToolbarDirectButton.cs | 44 +- .../Overlays/Toolbar/ToolbarHomeButton.cs | 34 +- .../Overlays/Toolbar/ToolbarModeButton.cs | 114 +- .../Overlays/Toolbar/ToolbarModeSelector.cs | 254 +-- .../Overlays/Toolbar/ToolbarMusicButton.cs | 44 +- .../Toolbar/ToolbarNotificationButton.cs | 224 +- .../Toolbar/ToolbarOverlayToggleButton.cs | 128 +- .../Overlays/Toolbar/ToolbarSettingsButton.cs | 48 +- .../Overlays/Toolbar/ToolbarSocialButton.cs | 44 +- osu.Game/Overlays/Toolbar/ToolbarUserArea.cs | 82 +- .../Overlays/Toolbar/ToolbarUserButton.cs | 128 +- osu.Game/Overlays/UserProfileOverlay.cs | 454 ++-- osu.Game/Overlays/Volume/MuteButton.cs | 166 +- .../Overlays/Volume/VolumeControlReceptor.cs | 38 +- osu.Game/Overlays/Volume/VolumeMeter.cs | 386 ++-- osu.Game/Overlays/VolumeOverlay.cs | 294 +-- osu.Game/Overlays/WaveOverlayContainer.cs | 406 ++-- osu.Game/Properties/AssemblyInfo.cs | 22 +- .../Configuration/IRulesetConfigManager.cs | 22 +- .../Configuration/RulesetConfigManager.cs | 30 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 298 +-- osu.Game/Rulesets/Edit/HitObjectMask.cs | 292 +-- osu.Game/Rulesets/Edit/ToolboxGroup.cs | 38 +- .../Edit/Tools/HitObjectCompositionTool.cs | 26 +- .../Rulesets/Edit/Tools/ICompositionTool.cs | 20 +- .../Edit/Types/IHasEditablePosition.cs | 26 +- .../Rulesets/Judgements/DrawableJudgement.cs | 222 +- osu.Game/Rulesets/Judgements/Judgement.cs | 140 +- .../Rulesets/Mods/IApplicableFailOverride.cs | 32 +- osu.Game/Rulesets/Mods/IApplicableMod.cs | 26 +- .../Mods/IApplicableToBeatmapConverter.cs | 44 +- osu.Game/Rulesets/Mods/IApplicableToClock.cs | 30 +- .../Rulesets/Mods/IApplicableToDifficulty.cs | 30 +- .../Mods/IApplicableToDrawableHitObject.cs | 40 +- .../Rulesets/Mods/IApplicableToHitObject.cs | 40 +- .../Mods/IApplicableToRulesetContainer.cs | 42 +- .../Mods/IApplicableToScoreProcessor.cs | 30 +- osu.Game/Rulesets/Mods/Mod.cs | 118 +- osu.Game/Rulesets/Mods/ModAutoplay.cs | 68 +- osu.Game/Rulesets/Mods/ModCinema.cs | 32 +- osu.Game/Rulesets/Mods/ModDaycore.cs | 52 +- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 50 +- osu.Game/Rulesets/Mods/ModEasy.cs | 58 +- osu.Game/Rulesets/Mods/ModFlashlight.cs | 34 +- osu.Game/Rulesets/Mods/ModHalfTime.cs | 50 +- osu.Game/Rulesets/Mods/ModHardRock.cs | 56 +- osu.Game/Rulesets/Mods/ModHidden.cs | 32 +- osu.Game/Rulesets/Mods/ModNightcore.cs | 52 +- osu.Game/Rulesets/Mods/ModNoFail.cs | 50 +- osu.Game/Rulesets/Mods/ModPerfect.cs | 36 +- osu.Game/Rulesets/Mods/ModRelax.cs | 34 +- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 56 +- osu.Game/Rulesets/Mods/ModType.cs | 24 +- osu.Game/Rulesets/Mods/MultiMod.cs | 30 +- .../Rulesets/Objects/BezierApproximator.cs | 300 +-- .../Rulesets/Objects/CatmullApproximator.cs | 140 +- .../Objects/CircularArcApproximator.cs | 198 +- .../Rulesets/Objects/Drawables/ArmedState.cs | 24 +- .../Objects/Drawables/DrawableHitObject.cs | 490 ++-- .../IDrawableHitObjectWithProxiedApproach.cs | 24 +- .../Objects/Drawables/IScrollingHitObject.cs | 62 +- osu.Game/Rulesets/Objects/HitObject.cs | 204 +- osu.Game/Rulesets/Objects/HitObjectParser.cs | 20 +- osu.Game/Rulesets/Objects/HitWindows.cs | 346 +-- .../Objects/Legacy/Catch/ConvertHit.cs | 34 +- .../Legacy/Catch/ConvertHitObjectParser.cs | 104 +- .../Objects/Legacy/Catch/ConvertSlider.cs | 34 +- .../Objects/Legacy/Catch/ConvertSpinner.cs | 34 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 630 ++--- .../Objects/Legacy/ConvertHitObjectType.cs | 36 +- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 98 +- .../Objects/Legacy/Mania/ConvertHit.cs | 34 +- .../Legacy/Mania/ConvertHitObjectParser.cs | 114 +- .../Objects/Legacy/Mania/ConvertHold.cs | 32 +- .../Objects/Legacy/Mania/ConvertSlider.cs | 34 +- .../Objects/Legacy/Mania/ConvertSpinner.cs | 38 +- .../Rulesets/Objects/Legacy/Osu/ConvertHit.cs | 44 +- .../Legacy/Osu/ConvertHitObjectParser.cs | 108 +- .../Objects/Legacy/Osu/ConvertSlider.cs | 44 +- .../Objects/Legacy/Osu/ConvertSpinner.cs | 48 +- .../Objects/Legacy/Taiko/ConvertHit.cs | 30 +- .../Legacy/Taiko/ConvertHitObjectParser.cs | 100 +- .../Objects/Legacy/Taiko/ConvertSlider.cs | 30 +- .../Objects/Legacy/Taiko/ConvertSpinner.cs | 34 +- osu.Game/Rulesets/Objects/SliderCurve.cs | 410 ++-- osu.Game/Rulesets/Objects/Types/CurveType.cs | 26 +- osu.Game/Rulesets/Objects/Types/IHasCombo.cs | 32 +- .../Rulesets/Objects/Types/IHasComboIndex.cs | 52 +- .../Objects/Types/IHasComboInformation.cs | 52 +- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 128 +- .../Rulesets/Objects/Types/IHasDistance.cs | 32 +- .../Rulesets/Objects/Types/IHasEndTime.cs | 42 +- osu.Game/Rulesets/Objects/Types/IHasHold.cs | 32 +- .../Rulesets/Objects/Types/IHasPosition.cs | 36 +- .../Rulesets/Objects/Types/IHasRepeats.cs | 66 +- .../Rulesets/Objects/Types/IHasXPosition.cs | 32 +- .../Rulesets/Objects/Types/IHasYPosition.cs | 32 +- osu.Game/Rulesets/Replays/AutoGenerator.cs | 78 +- .../Replays/FramedReplayInputHandler.cs | 252 +- osu.Game/Rulesets/Replays/IAutoGenerator.cs | 20 +- .../Replays/Legacy/LegacyReplayFrame.cs | 76 +- .../Replays/Legacy/ReplayButtonState.cs | 36 +- osu.Game/Rulesets/Replays/Replay.cs | 28 +- osu.Game/Rulesets/Replays/ReplayFrame.cs | 38 +- .../Replays/Types/IConvertibleReplayFrame.cs | 42 +- osu.Game/Rulesets/Ruleset.cs | 224 +- osu.Game/Rulesets/RulesetInfo.cs | 58 +- osu.Game/Rulesets/RulesetStore.cs | 244 +- osu.Game/Rulesets/Scoring/HitResult.cs | 86 +- .../Scoring/Legacy/LegacyScoreParser.cs | 304 +-- .../Rulesets/Scoring/PerformanceCalculator.cs | 86 +- osu.Game/Rulesets/Scoring/Score.cs | 90 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 654 +++--- osu.Game/Rulesets/Scoring/ScoreRank.cs | 58 +- osu.Game/Rulesets/Scoring/ScoreStore.cs | 112 +- .../Rulesets/Timing/MultiplierControlPoint.cs | 132 +- osu.Game/Rulesets/UI/HitObjectContainer.cs | 60 +- osu.Game/Rulesets/UI/JudgementContainer.cs | 48 +- osu.Game/Rulesets/UI/ModIcon.cs | 224 +- osu.Game/Rulesets/UI/Playfield.cs | 166 +- osu.Game/Rulesets/UI/RulesetContainer.cs | 788 +++---- osu.Game/Rulesets/UI/RulesetInputManager.cs | 540 ++--- osu.Game/Rulesets/UI/ScalableContainer.cs | 198 +- .../UI/Scrolling/ScrollingDirection.cs | 50 +- .../Scrolling/ScrollingHitObjectContainer.cs | 232 +- .../UI/Scrolling/ScrollingPlayfield.cs | 254 +-- .../UI/Scrolling/ScrollingRulesetContainer.cs | 220 +- .../Visualisers/ISpeedChangeVisualiser.cs | 66 +- .../OverlappingSpeedChangeVisualiser.cs | 160 +- .../SequentialSpeedChangeVisualiser.cs | 206 +- osu.Game/Screens/BackgroundScreen.cs | 170 +- .../Backgrounds/BackgroundScreenBeatmap.cs | 168 +- .../Backgrounds/BackgroundScreenCustom.cs | 52 +- .../Backgrounds/BackgroundScreenDefault.cs | 92 +- .../Backgrounds/BackgroundScreenEmpty.cs | 18 +- osu.Game/Screens/Charts/ChartInfo.cs | 18 +- osu.Game/Screens/Charts/ChartListing.cs | 30 +- osu.Game/Screens/Direct/OnlineListing.cs | 18 +- .../Edit/Components/BottomBarContainer.cs | 100 +- .../Edit/Components/PlaybackControl.cs | 320 +-- .../Edit/Components/TimeInfoContainer.cs | 94 +- .../Timelines/Summary/Parts/BookmarkPart.cs | 68 +- .../Timelines/Summary/Parts/BreakPart.cs | 70 +- .../Summary/Parts/ControlPointPart.cs | 140 +- .../Timelines/Summary/Parts/MarkerPart.cs | 222 +- .../Timelines/Summary/Parts/TimelinePart.cs | 108 +- .../Timelines/Summary/SummaryTimeline.cs | 170 +- .../Visualisations/DurationVisualisation.cs | 56 +- .../Visualisations/PointVisualisation.cs | 54 +- osu.Game/Screens/Edit/Editor.cs | 434 ++-- osu.Game/Screens/Edit/EditorClock.cs | 234 +- osu.Game/Screens/Edit/Menus/EditorMenuBar.cs | 380 ++-- osu.Game/Screens/Edit/Menus/EditorMenuItem.cs | 46 +- .../Edit/Menus/EditorMenuItemSpacer.cs | 26 +- .../Edit/Menus/ScreenSelectionTabControl.cs | 150 +- .../Screens/Compose/BeatDivisorControl.cs | 794 +++---- .../Screens/Compose/BindableBeatDivisor.cs | 78 +- .../Screens/Edit/Screens/Compose/Compose.cs | 236 +- .../Screens/Compose/Layers/BorderLayer.cs | 76 +- .../Edit/Screens/Compose/Layers/DragLayer.cs | 184 +- .../Compose/Layers/HitObjectMaskLayer.cs | 186 +- .../Screens/Compose/Layers/MaskContainer.cs | 246 +- .../Screens/Compose/Layers/MaskSelection.cs | 328 +-- .../RadioButtons/DrawableRadioButton.cs | 246 +- .../Compose/RadioButtons/RadioButton.cs | 102 +- .../RadioButtons/RadioButtonCollection.cs | 122 +- .../Compose/Timeline/BeatmapWaveformGraph.cs | 66 +- .../Compose/Timeline/ScrollableTimeline.cs | 262 +-- .../Timeline/ScrollingTimelineContainer.cs | 282 +-- .../Compose/Timeline/TimelineButton.cs | 104 +- .../Screens/Edit/Screens/Design/Design.cs | 104 +- osu.Game/Screens/Edit/Screens/EditorScreen.cs | 88 +- .../Screens/Edit/Screens/EditorScreenMode.cs | 38 +- osu.Game/Screens/Loader.cs | 236 +- osu.Game/Screens/Menu/Button.cs | 574 ++--- osu.Game/Screens/Menu/ButtonSystem.cs | 752 +++--- osu.Game/Screens/Menu/Disclaimer.cs | 218 +- .../Screens/Menu/FlowContainerWithOrigin.cs | 72 +- osu.Game/Screens/Menu/Intro.cs | 346 +-- osu.Game/Screens/Menu/IntroSequence.cs | 592 ++--- osu.Game/Screens/Menu/LogoVisualisation.cs | 448 ++-- osu.Game/Screens/Menu/MainMenu.cs | 376 +-- osu.Game/Screens/Menu/MenuSideFlashes.cs | 198 +- osu.Game/Screens/Menu/OsuLogo.cs | 766 +++---- .../Screens/Multiplayer/DrawableGameType.cs | 82 +- osu.Game/Screens/Multiplayer/DrawableRoom.cs | 524 ++--- osu.Game/Screens/Multiplayer/Lobby.cs | 32 +- osu.Game/Screens/Multiplayer/Match.cs | 74 +- osu.Game/Screens/Multiplayer/MatchCreate.cs | 40 +- osu.Game/Screens/Multiplayer/ModeTypeInfo.cs | 164 +- .../Screens/Multiplayer/ParticipantInfo.cs | 290 +-- osu.Game/Screens/Multiplayer/RoomInspector.cs | 822 +++---- osu.Game/Screens/OsuScreen.cs | 466 ++-- osu.Game/Screens/Play/Break/BlurredIcon.cs | 102 +- osu.Game/Screens/Play/Break/BreakArrows.cs | 200 +- osu.Game/Screens/Play/Break/BreakInfo.cs | 104 +- osu.Game/Screens/Play/Break/BreakInfoLine.cs | 164 +- osu.Game/Screens/Play/Break/GlowIcon.cs | 130 +- .../Screens/Play/Break/LetterboxOverlay.cs | 100 +- .../Play/Break/RemainingTimeCounter.cs | 58 +- osu.Game/Screens/Play/BreakOverlay.cs | 310 +-- osu.Game/Screens/Play/FailOverlay.cs | 72 +- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 564 ++--- osu.Game/Screens/Play/HUD/ComboCounter.cs | 392 ++-- .../Screens/Play/HUD/ComboResultCounter.cs | 64 +- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 48 +- osu.Game/Screens/Play/HUD/ModDisplay.cs | 204 +- .../Screens/Play/HUD/PlayerSettingsOverlay.cs | 142 +- .../Screens/Play/HUD/StandardComboCounter.cs | 282 +-- .../Screens/Play/HUD/StandardHealthDisplay.cs | 214 +- osu.Game/Screens/Play/HUDOverlay.cs | 460 ++-- osu.Game/Screens/Play/HotkeyRetryOverlay.cs | 138 +- osu.Game/Screens/Play/KeyCounter.cs | 266 +-- osu.Game/Screens/Play/KeyCounterAction.cs | 60 +- osu.Game/Screens/Play/KeyCounterCollection.cs | 308 +-- osu.Game/Screens/Play/KeyCounterKeyboard.cs | 58 +- osu.Game/Screens/Play/KeyCounterMouse.cs | 92 +- osu.Game/Screens/Play/PauseContainer.cs | 312 +-- osu.Game/Screens/Play/Player.cs | 804 +++---- osu.Game/Screens/Play/PlayerLoader.cs | 646 +++--- .../Play/PlayerSettings/CollectionSettings.cs | 66 +- .../Play/PlayerSettings/DiscussionSettings.cs | 70 +- .../Play/PlayerSettings/PlaybackSettings.cs | 152 +- .../Play/PlayerSettings/PlayerCheckbox.cs | 40 +- .../PlayerSettings/PlayerSettingsGroup.cs | 282 +-- .../Play/PlayerSettings/PlayerSliderBar.cs | 72 +- .../Play/PlayerSettings/VisualSettings.cs | 98 +- osu.Game/Screens/Play/ReplayPlayer.cs | 46 +- .../Play/ScreenWithBeatmapBackground.cs | 126 +- osu.Game/Screens/Play/SkipOverlay.cs | 652 +++--- osu.Game/Screens/Play/SongProgress.cs | 336 +-- osu.Game/Screens/Play/SongProgressBar.cs | 234 +- osu.Game/Screens/Play/SongProgressGraph.cs | 100 +- osu.Game/Screens/Play/SongProgressInfo.cs | 190 +- osu.Game/Screens/Play/SquareGraph.cs | 500 ++-- osu.Game/Screens/Ranking/AspectContainer.cs | 40 +- osu.Game/Screens/Ranking/ResultMode.cs | 24 +- osu.Game/Screens/Ranking/ResultModeButton.cs | 198 +- .../Screens/Ranking/ResultModeTabControl.cs | 60 +- osu.Game/Screens/Ranking/Results.cs | 578 ++--- osu.Game/Screens/Ranking/ResultsPage.cs | 182 +- .../Screens/Ranking/ResultsPageRanking.cs | 84 +- osu.Game/Screens/Ranking/ResultsPageScore.cs | 784 +++---- osu.Game/Screens/ScreenWhiteBox.cs | 400 ++-- osu.Game/Screens/Select/BeatmapCarousel.cs | 1300 +++++------ .../Screens/Select/BeatmapDeleteDialog.cs | 82 +- osu.Game/Screens/Select/BeatmapDetailArea.cs | 180 +- .../Select/BeatmapDetailAreaTabControl.cs | 170 +- osu.Game/Screens/Select/BeatmapDetails.cs | 832 +++---- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 728 +++--- .../Select/Carousel/CarouselBeatmap.cs | 110 +- .../Select/Carousel/CarouselBeatmapSet.cs | 116 +- .../Screens/Select/Carousel/CarouselGroup.cs | 178 +- .../Carousel/CarouselGroupEagerSelect.cs | 224 +- .../Screens/Select/Carousel/CarouselItem.cs | 124 +- .../Carousel/DrawableCarouselBeatmap.cs | 352 +-- .../Carousel/DrawableCarouselBeatmapSet.cs | 366 +-- .../Select/Carousel/DrawableCarouselItem.cs | 308 +-- .../Screens/Select/Details/AdvancedStats.cs | 304 +-- .../Screens/Select/Details/FailRetryGraph.cs | 124 +- .../Screens/Select/Details/UserRatings.cs | 256 +-- osu.Game/Screens/Select/EditSongSelect.cs | 32 +- osu.Game/Screens/Select/Filter/GroupMode.cs | 82 +- osu.Game/Screens/Select/Filter/SortMode.cs | 54 +- osu.Game/Screens/Select/FilterControl.cs | 392 ++-- osu.Game/Screens/Select/FilterCriteria.cs | 34 +- osu.Game/Screens/Select/Footer.cs | 294 +-- osu.Game/Screens/Select/FooterButton.cs | 270 +-- .../Screens/Select/ImportFromStablePopup.cs | 66 +- .../Select/Leaderboards/DrawableRank.cs | 104 +- .../Select/Leaderboards/Leaderboard.cs | 682 +++--- .../Select/Leaderboards/LeaderboardScore.cs | 688 +++--- .../Select/Leaderboards/MessagePlaceholder.cs | 76 +- .../Select/Leaderboards/Placeholder.cs | 40 +- .../RetrievalFailurePlaceholder.cs | 154 +- osu.Game/Screens/Select/MatchSongSelect.cs | 28 +- .../Select/Options/BeatmapOptionsButton.cs | 336 +-- .../Select/Options/BeatmapOptionsOverlay.cs | 234 +- osu.Game/Screens/Select/PlaySongSelect.cs | 342 +-- osu.Game/Screens/Select/SongSelect.cs | 1038 ++++----- osu.Game/Screens/Select/WedgeBackground.cs | 80 +- .../Components/DrawingsConfigManager.cs | 60 +- .../Components/VisualiserContainer.cs | 248 +- osu.Game/Screens/Tournament/Drawings.cs | 702 +++--- osu.Game/Screens/Tournament/Group.cs | 374 +-- osu.Game/Screens/Tournament/GroupContainer.cs | 210 +- .../Tournament/ScrollingTeamContainer.cs | 762 +++---- .../Screens/Tournament/Teams/DrawingsTeam.cs | 46 +- .../Screens/Tournament/Teams/ITeamList.cs | 24 +- .../Tournament/Teams/StorageBackedTeamList.cs | 148 +- osu.Game/Skinning/DefaultSkin.cs | 68 +- osu.Game/Skinning/ISkinSource.cs | 56 +- osu.Game/Skinning/LegacyBeatmapSkin.cs | 40 +- osu.Game/Skinning/LegacySkin.cs | 236 +- osu.Game/Skinning/LegacySkinDecoder.cs | 76 +- .../Skinning/LocalSkinOverrideContainer.cs | 158 +- osu.Game/Skinning/Skin.cs | 120 +- osu.Game/Skinning/SkinConfiguration.cs | 36 +- osu.Game/Skinning/SkinFileInfo.cs | 50 +- osu.Game/Skinning/SkinInfo.cs | 60 +- osu.Game/Skinning/SkinManager.cs | 254 +-- osu.Game/Skinning/SkinReloadableDrawable.cs | 112 +- osu.Game/Skinning/SkinStore.cs | 44 +- osu.Game/Skinning/SkinnableDrawable.cs | 140 +- osu.Game/Skinning/SkinnableSound.cs | 124 +- osu.Game/Storyboards/CommandLoop.cs | 70 +- osu.Game/Storyboards/CommandTimeline.cs | 154 +- osu.Game/Storyboards/CommandTimelineGroup.cs | 162 +- osu.Game/Storyboards/CommandTrigger.cs | 48 +- .../Drawables/DrawableStoryboard.cs | 144 +- .../Drawables/DrawableStoryboardAnimation.cs | 166 +- .../Drawables/DrawableStoryboardLayer.cs | 72 +- .../Drawables/DrawableStoryboardSprite.cs | 152 +- .../Drawables/DrawablesExtensions.cs | 60 +- osu.Game/Storyboards/Drawables/IFlippable.cs | 110 +- osu.Game/Storyboards/IStoryboardElement.cs | 30 +- osu.Game/Storyboards/Storyboard.cs | 168 +- osu.Game/Storyboards/StoryboardAnimation.cs | 66 +- osu.Game/Storyboards/StoryboardLayer.cs | 66 +- osu.Game/Storyboards/StoryboardSample.cs | 58 +- osu.Game/Storyboards/StoryboardSprite.cs | 230 +- .../Tests/Beatmaps/BeatmapConversionTest.cs | 288 +-- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1440 ++++++------ osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 92 +- osu.Game/Tests/CleanRunHeadlessGameHost.cs | 38 +- osu.Game/Tests/OsuTestBrowser.cs | 58 +- osu.Game/Tests/Platform/TestStorage.cs | 38 +- osu.Game/Tests/Visual/EditorClockTestCase.cs | 158 +- osu.Game/Tests/Visual/EditorTestCase.cs | 66 +- .../Visual/ManualInputManagerTestCase.cs | 58 +- osu.Game/Tests/Visual/OsuTestCase.cs | 74 +- osu.Game/Tests/Visual/ScreenTestCase.cs | 96 +- .../Tests/Visual/TestCasePerformancePoints.cs | 800 +++---- osu.Game/Tests/Visual/TestCasePlayer.cs | 196 +- osu.Game/Tests/VisualTestRunner.cs | 44 +- osu.Game/Users/Avatar.cs | 92 +- osu.Game/Users/Country.cs | 152 +- osu.Game/Users/Medal.cs | 26 +- osu.Game/Users/Team.cs | 20 +- osu.Game/Users/UpdateableAvatar.cs | 104 +- osu.Game/Users/User.cs | 284 +-- osu.Game/Users/UserCoverBackground.cs | 60 +- osu.Game/Users/UserPanel.cs | 468 ++-- osu.Game/Users/UserStatistics.cs | 166 +- osu.Game/Users/UserStatus.cs | 134 +- osu.Game/Utils/ZipUtils.cs | 66 +- osu.sln | 260 +-- 1069 files changed, 95293 insertions(+), 95293 deletions(-) diff --git a/.gitignore b/.gitignore index 33ff47e0df..5138e940ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,259 +1,259 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -bin/[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc -Staging/ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +bin/[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc +Staging/ diff --git a/.travis.yml b/.travis.yml index cb2b6d92db..ce353d9b27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,2 +1,2 @@ -language: csharp +language: csharp solution: osu.sln \ No newline at end of file diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config index f6673fef1a..9ec53d5a31 100644 --- a/osu.Desktop.Deploy/App.config +++ b/osu.Desktop.Deploy/App.config @@ -1,21 +1,21 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/osu.Desktop.Deploy/GitHubObject.cs b/osu.Desktop.Deploy/GitHubObject.cs index 59c15f8015..02ff16fa69 100644 --- a/osu.Desktop.Deploy/GitHubObject.cs +++ b/osu.Desktop.Deploy/GitHubObject.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; - -namespace osu.Desktop.Deploy -{ - public class GitHubObject - { - [JsonProperty(@"id")] - public int Id; - - [JsonProperty(@"name")] - public string Name; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; + +namespace osu.Desktop.Deploy +{ + public class GitHubObject + { + [JsonProperty(@"id")] + public int Id; + + [JsonProperty(@"name")] + public string Name; + } +} diff --git a/osu.Desktop.Deploy/GitHubRelease.cs b/osu.Desktop.Deploy/GitHubRelease.cs index 22820b047a..faf312d4f7 100644 --- a/osu.Desktop.Deploy/GitHubRelease.cs +++ b/osu.Desktop.Deploy/GitHubRelease.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; - -namespace osu.Desktop.Deploy -{ - public class GitHubRelease - { - [JsonProperty(@"id")] - public int Id; - - [JsonProperty(@"tag_name")] - public string TagName => $"v{Name}"; - - [JsonProperty(@"name")] - public string Name; - - [JsonProperty(@"draft")] - public bool Draft; - - [JsonProperty(@"prerelease")] - public bool PreRelease; - - [JsonProperty(@"upload_url")] - public string UploadUrl; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; + +namespace osu.Desktop.Deploy +{ + public class GitHubRelease + { + [JsonProperty(@"id")] + public int Id; + + [JsonProperty(@"tag_name")] + public string TagName => $"v{Name}"; + + [JsonProperty(@"name")] + public string Name; + + [JsonProperty(@"draft")] + public bool Draft; + + [JsonProperty(@"prerelease")] + public bool PreRelease; + + [JsonProperty(@"upload_url")] + public string UploadUrl; + } +} diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 3c1451d555..6095ce062d 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -1,438 +1,438 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Diagnostics; -using System.IO; -using System.Linq; -using Newtonsoft.Json; -using osu.Framework.IO.Network; -using FileWebRequest = osu.Framework.IO.Network.FileWebRequest; -using WebRequest = osu.Framework.IO.Network.WebRequest; - -namespace osu.Desktop.Deploy -{ - internal static class Program - { - private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); - private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe"); - private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.7.8\tools\Squirrel.exe"); - private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"; - - public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"]; - public static string ReleasesFolder = ConfigurationManager.AppSettings["ReleasesFolder"]; - public static string GitHubAccessToken = ConfigurationManager.AppSettings["GitHubAccessToken"]; - public static string GitHubUsername = ConfigurationManager.AppSettings["GitHubUsername"]; - public static string GitHubRepoName = ConfigurationManager.AppSettings["GitHubRepoName"]; - public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"]; - public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"]; - public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"]; - public static string TargetNames = ConfigurationManager.AppSettings["TargetName"]; - public static string PackageName = ConfigurationManager.AppSettings["PackageName"]; - public static string IconName = ConfigurationManager.AppSettings["IconName"]; - public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"]; - - public static string GitHubApiEndpoint => $"https://api.github.com/repos/{GitHubUsername}/{GitHubRepoName}/releases"; - public static string GitHubReleasePage => $"https://github.com/{GitHubUsername}/{GitHubRepoName}/releases"; - - /// - /// How many previous build deltas we want to keep when publishing. - /// - private const int keep_delta_count = 4; - - private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\""; - - private static string homeDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - private static string codeSigningCertPath => Path.Combine(homeDir, CodeSigningCertificate); - private static string solutionPath => Environment.CurrentDirectory; - private static string stagingPath => Path.Combine(solutionPath, StagingFolder); - private static string iconPath => Path.Combine(solutionPath, ProjectName, IconName); - - private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg"; - private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg"; - - private static readonly Stopwatch sw = new Stopwatch(); - - private static string codeSigningPassword; - - public static void Main(string[] args) - { - displayHeader(); - - findSolutionPath(); - - if (!Directory.Exists(ReleasesFolder)) - { - write("WARNING: No release directory found. Make sure you want this!", ConsoleColor.Yellow); - Directory.CreateDirectory(ReleasesFolder); - } - - checkGitHubReleases(); - - refreshDirectory(StagingFolder); - - //increment build number until we have a unique one. - string verBase = DateTime.Now.ToString("yyyy.Mdd."); - int increment = 0; - while (Directory.GetFiles(ReleasesFolder, $"*{verBase}{increment}*").Any()) - increment++; - - string version = $"{verBase}{increment}"; - - Console.ForegroundColor = ConsoleColor.White; - Console.Write($"Ready to deploy {version}: "); - Console.ReadLine(); - - sw.Start(); - - if (!string.IsNullOrEmpty(CodeSigningCertificate)) - { - Console.Write("Enter code signing password: "); - codeSigningPassword = readLineMasked(); - } - - write("Updating AssemblyInfo..."); - updateCsprojVersion(version); - - write("Running build process..."); - foreach (string targetName in TargetNames.Split(',')) - runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln"); - - write("Creating NuGet deployment package..."); - runCommand(nugetPath, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}"); - - //prune once before checking for files so we can avoid erroring on files which aren't even needed for this build. - pruneReleases(); - - checkReleaseFiles(); - - write("Running squirrel build..."); - runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi"); - - //prune again to clean up before upload. - pruneReleases(); - - //rename setup to install. - File.Copy(Path.Combine(ReleasesFolder, "Setup.exe"), Path.Combine(ReleasesFolder, "install.exe"), true); - File.Delete(Path.Combine(ReleasesFolder, "Setup.exe")); - - uploadBuild(version); - - //reset assemblyinfo. - updateCsprojVersion("0.0.0"); - - write("Done!", ConsoleColor.White); - Console.ReadLine(); - } - - private static void displayHeader() - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(); - Console.WriteLine(" Please note that OSU! and PPY are registered trademarks and as such covered by trademark law."); - Console.WriteLine(" Do not distribute builds of this project publicly that make use of these."); - Console.ResetColor(); - Console.WriteLine(); - } - - /// - /// Ensure we have all the files in the release directory which are expected to be there. - /// This should have been accounted for in earlier steps, and just serves as a verification step. - /// - private static void checkReleaseFiles() - { - if (!canGitHub) return; - - var releaseLines = getReleaseLines(); - - //ensure we have all files necessary - foreach (var l in releaseLines) - if (!File.Exists(Path.Combine(ReleasesFolder, l.Filename))) - error($"Local file missing {l.Filename}"); - } - - private static IEnumerable getReleaseLines() => File.ReadAllLines(Path.Combine(ReleasesFolder, "RELEASES")).Select(l => new ReleaseLine(l)); - - private static void pruneReleases() - { - if (!canGitHub) return; - - write("Pruning RELEASES..."); - - var releaseLines = getReleaseLines().ToList(); - - var fulls = releaseLines.Where(l => l.Filename.Contains("-full")).Reverse().Skip(1); - - //remove any FULL releases (except most recent) - foreach (var l in fulls) - { - write($"- Removing old release {l.Filename}", ConsoleColor.Yellow); - File.Delete(Path.Combine(ReleasesFolder, l.Filename)); - releaseLines.Remove(l); - } - - //remove excess deltas - var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray(); - if (deltas.Length > keep_delta_count) - { - foreach (var l in deltas.Take(deltas.Length - keep_delta_count)) - { - write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow); - File.Delete(Path.Combine(ReleasesFolder, l.Filename)); - releaseLines.Remove(l); - } - } - - var lines = new List(); - releaseLines.ForEach(l => lines.Add(l.ToString())); - File.WriteAllLines(Path.Combine(ReleasesFolder, "RELEASES"), lines); - } - - private static void uploadBuild(string version) - { - if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate)) - return; - - write("Publishing to GitHub..."); - - write($"- Creating release {version}...", ConsoleColor.Yellow); - var req = new JsonWebRequest($"{GitHubApiEndpoint}") - { - Method = HttpMethod.POST, - }; - req.AddRaw(JsonConvert.SerializeObject(new GitHubRelease - { - Name = version, - Draft = true, - PreRelease = true - })); - req.AuthenticatedBlockingPerform(); - - var assetUploadUrl = req.ResponseObject.UploadUrl.Replace("{?name,label}", "?name={0}"); - foreach (var a in Directory.GetFiles(ReleasesFolder).Reverse()) //reverse to upload RELEASES first. - { - write($"- Adding asset {a}...", ConsoleColor.Yellow); - var upload = new WebRequest(assetUploadUrl, Path.GetFileName(a)) - { - Method = HttpMethod.POST, - Timeout = 240000, - ContentType = "application/octet-stream", - }; - - upload.AddRaw(File.ReadAllBytes(a)); - upload.AuthenticatedBlockingPerform(); - } - - openGitHubReleasePage(); - } - - private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage); - - private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken); - - private static void checkGitHubReleases() - { - if (!canGitHub) return; - - write("Checking GitHub releases..."); - var req = new JsonWebRequest>($"{GitHubApiEndpoint}"); - req.AuthenticatedBlockingPerform(); - - var lastRelease = req.ResponseObject.FirstOrDefault(); - - if (lastRelease == null) - return; - - if (lastRelease.Draft) - { - openGitHubReleasePage(); - error("There's a pending draft release! You probably don't want to push a build with this present."); - } - - //there's a previous release for this project. - var assetReq = new JsonWebRequest>($"{GitHubApiEndpoint}/{lastRelease.Id}/assets"); - assetReq.AuthenticatedBlockingPerform(); - var assets = assetReq.ResponseObject; - - //make sure our RELEASES file is the same as the last build on the server. - var releaseAsset = assets.FirstOrDefault(a => a.Name == "RELEASES"); - - //if we don't have a RELEASES asset then the previous release likely wasn't a Squirrel one. - if (releaseAsset == null) return; - - write($"Last GitHub release was {lastRelease.Name}."); - - bool requireDownload = false; - - if (!File.Exists(Path.Combine(ReleasesFolder, nupkgDistroFilename(lastRelease.Name)))) - { - write("Last version's package not found locally.", ConsoleColor.Red); - requireDownload = true; - } - else - { - var lastReleases = new RawFileWebRequest($"{GitHubApiEndpoint}/assets/{releaseAsset.Id}"); - lastReleases.AuthenticatedBlockingPerform(); - if (File.ReadAllText(Path.Combine(ReleasesFolder, "RELEASES")) != lastReleases.ResponseString) - { - write("Server's RELEASES differed from ours.", ConsoleColor.Red); - requireDownload = true; - } - } - - if (!requireDownload) return; - - write("Refreshing local releases directory..."); - refreshDirectory(ReleasesFolder); - - foreach (var a in assets) - { - if (a.Name.EndsWith(".exe")) continue; - - write($"- Downloading {a.Name}...", ConsoleColor.Yellow); - new FileWebRequest(Path.Combine(ReleasesFolder, a.Name), $"{GitHubApiEndpoint}/assets/{a.Id}").AuthenticatedBlockingPerform(); - } - } - - private static void refreshDirectory(string directory) - { - if (Directory.Exists(directory)) - Directory.Delete(directory, true); - Directory.CreateDirectory(directory); - } - - private static void updateCsprojVersion(string version) - { - var toUpdate = new[] { "", "" }; - string file = Path.Combine(ProjectName, $"{ProjectName}.csproj"); - - var l1 = File.ReadAllLines(file); - List l2 = new List(); - foreach (var l in l1) - { - string line = l; - - foreach (var tag in toUpdate) - { - int startIndex = l.IndexOf(tag, StringComparison.InvariantCulture); - if (startIndex == -1) - continue; - startIndex += tag.Length; - - int endIndex = l.IndexOf("<", startIndex, StringComparison.InvariantCulture); - line = $"{l.Substring(0, startIndex)}{version}{l.Substring(endIndex)}"; - } - - l2.Add(line); - } - - File.WriteAllLines(file, l2); - } - - /// - /// Find the base path of the active solution (git checkout location) - /// - private static void findSolutionPath() - { - string path = Path.GetDirectoryName(Environment.CommandLine.Replace("\"", "").Trim()); - - if (string.IsNullOrEmpty(path)) - path = Environment.CurrentDirectory; - - while (!File.Exists(Path.Combine(path, $"{SolutionName}.sln"))) - path = path.Remove(path.LastIndexOf(Path.DirectorySeparatorChar)); - path += Path.DirectorySeparatorChar; - - Environment.CurrentDirectory = path; - } - - private static bool runCommand(string command, string args) - { - var psi = new ProcessStartInfo(command, args) - { - WorkingDirectory = solutionPath, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - WindowStyle = ProcessWindowStyle.Hidden - }; - - Process p = Process.Start(psi); - if (p == null) return false; - - string output = p.StandardOutput.ReadToEnd(); - output += p.StandardError.ReadToEnd(); - - if (p.ExitCode == 0) return true; - - write(output); - error($"Command {command} {args} failed!"); - return false; - } - - private static string readLineMasked() - { - var fg = Console.ForegroundColor; - Console.ForegroundColor = Console.BackgroundColor; - var ret = Console.ReadLine(); - Console.ForegroundColor = fg; - - return ret; - } - - private static void error(string message) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"FATAL ERROR: {message}"); - - Console.ReadLine(); - Environment.Exit(-1); - } - - private static void write(string message, ConsoleColor col = ConsoleColor.Gray) - { - if (sw.ElapsedMilliseconds > 0) - { - Console.ForegroundColor = ConsoleColor.Green; - Console.Write(sw.ElapsedMilliseconds.ToString().PadRight(8)); - } - Console.ForegroundColor = col; - Console.WriteLine(message); - } - - public static void AuthenticatedBlockingPerform(this WebRequest r) - { - r.AddHeader("Authorization", $"token {GitHubAccessToken}"); - r.Perform(); - } - } - - internal class RawFileWebRequest : WebRequest - { - public RawFileWebRequest(string url) : base(url) - { - } - - protected override string Accept => "application/octet-stream"; - } - - internal class ReleaseLine - { - public string Hash; - public string Filename; - public int Filesize; - - public ReleaseLine(string line) - { - var split = line.Split(' '); - Hash = split[0]; - Filename = split[1]; - Filesize = int.Parse(split[2]); - } - - public override string ToString() => $"{Hash} {Filename} {Filesize}"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using osu.Framework.IO.Network; +using FileWebRequest = osu.Framework.IO.Network.FileWebRequest; +using WebRequest = osu.Framework.IO.Network.WebRequest; + +namespace osu.Desktop.Deploy +{ + internal static class Program + { + private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); + private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe"); + private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.7.8\tools\Squirrel.exe"); + private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"; + + public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"]; + public static string ReleasesFolder = ConfigurationManager.AppSettings["ReleasesFolder"]; + public static string GitHubAccessToken = ConfigurationManager.AppSettings["GitHubAccessToken"]; + public static string GitHubUsername = ConfigurationManager.AppSettings["GitHubUsername"]; + public static string GitHubRepoName = ConfigurationManager.AppSettings["GitHubRepoName"]; + public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"]; + public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"]; + public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"]; + public static string TargetNames = ConfigurationManager.AppSettings["TargetName"]; + public static string PackageName = ConfigurationManager.AppSettings["PackageName"]; + public static string IconName = ConfigurationManager.AppSettings["IconName"]; + public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"]; + + public static string GitHubApiEndpoint => $"https://api.github.com/repos/{GitHubUsername}/{GitHubRepoName}/releases"; + public static string GitHubReleasePage => $"https://github.com/{GitHubUsername}/{GitHubRepoName}/releases"; + + /// + /// How many previous build deltas we want to keep when publishing. + /// + private const int keep_delta_count = 4; + + private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\""; + + private static string homeDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + private static string codeSigningCertPath => Path.Combine(homeDir, CodeSigningCertificate); + private static string solutionPath => Environment.CurrentDirectory; + private static string stagingPath => Path.Combine(solutionPath, StagingFolder); + private static string iconPath => Path.Combine(solutionPath, ProjectName, IconName); + + private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg"; + private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg"; + + private static readonly Stopwatch sw = new Stopwatch(); + + private static string codeSigningPassword; + + public static void Main(string[] args) + { + displayHeader(); + + findSolutionPath(); + + if (!Directory.Exists(ReleasesFolder)) + { + write("WARNING: No release directory found. Make sure you want this!", ConsoleColor.Yellow); + Directory.CreateDirectory(ReleasesFolder); + } + + checkGitHubReleases(); + + refreshDirectory(StagingFolder); + + //increment build number until we have a unique one. + string verBase = DateTime.Now.ToString("yyyy.Mdd."); + int increment = 0; + while (Directory.GetFiles(ReleasesFolder, $"*{verBase}{increment}*").Any()) + increment++; + + string version = $"{verBase}{increment}"; + + Console.ForegroundColor = ConsoleColor.White; + Console.Write($"Ready to deploy {version}: "); + Console.ReadLine(); + + sw.Start(); + + if (!string.IsNullOrEmpty(CodeSigningCertificate)) + { + Console.Write("Enter code signing password: "); + codeSigningPassword = readLineMasked(); + } + + write("Updating AssemblyInfo..."); + updateCsprojVersion(version); + + write("Running build process..."); + foreach (string targetName in TargetNames.Split(',')) + runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln"); + + write("Creating NuGet deployment package..."); + runCommand(nugetPath, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}"); + + //prune once before checking for files so we can avoid erroring on files which aren't even needed for this build. + pruneReleases(); + + checkReleaseFiles(); + + write("Running squirrel build..."); + runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi"); + + //prune again to clean up before upload. + pruneReleases(); + + //rename setup to install. + File.Copy(Path.Combine(ReleasesFolder, "Setup.exe"), Path.Combine(ReleasesFolder, "install.exe"), true); + File.Delete(Path.Combine(ReleasesFolder, "Setup.exe")); + + uploadBuild(version); + + //reset assemblyinfo. + updateCsprojVersion("0.0.0"); + + write("Done!", ConsoleColor.White); + Console.ReadLine(); + } + + private static void displayHeader() + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(); + Console.WriteLine(" Please note that OSU! and PPY are registered trademarks and as such covered by trademark law."); + Console.WriteLine(" Do not distribute builds of this project publicly that make use of these."); + Console.ResetColor(); + Console.WriteLine(); + } + + /// + /// Ensure we have all the files in the release directory which are expected to be there. + /// This should have been accounted for in earlier steps, and just serves as a verification step. + /// + private static void checkReleaseFiles() + { + if (!canGitHub) return; + + var releaseLines = getReleaseLines(); + + //ensure we have all files necessary + foreach (var l in releaseLines) + if (!File.Exists(Path.Combine(ReleasesFolder, l.Filename))) + error($"Local file missing {l.Filename}"); + } + + private static IEnumerable getReleaseLines() => File.ReadAllLines(Path.Combine(ReleasesFolder, "RELEASES")).Select(l => new ReleaseLine(l)); + + private static void pruneReleases() + { + if (!canGitHub) return; + + write("Pruning RELEASES..."); + + var releaseLines = getReleaseLines().ToList(); + + var fulls = releaseLines.Where(l => l.Filename.Contains("-full")).Reverse().Skip(1); + + //remove any FULL releases (except most recent) + foreach (var l in fulls) + { + write($"- Removing old release {l.Filename}", ConsoleColor.Yellow); + File.Delete(Path.Combine(ReleasesFolder, l.Filename)); + releaseLines.Remove(l); + } + + //remove excess deltas + var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray(); + if (deltas.Length > keep_delta_count) + { + foreach (var l in deltas.Take(deltas.Length - keep_delta_count)) + { + write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow); + File.Delete(Path.Combine(ReleasesFolder, l.Filename)); + releaseLines.Remove(l); + } + } + + var lines = new List(); + releaseLines.ForEach(l => lines.Add(l.ToString())); + File.WriteAllLines(Path.Combine(ReleasesFolder, "RELEASES"), lines); + } + + private static void uploadBuild(string version) + { + if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate)) + return; + + write("Publishing to GitHub..."); + + write($"- Creating release {version}...", ConsoleColor.Yellow); + var req = new JsonWebRequest($"{GitHubApiEndpoint}") + { + Method = HttpMethod.POST, + }; + req.AddRaw(JsonConvert.SerializeObject(new GitHubRelease + { + Name = version, + Draft = true, + PreRelease = true + })); + req.AuthenticatedBlockingPerform(); + + var assetUploadUrl = req.ResponseObject.UploadUrl.Replace("{?name,label}", "?name={0}"); + foreach (var a in Directory.GetFiles(ReleasesFolder).Reverse()) //reverse to upload RELEASES first. + { + write($"- Adding asset {a}...", ConsoleColor.Yellow); + var upload = new WebRequest(assetUploadUrl, Path.GetFileName(a)) + { + Method = HttpMethod.POST, + Timeout = 240000, + ContentType = "application/octet-stream", + }; + + upload.AddRaw(File.ReadAllBytes(a)); + upload.AuthenticatedBlockingPerform(); + } + + openGitHubReleasePage(); + } + + private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage); + + private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken); + + private static void checkGitHubReleases() + { + if (!canGitHub) return; + + write("Checking GitHub releases..."); + var req = new JsonWebRequest>($"{GitHubApiEndpoint}"); + req.AuthenticatedBlockingPerform(); + + var lastRelease = req.ResponseObject.FirstOrDefault(); + + if (lastRelease == null) + return; + + if (lastRelease.Draft) + { + openGitHubReleasePage(); + error("There's a pending draft release! You probably don't want to push a build with this present."); + } + + //there's a previous release for this project. + var assetReq = new JsonWebRequest>($"{GitHubApiEndpoint}/{lastRelease.Id}/assets"); + assetReq.AuthenticatedBlockingPerform(); + var assets = assetReq.ResponseObject; + + //make sure our RELEASES file is the same as the last build on the server. + var releaseAsset = assets.FirstOrDefault(a => a.Name == "RELEASES"); + + //if we don't have a RELEASES asset then the previous release likely wasn't a Squirrel one. + if (releaseAsset == null) return; + + write($"Last GitHub release was {lastRelease.Name}."); + + bool requireDownload = false; + + if (!File.Exists(Path.Combine(ReleasesFolder, nupkgDistroFilename(lastRelease.Name)))) + { + write("Last version's package not found locally.", ConsoleColor.Red); + requireDownload = true; + } + else + { + var lastReleases = new RawFileWebRequest($"{GitHubApiEndpoint}/assets/{releaseAsset.Id}"); + lastReleases.AuthenticatedBlockingPerform(); + if (File.ReadAllText(Path.Combine(ReleasesFolder, "RELEASES")) != lastReleases.ResponseString) + { + write("Server's RELEASES differed from ours.", ConsoleColor.Red); + requireDownload = true; + } + } + + if (!requireDownload) return; + + write("Refreshing local releases directory..."); + refreshDirectory(ReleasesFolder); + + foreach (var a in assets) + { + if (a.Name.EndsWith(".exe")) continue; + + write($"- Downloading {a.Name}...", ConsoleColor.Yellow); + new FileWebRequest(Path.Combine(ReleasesFolder, a.Name), $"{GitHubApiEndpoint}/assets/{a.Id}").AuthenticatedBlockingPerform(); + } + } + + private static void refreshDirectory(string directory) + { + if (Directory.Exists(directory)) + Directory.Delete(directory, true); + Directory.CreateDirectory(directory); + } + + private static void updateCsprojVersion(string version) + { + var toUpdate = new[] { "", "" }; + string file = Path.Combine(ProjectName, $"{ProjectName}.csproj"); + + var l1 = File.ReadAllLines(file); + List l2 = new List(); + foreach (var l in l1) + { + string line = l; + + foreach (var tag in toUpdate) + { + int startIndex = l.IndexOf(tag, StringComparison.InvariantCulture); + if (startIndex == -1) + continue; + startIndex += tag.Length; + + int endIndex = l.IndexOf("<", startIndex, StringComparison.InvariantCulture); + line = $"{l.Substring(0, startIndex)}{version}{l.Substring(endIndex)}"; + } + + l2.Add(line); + } + + File.WriteAllLines(file, l2); + } + + /// + /// Find the base path of the active solution (git checkout location) + /// + private static void findSolutionPath() + { + string path = Path.GetDirectoryName(Environment.CommandLine.Replace("\"", "").Trim()); + + if (string.IsNullOrEmpty(path)) + path = Environment.CurrentDirectory; + + while (!File.Exists(Path.Combine(path, $"{SolutionName}.sln"))) + path = path.Remove(path.LastIndexOf(Path.DirectorySeparatorChar)); + path += Path.DirectorySeparatorChar; + + Environment.CurrentDirectory = path; + } + + private static bool runCommand(string command, string args) + { + var psi = new ProcessStartInfo(command, args) + { + WorkingDirectory = solutionPath, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + WindowStyle = ProcessWindowStyle.Hidden + }; + + Process p = Process.Start(psi); + if (p == null) return false; + + string output = p.StandardOutput.ReadToEnd(); + output += p.StandardError.ReadToEnd(); + + if (p.ExitCode == 0) return true; + + write(output); + error($"Command {command} {args} failed!"); + return false; + } + + private static string readLineMasked() + { + var fg = Console.ForegroundColor; + Console.ForegroundColor = Console.BackgroundColor; + var ret = Console.ReadLine(); + Console.ForegroundColor = fg; + + return ret; + } + + private static void error(string message) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"FATAL ERROR: {message}"); + + Console.ReadLine(); + Environment.Exit(-1); + } + + private static void write(string message, ConsoleColor col = ConsoleColor.Gray) + { + if (sw.ElapsedMilliseconds > 0) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.Write(sw.ElapsedMilliseconds.ToString().PadRight(8)); + } + Console.ForegroundColor = col; + Console.WriteLine(message); + } + + public static void AuthenticatedBlockingPerform(this WebRequest r) + { + r.AddHeader("Authorization", $"token {GitHubAccessToken}"); + r.Perform(); + } + } + + internal class RawFileWebRequest : WebRequest + { + public RawFileWebRequest(string url) : base(url) + { + } + + protected override string Accept => "application/octet-stream"; + } + + internal class ReleaseLine + { + public string Hash; + public string Filename; + public int Filesize; + + public ReleaseLine(string line) + { + var split = line.Split(' '); + Hash = split[0]; + Filename = split[1]; + Filesize = int.Parse(split[2]); + } + + public override string ToString() => $"{Hash} {Filename} {Filesize}"; + } +} diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 3704d2d5d8..a495d7048d 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -1,120 +1,120 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using osu.Desktop.Overlays; -using osu.Framework.Graphics.Containers; -using osu.Framework.Platform; -using osu.Game; -using OpenTK.Input; -using Microsoft.Win32; - -namespace osu.Desktop -{ - internal class OsuGameDesktop : OsuGame - { - private readonly bool noVersionOverlay; - - public OsuGameDesktop(string[] args = null) - : base(args) - { - noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; - } - - public override Storage GetStorageForStableInstall() - { - try - { - return new StableStorage(); - } - catch - { - return null; - } - } - - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : DesktopStorage - { - protected override string LocateBasePath() - { - bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); - - string stableInstallPath; - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; - } - - public StableStorage() - : base(string.Empty) - { - } - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - if (!noVersionOverlay) - { - LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v => - { - Add(v); - v.State = Visibility.Visible; - }); - } - } - - public override void SetHost(GameHost host) - { - base.SetHost(host); - var desktopWindow = host.Window as DesktopGameWindow; - if (desktopWindow != null) - { - desktopWindow.CursorState |= CursorState.Hidden; - - desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); - desktopWindow.Title = Name; - - desktopWindow.FileDrop += fileDrop; - } - } - - private void fileDrop(object sender, FileDropEventArgs e) - { - var filePaths = new[] { e.FileName }; - - var firstExtension = Path.GetExtension(filePaths.First()); - - if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; - - Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using osu.Desktop.Overlays; +using osu.Framework.Graphics.Containers; +using osu.Framework.Platform; +using osu.Game; +using OpenTK.Input; +using Microsoft.Win32; + +namespace osu.Desktop +{ + internal class OsuGameDesktop : OsuGame + { + private readonly bool noVersionOverlay; + + public OsuGameDesktop(string[] args = null) + : base(args) + { + noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; + } + + public override Storage GetStorageForStableInstall() + { + try + { + return new StableStorage(); + } + catch + { + return null; + } + } + + /// + /// A method of accessing an osu-stable install in a controlled fashion. + /// + private class StableStorage : DesktopStorage + { + protected override string LocateBasePath() + { + bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + + string stableInstallPath; + + try + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + + public StableStorage() + : base(string.Empty) + { + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (!noVersionOverlay) + { + LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v => + { + Add(v); + v.State = Visibility.Visible; + }); + } + } + + public override void SetHost(GameHost host) + { + base.SetHost(host); + var desktopWindow = host.Window as DesktopGameWindow; + if (desktopWindow != null) + { + desktopWindow.CursorState |= CursorState.Hidden; + + desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); + desktopWindow.Title = Name; + + desktopWindow.FileDrop += fileDrop; + } + } + + private void fileDrop(object sender, FileDropEventArgs e) + { + var filePaths = new[] { e.FileName }; + + var firstExtension = Path.GetExtension(filePaths.First()); + + if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; + + Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); + } + } +} diff --git a/osu.Desktop/Overlays/SquirrelUpdateManager.cs b/osu.Desktop/Overlays/SquirrelUpdateManager.cs index 61d8a75ae3..ea86d2f028 100644 --- a/osu.Desktop/Overlays/SquirrelUpdateManager.cs +++ b/osu.Desktop/Overlays/SquirrelUpdateManager.cs @@ -1,164 +1,164 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -#if NET_FRAMEWORK -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Logging; -using osu.Game; -using osu.Game.Graphics; -using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; -using OpenTK; -using OpenTK.Graphics; -using Squirrel; - -namespace osu.Desktop.Overlays -{ - public class SquirrelUpdateManager : Component - { - private UpdateManager updateManager; - private NotificationOverlay notificationOverlay; - - public void PrepareUpdate() - { - // Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here - UpdateManager.RestartAppWhenExited().Wait(); - } - - [BackgroundDependencyLoader] - private void load(NotificationOverlay notification, OsuGameBase game) - { - notificationOverlay = notification; - - if (game.IsDeployedBuild) - Schedule(() => checkForUpdateAsync()); - } - - private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) - { - //should we schedule a retry on completion of this check? - bool scheduleRetry = true; - - try - { - if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); - - var info = await updateManager.CheckForUpdate(!useDeltaPatching); - if (info.ReleasesToApply.Count == 0) - //no updates available. bail and retry later. - return; - - if (notification == null) - { - notification = new UpdateProgressNotification(this) { State = ProgressNotificationState.Active }; - Schedule(() => notificationOverlay.Post(notification)); - } - - notification.Progress = 0; - notification.Text = @"Downloading update..."; - - try - { - await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f); - - notification.Progress = 0; - notification.Text = @"Installing update..."; - - await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f); - - notification.State = ProgressNotificationState.Completed; - } - catch (Exception e) - { - if (useDeltaPatching) - { - Logger.Error(e, @"delta patching failed!"); - - //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) - //try again without deltas. - checkForUpdateAsync(false, notification); - scheduleRetry = false; - } - else - { - Logger.Error(e, @"update failed!"); - } - } - } - catch (Exception) - { - // we'll ignore this and retry later. can be triggered by no internet connection or thread abortion. - } - finally - { - if (scheduleRetry) - { - if (notification != null) - notification.State = ProgressNotificationState.Cancelled; - - //check again in 30 minutes. - Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30); - } - } - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - updateManager?.Dispose(); - } - - private class UpdateProgressNotification : ProgressNotification - { - private readonly SquirrelUpdateManager updateManager; - private OsuGame game; - - public UpdateProgressNotification(SquirrelUpdateManager updateManager) - { - this.updateManager = updateManager; - } - - protected override Notification CreateCompletionNotification() - { - return new ProgressCompletionNotification - { - Text = @"Update ready to install. Click to restart!", - Activated = () => - { - updateManager.PrepareUpdate(); - game.GracefullyExit(); - return true; - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, OsuGame game) - { - this.game = game; - - IconContent.AddRange(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow) - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.fa_upload, - Colour = Color4.White, - Size = new Vector2(20), - } - }); - } - } - } -} -#endif +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +#if NET_FRAMEWORK +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; +using osu.Game; +using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; +using OpenTK; +using OpenTK.Graphics; +using Squirrel; + +namespace osu.Desktop.Overlays +{ + public class SquirrelUpdateManager : Component + { + private UpdateManager updateManager; + private NotificationOverlay notificationOverlay; + + public void PrepareUpdate() + { + // Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here + UpdateManager.RestartAppWhenExited().Wait(); + } + + [BackgroundDependencyLoader] + private void load(NotificationOverlay notification, OsuGameBase game) + { + notificationOverlay = notification; + + if (game.IsDeployedBuild) + Schedule(() => checkForUpdateAsync()); + } + + private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) + { + //should we schedule a retry on completion of this check? + bool scheduleRetry = true; + + try + { + if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); + + var info = await updateManager.CheckForUpdate(!useDeltaPatching); + if (info.ReleasesToApply.Count == 0) + //no updates available. bail and retry later. + return; + + if (notification == null) + { + notification = new UpdateProgressNotification(this) { State = ProgressNotificationState.Active }; + Schedule(() => notificationOverlay.Post(notification)); + } + + notification.Progress = 0; + notification.Text = @"Downloading update..."; + + try + { + await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f); + + notification.Progress = 0; + notification.Text = @"Installing update..."; + + await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f); + + notification.State = ProgressNotificationState.Completed; + } + catch (Exception e) + { + if (useDeltaPatching) + { + Logger.Error(e, @"delta patching failed!"); + + //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) + //try again without deltas. + checkForUpdateAsync(false, notification); + scheduleRetry = false; + } + else + { + Logger.Error(e, @"update failed!"); + } + } + } + catch (Exception) + { + // we'll ignore this and retry later. can be triggered by no internet connection or thread abortion. + } + finally + { + if (scheduleRetry) + { + if (notification != null) + notification.State = ProgressNotificationState.Cancelled; + + //check again in 30 minutes. + Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30); + } + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + updateManager?.Dispose(); + } + + private class UpdateProgressNotification : ProgressNotification + { + private readonly SquirrelUpdateManager updateManager; + private OsuGame game; + + public UpdateProgressNotification(SquirrelUpdateManager updateManager) + { + this.updateManager = updateManager; + } + + protected override Notification CreateCompletionNotification() + { + return new ProgressCompletionNotification + { + Text = @"Update ready to install. Click to restart!", + Activated = () => + { + updateManager.PrepareUpdate(); + game.GracefullyExit(); + return true; + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, OsuGame game) + { + this.game = game; + + IconContent.AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow) + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_upload, + Colour = Color4.White, + Size = new Vector2(20), + } + }); + } + } + } +} +#endif diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index b61603dcc5..b984c0bbba 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -1,142 +1,142 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Diagnostics; -using osu.Framework.Allocation; -using osu.Framework.Development; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Desktop.Overlays -{ - public class VersionManager : OverlayContainer - { - private OsuConfigManager config; - private OsuGameBase game; - private NotificationOverlay notificationOverlay; - - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - - [BackgroundDependencyLoader] - private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config) - { - notificationOverlay = notification; - this.config = config; - this.game = game; - - AutoSizeAxes = Axes.Both; - Anchor = Anchor.BottomCentre; - Origin = Anchor.BottomCentre; - - Alpha = 0; - - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Children = new Drawable[] - { - new OsuSpriteText - { - Font = @"Exo2.0-Bold", - Text = game.Name - }, - new OsuSpriteText - { - Colour = DebugUtils.IsDebug ? colours.Red : Color4.White, - Text = game.Version - }, - } - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - TextSize = 12, - Colour = colours.Yellow, - Font = @"Venera", - Text = @"Development Build" - }, - new Sprite - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Texture = textures.Get(@"Menu/dev-build-footer"), - }, - } - } - }; - -#if NET_FRAMEWORK - Add(new SquirrelUpdateManager()); -#endif - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - var version = game.Version; - var lastVersion = config.Get(OsuSetting.Version); - if (game.IsDeployedBuild && version != lastVersion) - { - config.Set(OsuSetting.Version, version); - - // only show a notification if we've previously saved a version to the config file (ie. not the first run). - if (!string.IsNullOrEmpty(lastVersion)) - notificationOverlay.Post(new UpdateCompleteNotification(version)); - } - } - - private class UpdateCompleteNotification : SimpleNotification - { - public UpdateCompleteNotification(string version) - { - Text = $"You are now running osu!lazer {version}.\nClick to see what's new!"; - Icon = FontAwesome.fa_check_square; - Activated = delegate - { - Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}"); - return true; - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - IconBackgound.Colour = colours.BlueDark; - } - } - - protected override void PopIn() - { - this.FadeIn(1000); - } - - protected override void PopOut() - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Diagnostics; +using osu.Framework.Allocation; +using osu.Framework.Development; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Desktop.Overlays +{ + public class VersionManager : OverlayContainer + { + private OsuConfigManager config; + private OsuGameBase game; + private NotificationOverlay notificationOverlay; + + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; + + [BackgroundDependencyLoader] + private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config) + { + notificationOverlay = notification; + this.config = config; + this.game = game; + + AutoSizeAxes = Axes.Both; + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + + Alpha = 0; + + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Children = new Drawable[] + { + new OsuSpriteText + { + Font = @"Exo2.0-Bold", + Text = game.Name + }, + new OsuSpriteText + { + Colour = DebugUtils.IsDebug ? colours.Red : Color4.White, + Text = game.Version + }, + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + TextSize = 12, + Colour = colours.Yellow, + Font = @"Venera", + Text = @"Development Build" + }, + new Sprite + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Texture = textures.Get(@"Menu/dev-build-footer"), + }, + } + } + }; + +#if NET_FRAMEWORK + Add(new SquirrelUpdateManager()); +#endif + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + var version = game.Version; + var lastVersion = config.Get(OsuSetting.Version); + if (game.IsDeployedBuild && version != lastVersion) + { + config.Set(OsuSetting.Version, version); + + // only show a notification if we've previously saved a version to the config file (ie. not the first run). + if (!string.IsNullOrEmpty(lastVersion)) + notificationOverlay.Post(new UpdateCompleteNotification(version)); + } + } + + private class UpdateCompleteNotification : SimpleNotification + { + public UpdateCompleteNotification(string version) + { + Text = $"You are now running osu!lazer {version}.\nClick to see what's new!"; + Icon = FontAwesome.fa_check_square; + Activated = delegate + { + Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}"); + return true; + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IconBackgound.Colour = colours.BlueDark; + } + } + + protected override void PopIn() + { + this.FadeIn(1000); + } + + protected override void PopOut() + { + } + } +} diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 7258610f90..61d2006315 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -1,66 +1,66 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using System.Linq; -using osu.Framework; -using osu.Framework.Platform; -using osu.Game.IPC; -#if NET_FRAMEWORK -using System.Runtime; -#endif - -namespace osu.Desktop -{ - public static class Program - { - [STAThread] - public static int Main(string[] args) - { - // required to initialise native SQLite libraries on some platforms. - - if (!RuntimeInfo.IsMono) - useMulticoreJit(); - - // Back up the cwd before DesktopGameHost changes it - var cwd = Environment.CurrentDirectory; - - using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) - { - if (!host.IsPrimaryInstance) - { - var importer = new ArchiveImportIPCChannel(host); - // Restore the cwd so relative paths given at the command line work correctly - Directory.SetCurrentDirectory(cwd); - foreach (var file in args) - { - Console.WriteLine(@"Importing {0}", file); - if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000)) - throw new TimeoutException(@"IPC took too long to send"); - } - } - else - { - switch (args.FirstOrDefault() ?? string.Empty) - { - default: - host.Run(new OsuGameDesktop(args)); - break; - } - } - - return 0; - } - } - - private static void useMulticoreJit() - { -#if NET_FRAMEWORK - var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles")); - ProfileOptimization.SetProfileRoot(directory.FullName); - ProfileOptimization.StartProfile("Startup.Profile"); -#endif - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using System.Linq; +using osu.Framework; +using osu.Framework.Platform; +using osu.Game.IPC; +#if NET_FRAMEWORK +using System.Runtime; +#endif + +namespace osu.Desktop +{ + public static class Program + { + [STAThread] + public static int Main(string[] args) + { + // required to initialise native SQLite libraries on some platforms. + + if (!RuntimeInfo.IsMono) + useMulticoreJit(); + + // Back up the cwd before DesktopGameHost changes it + var cwd = Environment.CurrentDirectory; + + using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) + { + if (!host.IsPrimaryInstance) + { + var importer = new ArchiveImportIPCChannel(host); + // Restore the cwd so relative paths given at the command line work correctly + Directory.SetCurrentDirectory(cwd); + foreach (var file in args) + { + Console.WriteLine(@"Importing {0}", file); + if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000)) + throw new TimeoutException(@"IPC took too long to send"); + } + } + else + { + switch (args.FirstOrDefault() ?? string.Empty) + { + default: + host.Run(new OsuGameDesktop(args)); + break; + } + } + + return 0; + } + } + + private static void useMulticoreJit() + { +#if NET_FRAMEWORK + var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles")); + ProfileOptimization.SetProfileRoot(directory.FullName); + ProfileOptimization.StartProfile("Startup.Profile"); +#endif + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index e40510b71b..bd0cc209b6 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -1,67 +1,67 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects; -using osu.Game.Tests.Beatmaps; - -namespace osu.Game.Rulesets.Catch.Tests -{ - public class CatchBeatmapConversionTest : BeatmapConversionTest - { - protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - - [TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")] - public new void Test(string name) - { - base.Test(name); - } - - protected override IEnumerable CreateConvertValue(HitObject hitObject) - { - if (hitObject is JuiceStream stream) - { - foreach (var nested in stream.NestedHitObjects) - { - yield return new ConvertValue - { - StartTime = nested.StartTime, - Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH - }; - } - } - else - { - yield return new ConvertValue - { - StartTime = hitObject.StartTime, - Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH - }; - } - } - - protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter(); - } - - public struct ConvertValue : IEquatable - { - /// - /// A sane value to account for osu!stable using ints everwhere. - /// - private const float conversion_lenience = 2; - - public double StartTime; - public float Position; - - public bool Equals(ConvertValue other) - => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) - && Precision.AlmostEquals(Position, other.Position, conversion_lenience); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class CatchBeatmapConversionTest : BeatmapConversionTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; + + [TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")] + public new void Test(string name) + { + base.Test(name); + } + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + if (hitObject is JuiceStream stream) + { + foreach (var nested in stream.NestedHitObjects) + { + yield return new ConvertValue + { + StartTime = nested.StartTime, + Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH + }; + } + } + else + { + yield return new ConvertValue + { + StartTime = hitObject.StartTime, + Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH + }; + } + } + + protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter(); + } + + public struct ConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everwhere. + /// + private const float conversion_lenience = 2; + + public double StartTime; + public float Position; + + public bool Equals(ConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) + && Precision.AlmostEquals(Position, other.Position, conversion_lenience); + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index 11a22c69f3..bce20520d3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -1,62 +1,62 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Screens.Play; -using osu.Game.Tests.Visual; -using OpenTK; - -namespace osu.Game.Rulesets.Catch.Tests -{ - public class TestCaseAutoJuiceStream : TestCasePlayer - { - public TestCaseAutoJuiceStream() - : base(new CatchRuleset()) - { - } - - protected override Beatmap CreateBeatmap(Ruleset ruleset) - { - var beatmap = new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, - Ruleset = ruleset.RulesetInfo - } - }; - - for (int i = 0; i < 100; i++) - { - float width = (i % 10 + 1) / 20f; - - beatmap.HitObjects.Add(new JuiceStream - { - X = 0.5f - width / 2, - ControlPoints = new List - { - Vector2.Zero, - new Vector2(width * CatchPlayfield.BASE_WIDTH, 0) - }, - CurveType = CurveType.Linear, - Distance = width * CatchPlayfield.BASE_WIDTH, - StartTime = i * 2000, - NewCombo = i % 8 == 0 - }); - } - - return beatmap; - } - - protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) - { - beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); - return base.CreatePlayer(beatmap, ruleset); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestCaseAutoJuiceStream : TestCasePlayer + { + public TestCaseAutoJuiceStream() + : base(new CatchRuleset()) + { + } + + protected override Beatmap CreateBeatmap(Ruleset ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, + Ruleset = ruleset.RulesetInfo + } + }; + + for (int i = 0; i < 100; i++) + { + float width = (i % 10 + 1) / 20f; + + beatmap.HitObjects.Add(new JuiceStream + { + X = 0.5f - width / 2, + ControlPoints = new List + { + Vector2.Zero, + new Vector2(width * CatchPlayfield.BASE_WIDTH, 0) + }, + CurveType = CurveType.Linear, + Distance = width * CatchPlayfield.BASE_WIDTH, + StartTime = i * 2000, + NewCombo = i % 8 == 0 + }); + } + + return beatmap; + } + + protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) + { + beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + return base.CreatePlayer(beatmap, ruleset); + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs index ec9dd15673..d13a6bb860 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs @@ -1,47 +1,47 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawable; -using osu.Game.Rulesets.Catch.UI; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(BananaShower), - typeof(DrawableBananaShower), - - typeof(CatchRuleset), - typeof(CatchRulesetContainer), - }; - - public TestCaseBananaShower() - : base(new CatchRuleset()) - { - } - - protected override Beatmap CreateBeatmap(Ruleset ruleset) - { - var beatmap = new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, - Ruleset = ruleset.RulesetInfo - } - }; - - beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true }); - - return beatmap; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Catch.UI; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(BananaShower), + typeof(DrawableBananaShower), + + typeof(CatchRuleset), + typeof(CatchRulesetContainer), + }; + + public TestCaseBananaShower() + : base(new CatchRuleset()) + { + } + + protected override Beatmap CreateBeatmap(Ruleset ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, + Ruleset = ruleset.RulesetInfo + } + }; + + beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true }); + + return beatmap; + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs index efebfa9739..a2c886f1f3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer - { - public TestCaseCatchPlayer() : base(new CatchRuleset()) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer + { + public TestCaseCatchPlayer() : base(new CatchRuleset()) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs index 8e5843f40a..2b58fcc93c 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs @@ -1,36 +1,36 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer - { - public TestCaseCatchStacker() - : base(new CatchRuleset()) - { - } - - protected override Beatmap CreateBeatmap(Ruleset ruleset) - { - var beatmap = new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, - Ruleset = ruleset.RulesetInfo - } - }; - - - for (int i = 0; i < 512; i++) - beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 }); - - return beatmap; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer + { + public TestCaseCatchStacker() + : base(new CatchRuleset()) + { + } + + protected override Beatmap CreateBeatmap(Ruleset ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, + Ruleset = ruleset.RulesetInfo + } + }; + + + for (int i = 0; i < 512; i++) + beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 }); + + return beatmap; + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs index 0329921c92..f239290ed4 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs @@ -1,61 +1,61 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - public class TestCaseCatcherArea : OsuTestCase - { - private RulesetInfo catchRuleset; - private TestCatcherArea catcherArea; - - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CatcherArea), - }; - - public TestCaseCatcherArea() - { - AddSliderStep("CircleSize", 0, 8, 5, createCatcher); - AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t)); - } - - private void createCatcher(float size) - { - Child = new CatchInputManager(catchRuleset) - { - RelativeSizeAxes = Axes.Both, - Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size }) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.BottomLeft - }, - }; - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - catchRuleset = rulesets.GetRuleset(2); - } - - private class TestCatcherArea : CatcherArea - { - public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) - : base(beatmapDifficulty) - { - } - - public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestCaseCatcherArea : OsuTestCase + { + private RulesetInfo catchRuleset; + private TestCatcherArea catcherArea; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(CatcherArea), + }; + + public TestCaseCatcherArea() + { + AddSliderStep("CircleSize", 0, 8, 5, createCatcher); + AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t)); + } + + private void createCatcher(float size) + { + Child = new CatchInputManager(catchRuleset) + { + RelativeSizeAxes = Axes.Both, + Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size }) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomLeft + }, + }; + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + catchRuleset = rulesets.GetRuleset(2); + } + + private class TestCatcherArea : CatcherArea + { + public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) + : base(beatmapDifficulty) + { + } + + public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1; + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs index 595ca6cb24..275752523d 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs @@ -1,74 +1,74 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawable; -using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; -using osu.Game.Tests.Visual; -using OpenTK; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - public class TestCaseFruitObjects : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CatchHitObject), - typeof(Fruit), - typeof(Droplet), - typeof(DrawableCatchHitObject), - typeof(DrawableFruit), - typeof(DrawableDroplet), - typeof(Pulp), - }; - - public TestCaseFruitObjects() - { - Add(new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - createDrawable(0), - createDrawable(1), - createDrawable(2), - }, - new Drawable[] - { - createDrawable(3), - createDrawable(4), - createDrawable(5), - }, - } - }); - } - - private DrawableFruit createDrawable(int index) - { - var fruit = new Fruit - { - StartTime = 1000000000000, - IndexInBeatmap = index, - Scale = 1.5f, - }; - - return new DrawableFruit(fruit) - { - Anchor = Anchor.Centre, - RelativePositionAxes = Axes.Both, - Position = Vector2.Zero, - Alpha = 1, - LifetimeStart = double.NegativeInfinity, - LifetimeEnd = double.PositiveInfinity, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; +using osu.Game.Tests.Visual; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestCaseFruitObjects : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(CatchHitObject), + typeof(Fruit), + typeof(Droplet), + typeof(DrawableCatchHitObject), + typeof(DrawableFruit), + typeof(DrawableDroplet), + typeof(Pulp), + }; + + public TestCaseFruitObjects() + { + Add(new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + createDrawable(0), + createDrawable(1), + createDrawable(2), + }, + new Drawable[] + { + createDrawable(3), + createDrawable(4), + createDrawable(5), + }, + } + }); + } + + private DrawableFruit createDrawable(int index) + { + var fruit = new Fruit + { + StartTime = 1000000000000, + IndexInBeatmap = index, + Scale = 1.5f, + }; + + return new DrawableFruit(fruit) + { + Anchor = Anchor.Centre, + RelativePositionAxes = Axes.Both, + Position = Vector2.Zero, + Alpha = 1, + LifetimeStart = double.NegativeInfinity, + LifetimeEnd = double.PositiveInfinity, + }; + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs index 7564adea8c..e7f936ca2a 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs @@ -1,30 +1,30 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer - { - public TestCaseHyperdash() - : base(new CatchRuleset()) - { - } - - protected override Beatmap CreateBeatmap(Ruleset ruleset) - { - var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } }; - - - for (int i = 0; i < 512; i++) - if (i % 5 < 3) - beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); - - return beatmap; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer + { + public TestCaseHyperdash() + : base(new CatchRuleset()) + { + } + + protected override Beatmap CreateBeatmap(Ruleset ruleset) + { + var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } }; + + + for (int i = 0; i < 512; i++) + if (i % 5 < 3) + beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); + + return beatmap; + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Catch.Tests/TestCasePerformancePoints.cs index 2be6dd005d..9512cf2061 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCasePerformancePoints.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints - { - public TestCasePerformancePoints() - : base(new CatchRuleset()) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints + { + public TestCasePerformancePoints() + : base(new CatchRuleset()) + { + } + } +} 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 51c6d18f1f..7a4c7b3f1c 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -1,10 +1,10 @@ - - - - WinExe - netcoreapp2.0;net461 - - - - + + + + WinExe + netcoreapp2.0;net461 + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 01aa7abb9f..34e5f425fd 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -1,68 +1,68 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using System.Collections.Generic; -using System; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Catch.Beatmaps -{ - public class CatchBeatmapConverter : BeatmapConverter - { - protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; - - protected override IEnumerable ConvertHitObject(HitObject obj, Beatmap beatmap) - { - var curveData = obj as IHasCurve; - var positionData = obj as IHasXPosition; - var comboData = obj as IHasCombo; - var endTime = obj as IHasEndTime; - - if (positionData == null) - yield break; - - if (curveData != null) - { - yield return new JuiceStream - { - StartTime = obj.StartTime, - Samples = obj.Samples, - ControlPoints = curveData.ControlPoints, - CurveType = curveData.CurveType, - Distance = curveData.Distance, - RepeatSamples = curveData.RepeatSamples, - RepeatCount = curveData.RepeatCount, - X = positionData.X / CatchPlayfield.BASE_WIDTH, - NewCombo = comboData?.NewCombo ?? false - }; - - yield break; - } - - if (endTime != null) - { - yield return new BananaShower - { - StartTime = obj.StartTime, - Samples = obj.Samples, - Duration = endTime.Duration, - NewCombo = comboData?.NewCombo ?? false - }; - - yield break; - } - - yield return new Fruit - { - StartTime = obj.StartTime, - Samples = obj.Samples, - NewCombo = comboData?.NewCombo ?? false, - X = positionData.X / CatchPlayfield.BASE_WIDTH - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using System.Collections.Generic; +using System; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Catch.Beatmaps +{ + public class CatchBeatmapConverter : BeatmapConverter + { + protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; + + protected override IEnumerable ConvertHitObject(HitObject obj, Beatmap beatmap) + { + var curveData = obj as IHasCurve; + var positionData = obj as IHasXPosition; + var comboData = obj as IHasCombo; + var endTime = obj as IHasEndTime; + + if (positionData == null) + yield break; + + if (curveData != null) + { + yield return new JuiceStream + { + StartTime = obj.StartTime, + Samples = obj.Samples, + ControlPoints = curveData.ControlPoints, + CurveType = curveData.CurveType, + Distance = curveData.Distance, + RepeatSamples = curveData.RepeatSamples, + RepeatCount = curveData.RepeatCount, + X = positionData.X / CatchPlayfield.BASE_WIDTH, + NewCombo = comboData?.NewCombo ?? false + }; + + yield break; + } + + if (endTime != null) + { + yield return new BananaShower + { + StartTime = obj.StartTime, + Samples = obj.Samples, + Duration = endTime.Duration, + NewCombo = comboData?.NewCombo ?? false + }; + + yield break; + } + + yield return new Fruit + { + StartTime = obj.StartTime, + Samples = obj.Samples, + NewCombo = comboData?.NewCombo ?? false, + X = positionData.X / CatchPlayfield.BASE_WIDTH + }; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 1bebe9dae0..dfd10e0df7 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -1,72 +1,72 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Catch.Beatmaps -{ - public class CatchBeatmapProcessor : BeatmapProcessor - { - public override void PostProcess(Beatmap beatmap) - { - initialiseHyperDash(beatmap.HitObjects); - - base.PostProcess(beatmap); - - int index = 0; - foreach (var obj in beatmap.HitObjects) - obj.IndexInBeatmap = index++; - } - - private void initialiseHyperDash(List objects) - { - // todo: add difficulty adjust. - double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2; - - int lastDirection = 0; - double lastExcess = halfCatcherWidth; - - int objCount = objects.Count; - - for (int i = 0; i < objCount - 1; i++) - { - CatchHitObject currentObject = objects[i]; - - // not needed? - // if (currentObject is TinyDroplet) continue; - - CatchHitObject nextObject = objects[i + 1]; - - // while (nextObject is TinyDroplet) - // { - // if (++i == objCount - 1) break; - // nextObject = objects[i + 1]; - // } - - int thisDirection = nextObject.X > currentObject.X ? 1 : -1; - double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; - double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); - - if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) - { - currentObject.HyperDashTarget = nextObject; - lastExcess = halfCatcherWidth; - } - else - { - //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; - lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth); - } - - lastDirection = thisDirection; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.Beatmaps +{ + public class CatchBeatmapProcessor : BeatmapProcessor + { + public override void PostProcess(Beatmap beatmap) + { + initialiseHyperDash(beatmap.HitObjects); + + base.PostProcess(beatmap); + + int index = 0; + foreach (var obj in beatmap.HitObjects) + obj.IndexInBeatmap = index++; + } + + private void initialiseHyperDash(List objects) + { + // todo: add difficulty adjust. + double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2; + + int lastDirection = 0; + double lastExcess = halfCatcherWidth; + + int objCount = objects.Count; + + for (int i = 0; i < objCount - 1; i++) + { + CatchHitObject currentObject = objects[i]; + + // not needed? + // if (currentObject is TinyDroplet) continue; + + CatchHitObject nextObject = objects[i + 1]; + + // while (nextObject is TinyDroplet) + // { + // if (++i == objCount - 1) break; + // nextObject = objects[i + 1]; + // } + + int thisDirection = nextObject.X > currentObject.X ? 1 : -1; + double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; + double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); + + if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) + { + currentObject.HyperDashTarget = nextObject; + lastExcess = halfCatcherWidth; + } + else + { + //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; + lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth); + } + + lastDirection = thisDirection; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs index 917a076426..876b394da0 100644 --- a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Catch -{ - public class CatchDifficultyCalculator : DifficultyCalculator - { - public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap) - { - } - - public override double Calculate(Dictionary categoryDifficulty = null) => 0; - - protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Catch +{ + public class CatchDifficultyCalculator : DifficultyCalculator + { + public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap) + { + } + + public override double Calculate(Dictionary categoryDifficulty = null) => 0; + + protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter(); + } +} diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index fa8958687c..4f976bb7b2 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Catch -{ - public class CatchInputManager : RulesetInputManager - { - public CatchInputManager(RulesetInfo ruleset) - : base(ruleset, 0, SimultaneousBindingMode.Unique) - { - } - } - - public enum CatchAction - { - [Description("Move left")] - MoveLeft, - [Description("Move right")] - MoveRight, - [Description("Engage dash")] - Dash, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Catch +{ + public class CatchInputManager : RulesetInputManager + { + public CatchInputManager(RulesetInfo ruleset) + : base(ruleset, 0, SimultaneousBindingMode.Unique) + { + } + } + + public enum CatchAction + { + [Description("Move left")] + MoveLeft, + [Description("Move right")] + MoveRight, + [Description("Engage dash")] + Dash, + } +} diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 4dbe65b3ce..8930b09089 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -1,113 +1,113 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Catch.Mods; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Catch.Replays; -using osu.Game.Rulesets.Replays.Types; - -namespace osu.Game.Rulesets.Catch -{ - public class CatchRuleset : Ruleset - { - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchRulesetContainer(this, beatmap, isForCurrentRuleset); - - public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] - { - new KeyBinding(InputKey.Z, CatchAction.MoveLeft), - new KeyBinding(InputKey.Left, CatchAction.MoveLeft), - new KeyBinding(InputKey.X, CatchAction.MoveRight), - new KeyBinding(InputKey.Right, CatchAction.MoveRight), - new KeyBinding(InputKey.Shift, CatchAction.Dash), - new KeyBinding(InputKey.Shift, CatchAction.Dash), - }; - - public override IEnumerable GetModsFor(ModType type) - { - switch (type) - { - case ModType.DifficultyReduction: - return new Mod[] - { - new CatchModEasy(), - new CatchModNoFail(), - new MultiMod - { - Mods = new Mod[] - { - new CatchModHalfTime(), - new CatchModDaycore(), - }, - }, - }; - - case ModType.DifficultyIncrease: - return new Mod[] - { - new CatchModHardRock(), - new MultiMod - { - Mods = new Mod[] - { - new CatchModSuddenDeath(), - new CatchModPerfect(), - }, - }, - new MultiMod - { - Mods = new Mod[] - { - new CatchModDoubleTime(), - new CatchModNightcore(), - }, - }, - new CatchModHidden(), - new CatchModFlashlight(), - }; - - case ModType.Special: - return new Mod[] - { - new CatchModRelax(), - null, - null, - new MultiMod - { - Mods = new Mod[] - { - new CatchModAutoplay(), - new ModCinema(), - }, - }, - }; - - default: - return new Mod[] { }; - } - } - - public override string Description => "osu!catch"; - - public override string ShortName => "fruits"; - - public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; - - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); - - public override int? LegacyID => 2; - - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); - - public CatchRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Replays.Types; + +namespace osu.Game.Rulesets.Catch +{ + public class CatchRuleset : Ruleset + { + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchRulesetContainer(this, beatmap, isForCurrentRuleset); + + public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] + { + new KeyBinding(InputKey.Z, CatchAction.MoveLeft), + new KeyBinding(InputKey.Left, CatchAction.MoveLeft), + new KeyBinding(InputKey.X, CatchAction.MoveRight), + new KeyBinding(InputKey.Right, CatchAction.MoveRight), + new KeyBinding(InputKey.Shift, CatchAction.Dash), + new KeyBinding(InputKey.Shift, CatchAction.Dash), + }; + + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new CatchModEasy(), + new CatchModNoFail(), + new MultiMod + { + Mods = new Mod[] + { + new CatchModHalfTime(), + new CatchModDaycore(), + }, + }, + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new CatchModHardRock(), + new MultiMod + { + Mods = new Mod[] + { + new CatchModSuddenDeath(), + new CatchModPerfect(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new CatchModDoubleTime(), + new CatchModNightcore(), + }, + }, + new CatchModHidden(), + new CatchModFlashlight(), + }; + + case ModType.Special: + return new Mod[] + { + new CatchModRelax(), + null, + null, + new MultiMod + { + Mods = new Mod[] + { + new CatchModAutoplay(), + new ModCinema(), + }, + }, + }; + + default: + return new Mod[] { }; + } + } + + public override string Description => "osu!catch"; + + public override string ShortName => "fruits"; + + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; + + public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); + + public override int? LegacyID => 2; + + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); + + public CatchRuleset(RulesetInfo rulesetInfo = null) + : base(rulesetInfo) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs index d0e22bb04e..bb2786f14f 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Judgements; - -namespace osu.Game.Rulesets.Catch.Judgements -{ - public class CatchJudgement : Judgement - { - // todo: wangs - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Judgements; + +namespace osu.Game.Rulesets.Catch.Judgements +{ + public class CatchJudgement : Judgement + { + // todo: wangs + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs b/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs index 8ff08ab825..5ed563614a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Replays; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; -using osu.Game.Users; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModAutoplay : ModAutoplay - { - protected override Score CreateReplayScore(Beatmap beatmap) - { - return new Score - { - User = new User { Username = "osu!salad!" }, - Replay = new CatchAutoGenerator(beatmap).Generate(), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Users; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModAutoplay : ModAutoplay + { + protected override Score CreateReplayScore(Beatmap beatmap) + { + return new Score + { + User = new User { Username = "osu!salad!" }, + Replay = new CatchAutoGenerator(beatmap).Generate(), + }; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs index 8eb8fd8435..6d4caef8d2 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModDaycore : ModDaycore - { - public override double ScoreMultiplier => 0.3; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModDaycore : ModDaycore + { + public override double ScoreMultiplier => 0.3; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDoubleTime.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDoubleTime.cs index 7a7eeed719..dcf417c405 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDoubleTime.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDoubleTime.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModDoubleTime : ModDoubleTime - { - public override double ScoreMultiplier => 1.06; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1.06; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs b/osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs index 07bc8b825a..d20a2ec727 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModEasy.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModEasy : ModEasy - { - public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModEasy : ModEasy + { + public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!"; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index 424f14ad02..21e09f991c 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModFlashlight : ModFlashlight - { - public override double ScoreMultiplier => 1.12; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModFlashlight : ModFlashlight + { + public override double ScoreMultiplier => 1.12; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs index 947990cce5..4e48de454f 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModHalfTime : ModHalfTime - { - public override double ScoreMultiplier => 0.3; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModHalfTime : ModHalfTime + { + public override double ScoreMultiplier => 0.3; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index ed33bf7124..9e48e8de74 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModHardRock : ModHardRock - { - public override double ScoreMultiplier => 1.12; - public override bool Ranked => true; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModHardRock : ModHardRock + { + public override double ScoreMultiplier => 1.12; + public override bool Ranked => true; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index 14291f744c..f2716f351e 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModHidden : ModHidden - { - public override string Description => @"Play with fading fruits."; - public override double ScoreMultiplier => 1.06; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModHidden : ModHidden + { + public override string Description => @"Play with fading fruits."; + public override double ScoreMultiplier => 1.06; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs b/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs index b53cac0d7c..687db172cf 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModNightcore : ModNightcore - { - public override double ScoreMultiplier => 1.06; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModNightcore : ModNightcore + { + public override double ScoreMultiplier => 1.06; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModNoFail.cs b/osu.Game.Rulesets.Catch/Mods/CatchModNoFail.cs index afb2d83720..914419438d 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModNoFail.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModNoFail.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModNoFail : ModNoFail - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModNoFail : ModNoFail + { + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModPerfect.cs b/osu.Game.Rulesets.Catch/Mods/CatchModPerfect.cs index bc0dd10f6c..de00ff31ce 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModPerfect.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModPerfect.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModPerfect : ModPerfect - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModPerfect : ModPerfect + { + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index de0b6bc614..8bf9f32572 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModRelax : ModRelax - { - public override string Description => @"Use the mouse to control the catcher."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModRelax : ModRelax + { + public override string Description => @"Use the mouse to control the catcher."; + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModSuddenDeath.cs b/osu.Game.Rulesets.Catch/Mods/CatchModSuddenDeath.cs index 1e778e5305..71461cfc40 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModSuddenDeath.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModSuddenDeath.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModSuddenDeath : ModSuddenDeath - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModSuddenDeath : ModSuddenDeath + { + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 487345019b..a6aba42f03 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -1,48 +1,48 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Catch.Objects -{ - public class BananaShower : CatchHitObject, IHasEndTime - { - public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; - - public override bool LastInCombo => true; - - protected override void CreateNestedHitObjects() - { - base.CreateNestedHitObjects(); - createBananas(); - } - - private void createBananas() - { - double spacing = Duration; - while (spacing > 100) - spacing /= 2; - - if (spacing <= 0) - return; - - for (double i = StartTime; i <= EndTime; i += spacing) - AddNested(new Banana - { - Samples = Samples, - StartTime = i, - X = RNG.NextSingle() - }); - } - - public double EndTime => StartTime + Duration; - - public double Duration { get; set; } - - public class Banana : Fruit - { - public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Catch.Objects +{ + public class BananaShower : CatchHitObject, IHasEndTime + { + public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; + + public override bool LastInCombo => true; + + protected override void CreateNestedHitObjects() + { + base.CreateNestedHitObjects(); + createBananas(); + } + + private void createBananas() + { + double spacing = Duration; + while (spacing > 100) + spacing /= 2; + + if (spacing <= 0) + return; + + for (double i = StartTime; i <= EndTime; i += spacing) + AddNested(new Banana + { + Samples = Samples, + StartTime = i, + X = RNG.NextSingle() + }); + } + + public double EndTime => StartTime + Duration; + + public double Duration { get; set; } + + public class Banana : Fruit + { + public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 1a0ccc9b1e..95ffd41518 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -1,60 +1,60 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Catch.Objects -{ - public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation - { - public const double OBJECT_RADIUS = 44; - - public float X { get; set; } - - public int IndexInBeatmap { get; set; } - - public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4); - - public virtual bool NewCombo { get; set; } - - public int IndexInCurrentCombo { get; set; } - - public int ComboIndex { get; set; } - - /// - /// The next fruit starts a new combo. Used for explodey. - /// - public virtual bool LastInCombo { get; set; } - - public float Scale { get; set; } = 1; - - /// - /// Whether this fruit can initiate a hyperdash. - /// - public bool HyperDash => HyperDashTarget != null; - - /// - /// The target fruit if we are to initiate a hyperdash. - /// - public CatchHitObject HyperDashTarget; - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5; - } - } - - public enum FruitVisualRepresentation - { - Pear, - Grape, - Raspberry, - Pineapple, - Banana // banananananannaanana - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Catch.Objects +{ + public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation + { + public const double OBJECT_RADIUS = 44; + + public float X { get; set; } + + public int IndexInBeatmap { get; set; } + + public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4); + + public virtual bool NewCombo { get; set; } + + public int IndexInCurrentCombo { get; set; } + + public int ComboIndex { get; set; } + + /// + /// The next fruit starts a new combo. Used for explodey. + /// + public virtual bool LastInCombo { get; set; } + + public float Scale { get; set; } = 1; + + /// + /// Whether this fruit can initiate a hyperdash. + /// + public bool HyperDash => HyperDashTarget != null; + + /// + /// The target fruit if we are to initiate a hyperdash. + /// + public CatchHitObject HyperDashTarget; + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5; + } + } + + public enum FruitVisualRepresentation + { + Pear, + Grape, + Raspberry, + Pineapple, + Banana // banananananannaanana + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs index 3c6ec0703d..739cc6a59b 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs @@ -1,44 +1,44 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Catch.Objects.Drawable -{ - public class DrawableBananaShower : DrawableCatchHitObject - { - private readonly Container bananaContainer; - - public DrawableBananaShower(BananaShower s, Func> getVisualRepresentation = null) - : base(s) - { - RelativeSizeAxes = Axes.X; - Origin = Anchor.BottomLeft; - X = 0; - - InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both }; - - foreach (var b in s.NestedHitObjects.Cast()) - AddNested(getVisualRepresentation?.Invoke(b)); - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (timeOffset >= 0) - AddJudgement(new Judgement { Result = NestedHitObjects.Cast().Any(n => n.Judgements.Any(j => j.IsHit)) ? HitResult.Perfect : HitResult.Miss }); - } - - protected override void AddNested(DrawableHitObject h) - { - ((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false; - bananaContainer.Add(h); - base.AddNested(h); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Catch.Objects.Drawable +{ + public class DrawableBananaShower : DrawableCatchHitObject + { + private readonly Container bananaContainer; + + public DrawableBananaShower(BananaShower s, Func> getVisualRepresentation = null) + : base(s) + { + RelativeSizeAxes = Axes.X; + Origin = Anchor.BottomLeft; + X = 0; + + InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both }; + + foreach (var b in s.NestedHitObjects.Cast()) + AddNested(getVisualRepresentation?.Invoke(b)); + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (timeOffset >= 0) + AddJudgement(new Judgement { Result = NestedHitObjects.Cast().Any(n => n.Judgements.Any(j => j.IsHit)) ? HitResult.Perfect : HitResult.Miss }); + } + + protected override void AddNested(DrawableHitObject h) + { + ((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false; + bananaContainer.Add(h); + base.AddNested(h); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 582946ff00..3dbda708e5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -1,93 +1,93 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; -using OpenTK; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Catch.Objects.Drawable -{ - public abstract class PalpableCatchHitObject : DrawableCatchHitObject - where TObject : CatchHitObject - { - public override bool CanBePlated => true; - - protected PalpableCatchHitObject(TObject hitObject) - : base(hitObject) - { - Scale = new Vector2(HitObject.Scale); - } - } - - public abstract class DrawableCatchHitObject : DrawableCatchHitObject - where TObject : CatchHitObject - { - public new TObject HitObject; - - protected DrawableCatchHitObject(TObject hitObject) - : base(hitObject) - { - HitObject = hitObject; - Anchor = Anchor.BottomLeft; - } - } - - public abstract class DrawableCatchHitObject : DrawableHitObject - { - public virtual bool CanBePlated => false; - - protected DrawableCatchHitObject(CatchHitObject hitObject) - : base(hitObject) - { - RelativePositionAxes = Axes.X; - X = hitObject.X; - } - - public Func CheckPosition; - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (CheckPosition == null) return; - - if (timeOffset >= 0) - AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss }); - } - - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - - if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; - } - - private const float preempt = 1000; - - protected override void UpdateState(ArmedState state) - { - using (BeginAbsoluteSequence(HitObject.StartTime - preempt)) - this.FadeIn(200); - - var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; - - using (BeginAbsoluteSequence(endTime, true)) - { - switch (state) - { - case ArmedState.Miss: - this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire(); - break; - case ArmedState.Hit: - this.FadeOut().Expire(); - break; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using OpenTK; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawable +{ + public abstract class PalpableCatchHitObject : DrawableCatchHitObject + where TObject : CatchHitObject + { + public override bool CanBePlated => true; + + protected PalpableCatchHitObject(TObject hitObject) + : base(hitObject) + { + Scale = new Vector2(HitObject.Scale); + } + } + + public abstract class DrawableCatchHitObject : DrawableCatchHitObject + where TObject : CatchHitObject + { + public new TObject HitObject; + + protected DrawableCatchHitObject(TObject hitObject) + : base(hitObject) + { + HitObject = hitObject; + Anchor = Anchor.BottomLeft; + } + } + + public abstract class DrawableCatchHitObject : DrawableHitObject + { + public virtual bool CanBePlated => false; + + protected DrawableCatchHitObject(CatchHitObject hitObject) + : base(hitObject) + { + RelativePositionAxes = Axes.X; + X = hitObject.X; + } + + public Func CheckPosition; + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (CheckPosition == null) return; + + if (timeOffset >= 0) + AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss }); + } + + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + if (HitObject is IHasComboInformation combo) + AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + } + + private const float preempt = 1000; + + protected override void UpdateState(ArmedState state) + { + using (BeginAbsoluteSequence(HitObject.StartTime - preempt)) + this.FadeIn(200); + + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + + using (BeginAbsoluteSequence(endTime, true)) + { + switch (state) + { + case ArmedState.Miss: + this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire(); + break; + case ArmedState.Hit: + this.FadeOut().Expire(); + break; + } + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs index 719cf0a110..a19d67ebbe 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Catch.Objects.Drawable -{ - public class DrawableDroplet : PalpableCatchHitObject - { - private Pulp pulp; - - public DrawableDroplet(Droplet h) - : base(h) - { - Origin = Anchor.Centre; - Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4; - Masking = false; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = pulp = new Pulp - { - Size = Size - }; - } - - public override Color4 AccentColour - { - get { return base.AccentColour; } - set - { - base.AccentColour = value; - pulp.AccentColour = AccentColour; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawable +{ + public class DrawableDroplet : PalpableCatchHitObject + { + private Pulp pulp; + + public DrawableDroplet(Droplet h) + : base(h) + { + Origin = Anchor.Centre; + Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4; + Masking = false; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = pulp = new Pulp + { + Size = Size + }; + } + + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + pulp.AccentColour = AccentColour; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 03c2444d8c..41792b10a4 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -1,305 +1,305 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Catch.Objects.Drawable -{ - public class DrawableFruit : PalpableCatchHitObject - { - private Circle border; - - public DrawableFruit(Fruit h) - : base(h) - { - Origin = Anchor.Centre; - - Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS); - Masking = false; - - Rotation = (float)(RNG.NextDouble() - 0.5f) * 40; - } - - [BackgroundDependencyLoader] - private void load() - { - // todo: this should come from the skin. - AccentColour = colourForRrepesentation(HitObject.VisualRepresentation); - - InternalChildren = new[] - { - createPulp(HitObject.VisualRepresentation), - border = new Circle - { - EdgeEffect = new EdgeEffectParameters - { - Hollow = !HitObject.HyperDash, - Type = EdgeEffectType.Glow, - Radius = 4, - Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f) - }, - Size = new Vector2(Height * 1.5f), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - BorderColour = Color4.White, - BorderThickness = 4f, - Children = new Framework.Graphics.Drawable[] - { - new Box - { - AlwaysPresent = true, - Colour = AccentColour, - Alpha = 0, - RelativeSizeAxes = Axes.Both - } - } - }, - }; - - if (HitObject.HyperDash) - { - AddInternal(new Pulp - { - RelativePositionAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AccentColour = Color4.Red, - Blending = BlendingMode.Additive, - Alpha = 0.5f, - Scale = new Vector2(1.333f) - }); - } - } - - private Framework.Graphics.Drawable createPulp(FruitVisualRepresentation representation) - { - const float large_pulp_3 = 13f; - const float distance_from_centre_3 = 0.23f; - - const float large_pulp_4 = large_pulp_3 * 0.925f; - const float distance_from_centre_4 = distance_from_centre_3 / 0.925f; - - const float small_pulp = large_pulp_3 / 2; - - Vector2 positionAt(float angle, float distance) => new Vector2( - distance * (float)Math.Sin(angle * Math.PI / 180), - distance * (float)Math.Cos(angle * Math.PI / 180)); - - switch (representation) - { - default: - return new Container(); - case FruitVisualRepresentation.Raspberry: - return new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Framework.Graphics.Drawable[] - { - new Pulp - { - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - AccentColour = AccentColour, - Size = new Vector2(small_pulp), - Y = 0.05f, - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_4), - Position = positionAt(0, distance_from_centre_4), - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_4), - Position = positionAt(90, distance_from_centre_4), - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_4), - Position = positionAt(180, distance_from_centre_4), - }, - new Pulp - { - Size = new Vector2(large_pulp_4), - AccentColour = AccentColour, - Position = positionAt(270, distance_from_centre_4), - }, - } - }; - case FruitVisualRepresentation.Pineapple: - return new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Framework.Graphics.Drawable[] - { - new Pulp - { - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - AccentColour = AccentColour, - Size = new Vector2(small_pulp), - Y = 0.1f, - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_4), - Position = positionAt(45, distance_from_centre_4), - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_4), - Position = positionAt(135, distance_from_centre_4), - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_4), - Position = positionAt(225, distance_from_centre_4), - }, - new Pulp - { - Size = new Vector2(large_pulp_4), - AccentColour = AccentColour, - Position = positionAt(315, distance_from_centre_4), - }, - } - }; - case FruitVisualRepresentation.Pear: - return new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Framework.Graphics.Drawable[] - { - new Pulp - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AccentColour = AccentColour, - Size = new Vector2(small_pulp), - Y = -0.1f, - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_3), - Position = positionAt(60, distance_from_centre_3), - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_3), - Position = positionAt(180, distance_from_centre_3), - }, - new Pulp - { - Size = new Vector2(large_pulp_3), - AccentColour = AccentColour, - Position = positionAt(300, distance_from_centre_3), - }, - } - }; - case FruitVisualRepresentation.Grape: - return new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Framework.Graphics.Drawable[] - { - new Pulp - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AccentColour = AccentColour, - Size = new Vector2(small_pulp), - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_3), - Position = positionAt(0, distance_from_centre_3), - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_3), - Position = positionAt(120, distance_from_centre_3), - }, - new Pulp - { - Size = new Vector2(large_pulp_3), - AccentColour = AccentColour, - Position = positionAt(240, distance_from_centre_3), - }, - } - }; - case FruitVisualRepresentation.Banana: - return new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Framework.Graphics.Drawable[] - { - new Pulp - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AccentColour = AccentColour, - Size = new Vector2(small_pulp), - Y = -0.15f - }, - new Pulp - { - AccentColour = AccentColour, - Size = new Vector2(large_pulp_4 * 1.2f, large_pulp_4 * 3), - }, - } - }; - } - } - - protected override void Update() - { - base.Update(); - - border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1); - } - - private Color4 colourForRrepesentation(FruitVisualRepresentation representation) - { - switch (representation) - { - default: - case FruitVisualRepresentation.Pear: - return new Color4(17, 136, 170, 255); - case FruitVisualRepresentation.Grape: - return new Color4(204, 102, 0, 255); - case FruitVisualRepresentation.Raspberry: - return new Color4(121, 9, 13, 255); - case FruitVisualRepresentation.Pineapple: - return new Color4(102, 136, 0, 255); - case FruitVisualRepresentation.Banana: - switch (RNG.Next(0, 3)) - { - default: - return new Color4(255, 240, 0, 255); - case 1: - return new Color4(255, 192, 0, 255); - case 2: - return new Color4(214, 221, 28, 255); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawable +{ + public class DrawableFruit : PalpableCatchHitObject + { + private Circle border; + + public DrawableFruit(Fruit h) + : base(h) + { + Origin = Anchor.Centre; + + Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS); + Masking = false; + + Rotation = (float)(RNG.NextDouble() - 0.5f) * 40; + } + + [BackgroundDependencyLoader] + private void load() + { + // todo: this should come from the skin. + AccentColour = colourForRrepesentation(HitObject.VisualRepresentation); + + InternalChildren = new[] + { + createPulp(HitObject.VisualRepresentation), + border = new Circle + { + EdgeEffect = new EdgeEffectParameters + { + Hollow = !HitObject.HyperDash, + Type = EdgeEffectType.Glow, + Radius = 4, + Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f) + }, + Size = new Vector2(Height * 1.5f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + BorderColour = Color4.White, + BorderThickness = 4f, + Children = new Framework.Graphics.Drawable[] + { + new Box + { + AlwaysPresent = true, + Colour = AccentColour, + Alpha = 0, + RelativeSizeAxes = Axes.Both + } + } + }, + }; + + if (HitObject.HyperDash) + { + AddInternal(new Pulp + { + RelativePositionAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AccentColour = Color4.Red, + Blending = BlendingMode.Additive, + Alpha = 0.5f, + Scale = new Vector2(1.333f) + }); + } + } + + private Framework.Graphics.Drawable createPulp(FruitVisualRepresentation representation) + { + const float large_pulp_3 = 13f; + const float distance_from_centre_3 = 0.23f; + + const float large_pulp_4 = large_pulp_3 * 0.925f; + const float distance_from_centre_4 = distance_from_centre_3 / 0.925f; + + const float small_pulp = large_pulp_3 / 2; + + Vector2 positionAt(float angle, float distance) => new Vector2( + distance * (float)Math.Sin(angle * Math.PI / 180), + distance * (float)Math.Cos(angle * Math.PI / 180)); + + switch (representation) + { + default: + return new Container(); + case FruitVisualRepresentation.Raspberry: + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Framework.Graphics.Drawable[] + { + new Pulp + { + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + AccentColour = AccentColour, + Size = new Vector2(small_pulp), + Y = 0.05f, + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_4), + Position = positionAt(0, distance_from_centre_4), + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_4), + Position = positionAt(90, distance_from_centre_4), + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_4), + Position = positionAt(180, distance_from_centre_4), + }, + new Pulp + { + Size = new Vector2(large_pulp_4), + AccentColour = AccentColour, + Position = positionAt(270, distance_from_centre_4), + }, + } + }; + case FruitVisualRepresentation.Pineapple: + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Framework.Graphics.Drawable[] + { + new Pulp + { + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + AccentColour = AccentColour, + Size = new Vector2(small_pulp), + Y = 0.1f, + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_4), + Position = positionAt(45, distance_from_centre_4), + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_4), + Position = positionAt(135, distance_from_centre_4), + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_4), + Position = positionAt(225, distance_from_centre_4), + }, + new Pulp + { + Size = new Vector2(large_pulp_4), + AccentColour = AccentColour, + Position = positionAt(315, distance_from_centre_4), + }, + } + }; + case FruitVisualRepresentation.Pear: + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Framework.Graphics.Drawable[] + { + new Pulp + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AccentColour = AccentColour, + Size = new Vector2(small_pulp), + Y = -0.1f, + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_3), + Position = positionAt(60, distance_from_centre_3), + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_3), + Position = positionAt(180, distance_from_centre_3), + }, + new Pulp + { + Size = new Vector2(large_pulp_3), + AccentColour = AccentColour, + Position = positionAt(300, distance_from_centre_3), + }, + } + }; + case FruitVisualRepresentation.Grape: + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Framework.Graphics.Drawable[] + { + new Pulp + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AccentColour = AccentColour, + Size = new Vector2(small_pulp), + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_3), + Position = positionAt(0, distance_from_centre_3), + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_3), + Position = positionAt(120, distance_from_centre_3), + }, + new Pulp + { + Size = new Vector2(large_pulp_3), + AccentColour = AccentColour, + Position = positionAt(240, distance_from_centre_3), + }, + } + }; + case FruitVisualRepresentation.Banana: + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Framework.Graphics.Drawable[] + { + new Pulp + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AccentColour = AccentColour, + Size = new Vector2(small_pulp), + Y = -0.15f + }, + new Pulp + { + AccentColour = AccentColour, + Size = new Vector2(large_pulp_4 * 1.2f, large_pulp_4 * 3), + }, + } + }; + } + } + + protected override void Update() + { + base.Update(); + + border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1); + } + + private Color4 colourForRrepesentation(FruitVisualRepresentation representation) + { + switch (representation) + { + default: + case FruitVisualRepresentation.Pear: + return new Color4(17, 136, 170, 255); + case FruitVisualRepresentation.Grape: + return new Color4(204, 102, 0, 255); + case FruitVisualRepresentation.Raspberry: + return new Color4(121, 9, 13, 255); + case FruitVisualRepresentation.Pineapple: + return new Color4(102, 136, 0, 255); + case FruitVisualRepresentation.Banana: + switch (RNG.Next(0, 3)) + { + default: + return new Color4(255, 240, 0, 255); + case 1: + return new Color4(255, 192, 0, 255); + case 2: + return new Color4(214, 221, 28, 255); + } + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs index b3532e2473..854b63edeb 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Catch.Objects.Drawable -{ - public class DrawableJuiceStream : DrawableCatchHitObject - { - private readonly Container dropletContainer; - - public DrawableJuiceStream(JuiceStream s, Func> getVisualRepresentation = null) - : base(s) - { - RelativeSizeAxes = Axes.Both; - Origin = Anchor.BottomLeft; - X = 0; - - InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, }; - - foreach (var o in s.NestedHitObjects.Cast()) - AddNested(getVisualRepresentation?.Invoke(o)); - } - - protected override bool ProvidesJudgement => false; - - protected override void AddNested(DrawableHitObject h) - { - var catchObject = (DrawableCatchHitObject)h; - - catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false; - - dropletContainer.Add(h); - base.AddNested(h); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Catch.Objects.Drawable +{ + public class DrawableJuiceStream : DrawableCatchHitObject + { + private readonly Container dropletContainer; + + public DrawableJuiceStream(JuiceStream s, Func> getVisualRepresentation = null) + : base(s) + { + RelativeSizeAxes = Axes.Both; + Origin = Anchor.BottomLeft; + X = 0; + + InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, }; + + foreach (var o in s.NestedHitObjects.Cast()) + AddNested(getVisualRepresentation?.Invoke(o)); + } + + protected override bool ProvidesJudgement => false; + + protected override void AddNested(DrawableHitObject h) + { + var catchObject = (DrawableCatchHitObject)h; + + catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false; + + dropletContainer.Add(h); + base.AddNested(h); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs index 80520ea846..d17a72a165 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs @@ -1,42 +1,42 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces -{ - public class Pulp : Circle, IHasAccentColour - { - public Pulp() - { - RelativePositionAxes = Axes.Both; - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - Blending = BlendingMode.Additive; - Colour = Color4.White.Opacity(0.9f); - } - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Radius = 8, - Colour = accentColour.Darken(0.2f).Opacity(0.75f) - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces +{ + public class Pulp : Circle, IHasAccentColour + { + public Pulp() + { + RelativePositionAxes = Axes.Both; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Blending = BlendingMode.Additive; + Colour = Color4.White.Opacity(0.9f); + } + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 8, + Colour = accentColour.Darken(0.2f).Opacity(0.75f) + }; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Droplet.cs b/osu.Game.Rulesets.Catch/Objects/Droplet.cs index 4f9636e110..f91a70c506 100644 --- a/osu.Game.Rulesets.Catch/Objects/Droplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Droplet.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Catch.Objects -{ - public class Droplet : CatchHitObject - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Catch.Objects +{ + public class Droplet : CatchHitObject + { + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Fruit.cs b/osu.Game.Rulesets.Catch/Objects/Fruit.cs index c738984667..fcbb339ffd 100644 --- a/osu.Game.Rulesets.Catch/Objects/Fruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Fruit.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Catch.Objects -{ - public class Fruit : CatchHitObject - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Catch.Objects +{ + public class Fruit : CatchHitObject + { + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 29ad3c3956..ae799875a9 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -1,157 +1,157 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Catch.Objects -{ - public class JuiceStream : CatchHitObject, IHasCurve - { - /// - /// Positional distance that results in a duration of one second, before any speed adjustments. - /// - private const float base_scoring_distance = 100; - - public int RepeatCount { get; set; } - - public double Velocity; - public double TickDistance; - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); - - double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; - - Velocity = scoringDistance / timingPoint.BeatLength; - TickDistance = scoringDistance / difficulty.SliderTickRate; - } - - protected override void CreateNestedHitObjects() - { - base.CreateNestedHitObjects(); - - createTicks(); - } - - private void createTicks() - { - if (TickDistance == 0) - return; - - var length = Curve.Distance; - var tickDistance = Math.Min(TickDistance, length); - var spanDuration = length / Velocity; - - var minDistanceFromEnd = Velocity * 0.01; - - AddNested(new Fruit - { - Samples = Samples, - StartTime = StartTime, - X = X - }); - - double lastDropletTime = StartTime; - - for (int span = 0; span < this.SpanCount(); span++) - { - var spanStartTime = StartTime + span * spanDuration; - var reversed = span % 2 == 1; - - for (double d = 0; d <= length; d += tickDistance) - { - var timeProgress = d / length; - var distanceProgress = reversed ? 1 - timeProgress : timeProgress; - - double time = spanStartTime + timeProgress * spanDuration; - - double tinyTickInterval = time - lastDropletTime; - while (tinyTickInterval > 100) - tinyTickInterval /= 2; - - for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval) - { - double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration; - - AddNested(new TinyDroplet - { - StartTime = t, - X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, - Samples = new List(Samples.Select(s => new SampleInfo - { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) - }); - } - - if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd) - { - AddNested(new Droplet - { - StartTime = time, - X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, - Samples = new List(Samples.Select(s => new SampleInfo - { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) - }); - } - - lastDropletTime = time; - } - - AddNested(new Fruit - { - Samples = Samples, - StartTime = spanStartTime + spanDuration, - X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH - }); - } - } - - public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; - - public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; - - public double Duration => EndTime - StartTime; - - public double Distance - { - get { return Curve.Distance; } - set { Curve.Distance = value; } - } - - public SliderCurve Curve { get; } = new SliderCurve(); - - public List ControlPoints - { - get { return Curve.ControlPoints; } - set { Curve.ControlPoints = value; } - } - - public List> RepeatSamples { get; set; } = new List>(); - - public CurveType CurveType - { - get { return Curve.CurveType; } - set { Curve.CurveType = value; } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.Objects +{ + public class JuiceStream : CatchHitObject, IHasCurve + { + /// + /// Positional distance that results in a duration of one second, before any speed adjustments. + /// + private const float base_scoring_distance = 100; + + public int RepeatCount { get; set; } + + public double Velocity; + public double TickDistance; + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); + + double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; + + Velocity = scoringDistance / timingPoint.BeatLength; + TickDistance = scoringDistance / difficulty.SliderTickRate; + } + + protected override void CreateNestedHitObjects() + { + base.CreateNestedHitObjects(); + + createTicks(); + } + + private void createTicks() + { + if (TickDistance == 0) + return; + + var length = Curve.Distance; + var tickDistance = Math.Min(TickDistance, length); + var spanDuration = length / Velocity; + + var minDistanceFromEnd = Velocity * 0.01; + + AddNested(new Fruit + { + Samples = Samples, + StartTime = StartTime, + X = X + }); + + double lastDropletTime = StartTime; + + for (int span = 0; span < this.SpanCount(); span++) + { + var spanStartTime = StartTime + span * spanDuration; + var reversed = span % 2 == 1; + + for (double d = 0; d <= length; d += tickDistance) + { + var timeProgress = d / length; + var distanceProgress = reversed ? 1 - timeProgress : timeProgress; + + double time = spanStartTime + timeProgress * spanDuration; + + double tinyTickInterval = time - lastDropletTime; + while (tinyTickInterval > 100) + tinyTickInterval /= 2; + + for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval) + { + double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration; + + AddNested(new TinyDroplet + { + StartTime = t, + X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, + Samples = new List(Samples.Select(s => new SampleInfo + { + Bank = s.Bank, + Name = @"slidertick", + Volume = s.Volume + })) + }); + } + + if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd) + { + AddNested(new Droplet + { + StartTime = time, + X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, + Samples = new List(Samples.Select(s => new SampleInfo + { + Bank = s.Bank, + Name = @"slidertick", + Volume = s.Volume + })) + }); + } + + lastDropletTime = time; + } + + AddNested(new Fruit + { + Samples = Samples, + StartTime = spanStartTime + spanDuration, + X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH + }); + } + } + + public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; + + public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; + + public double Duration => EndTime - StartTime; + + public double Distance + { + get { return Curve.Distance; } + set { Curve.Distance = value; } + } + + public SliderCurve Curve { get; } = new SliderCurve(); + + public List ControlPoints + { + get { return Curve.ControlPoints; } + set { Curve.ControlPoints = value; } + } + + public List> RepeatSamples { get; set; } = new List>(); + + public CurveType CurveType + { + get { return Curve.CurveType; } + set { Curve.CurveType = value; } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs b/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs index 23b8a7e02f..76cc8d9808 100644 --- a/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Catch.Objects -{ - public class TinyDroplet : Droplet - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Catch.Objects +{ + public class TinyDroplet : Droplet + { + } +} diff --git a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs index fed1013ae1..045d0824c6 100644 --- a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Runtime.CompilerServices; - -// We publish our internal attributes to other sub-projects of the framework. -// Note, that we omit visual tests as they are meant to test the framework -// behavior "in the wild". - -[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")] -[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")] +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Runtime.CompilerServices; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 244ab2b508..936ab6a9d3 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -1,121 +1,121 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Replays; -using osu.Game.Users; - -namespace osu.Game.Rulesets.Catch.Replays -{ - internal class CatchAutoGenerator : AutoGenerator - { - public const double RELEASE_DELAY = 20; - - public CatchAutoGenerator(Beatmap beatmap) - : base(beatmap) - { - Replay = new Replay { User = new User { Username = @"Autoplay" } }; - } - - protected Replay Replay; - - public override Replay Generate() - { - // todo: add support for HT DT - const double dash_speed = CatcherArea.Catcher.BASE_SPEED; - const double movement_speed = dash_speed / 2; - float lastPosition = 0.5f; - double lastTime = 0; - - // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled - Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition)); - - void moveToNext(CatchHitObject h) - { - float positionChange = Math.Abs(lastPosition - h.X); - double timeAvailable = h.StartTime - lastTime; - - //So we can either make it there without a dash or not. - double speedRequired = positionChange / timeAvailable; - - bool dashRequired = speedRequired > movement_speed && h.StartTime != 0; - - // todo: get correct catcher size, based on difficulty CS. - const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f; - - if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X) - { - //we are already in the correct range. - lastTime = h.StartTime; - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition)); - return; - } - - if (h is BananaShower.Banana) - { - // auto bananas unrealistically warp to catch 100% combo. - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); - } - else if (h.HyperDash) - { - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition)); - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); - } - else if (dashRequired) - { - //we do a movement in two parts - the dash part then the normal part... - double timeAtNormalSpeed = positionChange / movement_speed; - double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable; - double timeAtDashSpeed = timeWeNeedToSave / 2; - - float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable); - - //dash movement - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true)); - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition)); - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); - } - else - { - double timeBefore = positionChange / movement_speed; - - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition)); - Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); - } - - lastTime = h.StartTime; - lastPosition = h.X; - } - - foreach (var obj in Beatmap.HitObjects) - { - switch (obj) - { - case Fruit _: - moveToNext(obj); - break; - } - - foreach (var nestedObj in obj.NestedHitObjects.Cast()) - { - switch (nestedObj) - { - case BananaShower.Banana _: - case TinyDroplet _: - case Droplet _: - case Fruit _: - moveToNext(nestedObj); - break; - } - } - } - - return Replay; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Replays; +using osu.Game.Users; + +namespace osu.Game.Rulesets.Catch.Replays +{ + internal class CatchAutoGenerator : AutoGenerator + { + public const double RELEASE_DELAY = 20; + + public CatchAutoGenerator(Beatmap beatmap) + : base(beatmap) + { + Replay = new Replay { User = new User { Username = @"Autoplay" } }; + } + + protected Replay Replay; + + public override Replay Generate() + { + // todo: add support for HT DT + const double dash_speed = CatcherArea.Catcher.BASE_SPEED; + const double movement_speed = dash_speed / 2; + float lastPosition = 0.5f; + double lastTime = 0; + + // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled + Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition)); + + void moveToNext(CatchHitObject h) + { + float positionChange = Math.Abs(lastPosition - h.X); + double timeAvailable = h.StartTime - lastTime; + + //So we can either make it there without a dash or not. + double speedRequired = positionChange / timeAvailable; + + bool dashRequired = speedRequired > movement_speed && h.StartTime != 0; + + // todo: get correct catcher size, based on difficulty CS. + const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f; + + if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X) + { + //we are already in the correct range. + lastTime = h.StartTime; + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition)); + return; + } + + if (h is BananaShower.Banana) + { + // auto bananas unrealistically warp to catch 100% combo. + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + } + else if (h.HyperDash) + { + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + } + else if (dashRequired) + { + //we do a movement in two parts - the dash part then the normal part... + double timeAtNormalSpeed = positionChange / movement_speed; + double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable; + double timeAtDashSpeed = timeWeNeedToSave / 2; + + float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable); + + //dash movement + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + } + else + { + double timeBefore = positionChange / movement_speed; + + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); + } + + lastTime = h.StartTime; + lastPosition = h.X; + } + + foreach (var obj in Beatmap.HitObjects) + { + switch (obj) + { + case Fruit _: + moveToNext(obj); + break; + } + + foreach (var nestedObj in obj.NestedHitObjects.Cast()) + { + switch (nestedObj) + { + case BananaShower.Banana _: + case TinyDroplet _: + case Droplet _: + case Fruit _: + moveToNext(nestedObj); + break; + } + } + } + + return Replay; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs index 9c9b06fcea..6a9d1bdbc7 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs @@ -1,60 +1,60 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Input; -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Replays; - -namespace osu.Game.Rulesets.Catch.Replays -{ - public class CatchFramedReplayInputHandler : FramedReplayInputHandler - { - public CatchFramedReplayInputHandler(Replay replay) - : base(replay) - { - } - - protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0; - - protected float? Position - { - get - { - if (!HasFrames) - return null; - - return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); - } - } - - public override List GetPendingStates() - { - if (!Position.HasValue) return new List(); - - var actions = new List(); - - if (CurrentFrame.Dashing) - actions.Add(CatchAction.Dash); - - if (Position.Value > CurrentFrame.Position) - actions.Add(CatchAction.MoveRight); - else if (Position.Value < CurrentFrame.Position) - actions.Add(CatchAction.MoveLeft); - - return new List - { - new CatchReplayState - { - PressedActions = actions, - CatcherX = Position.Value - }, - }; - } - - public class CatchReplayState : ReplayState - { - public float? CatcherX { get; set; } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Replays; + +namespace osu.Game.Rulesets.Catch.Replays +{ + public class CatchFramedReplayInputHandler : FramedReplayInputHandler + { + public CatchFramedReplayInputHandler(Replay replay) + : base(replay) + { + } + + protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0; + + protected float? Position + { + get + { + if (!HasFrames) + return null; + + return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); + } + } + + public override List GetPendingStates() + { + if (!Position.HasValue) return new List(); + + var actions = new List(); + + if (CurrentFrame.Dashing) + actions.Add(CatchAction.Dash); + + if (Position.Value > CurrentFrame.Position) + actions.Add(CatchAction.MoveRight); + else if (Position.Value < CurrentFrame.Position) + actions.Add(CatchAction.MoveLeft); + + return new List + { + new CatchReplayState + { + PressedActions = actions, + CatcherX = Position.Value + }, + }; + } + + public class CatchReplayState : ReplayState + { + public float? CatcherX { get; set; } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index b444b0d7ba..d63d1bd331 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Replays.Legacy; -using osu.Game.Rulesets.Replays.Types; - -namespace osu.Game.Rulesets.Catch.Replays -{ - public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame - { - public float Position; - public bool Dashing; - - public CatchReplayFrame() - { - } - - public CatchReplayFrame(double time, float? position = null, bool dashing = false) - : base(time) - { - Position = position ?? -1; - Dashing = dashing; - } - - public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) - { - Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH; - Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; + +namespace osu.Game.Rulesets.Catch.Replays +{ + public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame + { + public float Position; + public bool Dashing; + + public CatchReplayFrame() + { + } + + public CatchReplayFrame(double time, float? position = null, bool dashing = false) + : base(time) + { + Position = position ?? -1; + Dashing = dashing; + } + + public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) + { + Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH; + Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 0a8a53c6f1..ce1aee5c34 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -1,44 +1,44 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Judgements; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Catch.Scoring -{ - public class CatchScoreProcessor : ScoreProcessor - { - public CatchScoreProcessor(RulesetContainer rulesetContainer) - : base(rulesetContainer) - { - } - - protected override void SimulateAutoplay(Beatmap beatmap) - { - foreach (var obj in beatmap.HitObjects) - { - switch (obj) - { - case JuiceStream stream: - foreach (var _ in stream.NestedHitObjects.Cast()) - AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); - break; - case BananaShower shower: - foreach (var _ in shower.NestedHitObjects.Cast()) - AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); - AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); - break; - case Fruit _: - AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); - break; - } - } - - base.SimulateAutoplay(beatmap); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Judgements; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Catch.Scoring +{ + public class CatchScoreProcessor : ScoreProcessor + { + public CatchScoreProcessor(RulesetContainer rulesetContainer) + : base(rulesetContainer) + { + } + + protected override void SimulateAutoplay(Beatmap beatmap) + { + foreach (var obj in beatmap.HitObjects) + { + switch (obj) + { + case JuiceStream stream: + foreach (var _ in stream.NestedHitObjects.Cast()) + AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); + break; + case BananaShower shower: + foreach (var _ in shower.NestedHitObjects.Cast()) + AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); + AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); + break; + case Fruit _: + AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); + break; + } + } + + base.SimulateAutoplay(beatmap); + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 2b6a7c41f4..9eca8f6871 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -1,70 +1,70 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawable; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.UI.Scrolling; - -namespace osu.Game.Rulesets.Catch.UI -{ - public class CatchPlayfield : ScrollingPlayfield - { - public const float BASE_WIDTH = 512; - - protected override Container Content => content; - private readonly Container content; - - private readonly CatcherArea catcherArea; - - public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation) - : base(ScrollingDirection.Down, BASE_WIDTH) - { - Container explodingFruitContainer; - - Anchor = Anchor.TopCentre; - Origin = Anchor.TopCentre; - - base.Content.Anchor = Anchor.BottomLeft; - base.Content.Origin = Anchor.BottomLeft; - - base.Content.AddRange(new Drawable[] - { - explodingFruitContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }, - catcherArea = new CatcherArea(difficulty) - { - GetVisualRepresentation = getVisualRepresentation, - ExplodingFruitTarget = explodingFruitContainer, - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, - }, - content = new Container - { - RelativeSizeAxes = Axes.Both, - }, - }); - } - - public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj); - - public override void Add(DrawableHitObject h) - { - h.OnJudgement += onJudgement; - - base.Add(h); - - var fruit = (DrawableCatchHitObject)h; - fruit.CheckPosition = CheckIfWeCanCatch; - } - - private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class CatchPlayfield : ScrollingPlayfield + { + public const float BASE_WIDTH = 512; + + protected override Container Content => content; + private readonly Container content; + + private readonly CatcherArea catcherArea; + + public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation) + : base(ScrollingDirection.Down, BASE_WIDTH) + { + Container explodingFruitContainer; + + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + + base.Content.Anchor = Anchor.BottomLeft; + base.Content.Origin = Anchor.BottomLeft; + + base.Content.AddRange(new Drawable[] + { + explodingFruitContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + catcherArea = new CatcherArea(difficulty) + { + GetVisualRepresentation = getVisualRepresentation, + ExplodingFruitTarget = explodingFruitContainer, + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + }, + content = new Container + { + RelativeSizeAxes = Axes.Both, + }, + }); + } + + public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj); + + public override void Add(DrawableHitObject h) + { + h.OnJudgement += onJudgement; + + base.Add(h); + + var fruit = (DrawableCatchHitObject)h; + fruit.CheckPosition = CheckIfWeCanCatch; + } + + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement); + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index 41dd7fdf4e..022a8a8b43 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -1,59 +1,59 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Input; -using osu.Game.Beatmaps; -using osu.Game.Input.Handlers; -using osu.Game.Rulesets.Catch.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawable; -using osu.Game.Rulesets.Catch.Replays; -using osu.Game.Rulesets.Catch.Scoring; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.UI.Scrolling; -using OpenTK; - -namespace osu.Game.Rulesets.Catch.UI -{ - public class CatchRulesetContainer : ScrollingRulesetContainer - { - public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) - { - } - - public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); - - protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); - - protected override BeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor(); - - protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter(); - - protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation); - - public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); - - protected override DrawableHitObject GetVisualRepresentation(CatchHitObject h) - { - switch (h) - { - case Fruit fruit: - return new DrawableFruit(fruit); - case JuiceStream stream: - return new DrawableJuiceStream(stream, GetVisualRepresentation); - case BananaShower banana: - return new DrawableBananaShower(banana, GetVisualRepresentation); - case TinyDroplet tiny: - return new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) }; - case Droplet droplet: - return new DrawableDroplet(droplet); - } - - return null; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Input.Handlers; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Catch.Scoring; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class CatchRulesetContainer : ScrollingRulesetContainer + { + public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + } + + public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); + + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); + + protected override BeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor(); + + protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter(); + + protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation); + + public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); + + protected override DrawableHitObject GetVisualRepresentation(CatchHitObject h) + { + switch (h) + { + case Fruit fruit: + return new DrawableFruit(fruit); + case JuiceStream stream: + return new DrawableJuiceStream(stream, GetVisualRepresentation); + case BananaShower banana: + return new DrawableBananaShower(banana, GetVisualRepresentation); + case TinyDroplet tiny: + return new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) }; + case Droplet droplet: + return new DrawableDroplet(droplet); + } + + return null; + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index bf2f9db4a8..181536a91e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -1,416 +1,416 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input.Bindings; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawable; -using osu.Game.Rulesets.Catch.Replays; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Catch.UI -{ - public class CatcherArea : Container - { - public const float CATCHER_SIZE = 172; - - protected readonly Catcher MovableCatcher; - - public Func> GetVisualRepresentation; - - public Container ExplodingFruitTarget - { - set { MovableCatcher.ExplodingFruitTarget = value; } - } - - public CatcherArea(BeatmapDifficulty difficulty = null) - { - RelativeSizeAxes = Axes.X; - Height = CATCHER_SIZE; - Child = MovableCatcher = new Catcher(difficulty) - { - AdditiveTarget = this, - }; - } - - private DrawableCatchHitObject lastPlateableFruit; - - public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement) - { - if (judgement.IsHit && fruit.CanBePlated) - { - var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject); - - if (caughtFruit == null) return; - - caughtFruit.RelativePositionAxes = Axes.None; - caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0); - - caughtFruit.Anchor = Anchor.TopCentre; - caughtFruit.Origin = Anchor.Centre; - caughtFruit.Scale *= 0.7f; - caughtFruit.LifetimeEnd = double.MaxValue; - - MovableCatcher.Add(caughtFruit); - - lastPlateableFruit = caughtFruit; - } - - if (fruit.HitObject.LastInCombo) - { - if (judgement.IsHit) - { - // this is required to make this run after the last caught fruit runs UpdateState at least once. - // TODO: find a better alternative - if (lastPlateableFruit.IsLoaded) - MovableCatcher.Explode(); - else - lastPlateableFruit.OnLoadComplete = _ => { MovableCatcher.Explode(); }; - } - else - MovableCatcher.Drop(); - } - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState; - - if (state?.CatcherX != null) - MovableCatcher.X = state.CatcherX.Value; - } - - public bool OnReleased(CatchAction action) => false; - - public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); - - public class Catcher : Container, IKeyBindingHandler - { - private Texture texture; - - private Container caughtFruit; - - public Container ExplodingFruitTarget; - - public Container AdditiveTarget; - - public Catcher(BeatmapDifficulty difficulty = null) - { - RelativePositionAxes = Axes.X; - X = 0.5f; - - Origin = Anchor.TopCentre; - Anchor = Anchor.TopLeft; - - Size = new Vector2(CATCHER_SIZE); - if (difficulty != null) - Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - texture = textures.Get(@"Play/Catch/fruit-catcher-idle"); - - Children = new Drawable[] - { - caughtFruit = new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - }, - createCatcherSprite(), - }; - } - - private int currentDirection; - - private bool dashing; - - protected bool Dashing - { - get { return dashing; } - set - { - if (value == dashing) return; - - dashing = value; - - Trail |= dashing; - } - } - - private bool trail; - - /// - /// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met. - /// - protected bool Trail - { - get { return trail; } - set - { - if (value == trail) return; - - trail = value; - - if (Trail) - beginTrail(); - } - } - - private void beginTrail() - { - Trail &= dashing || HyperDashing; - Trail &= AdditiveTarget != null; - - if (!Trail) return; - - var additive = createCatcherSprite(); - - additive.Anchor = Anchor; - additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly. - additive.Position = Position; - additive.Scale = Scale; - additive.Colour = HyperDashing ? Color4.Red : Color4.White; - additive.RelativePositionAxes = RelativePositionAxes; - additive.Blending = BlendingMode.Additive; - - AdditiveTarget.Add(additive); - - additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); - - Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50); - } - - private Sprite createCatcherSprite() => new Sprite - { - Size = new Vector2(CATCHER_SIZE), - FillMode = FillMode.Fill, - Texture = texture, - OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly. - }; - - /// - /// Add a caught fruit to the catcher's stack. - /// - /// The fruit that was caught. - public void Add(DrawableHitObject fruit) - { - float ourRadius = fruit.DrawSize.X / 2 * fruit.Scale.X; - float theirRadius = 0; - - const float allowance = 6; - - while (caughtFruit.Any(f => - f.LifetimeEnd == double.MaxValue && - Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2))) - { - float diff = (ourRadius + theirRadius) / allowance; - fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff; - fruit.Y -= RNG.NextSingle() * diff; - } - - fruit.X = MathHelper.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2); - - caughtFruit.Add(fruit); - } - - /// - /// Let the catcher attempt to catch a fruit. - /// - /// The fruit to catch. - /// Whether the catch is possible. - public bool AttemptCatch(CatchHitObject fruit) - { - double halfCatcherWidth = CATCHER_SIZE * Math.Abs(Scale.X) * 0.5f; - - // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. - var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; - var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH; - - var validCatch = - catchObjectPosition >= catcherPosition - halfCatcherWidth && - catchObjectPosition <= catcherPosition + halfCatcherWidth; - - if (validCatch && fruit.HyperDash) - { - HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; - HyperDashDirection = fruit.HyperDashTarget.X - fruit.X; - } - else - HyperDashModifier = 1; - - return validCatch; - } - - /// - /// Whether we are hypderdashing or not. - /// - public bool HyperDashing => hyperDashModifier != 1; - - private double hyperDashModifier = 1; - - /// - /// The direction in which hyperdash is allowed. 0 allows both directions. - /// - public double HyperDashDirection; - - /// - /// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1. - /// - public double HyperDashModifier - { - get { return hyperDashModifier; } - set - { - if (value == hyperDashModifier) return; - hyperDashModifier = value; - - const float transition_length = 180; - - if (HyperDashing) - { - this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint); - this.FadeTo(0.2f, transition_length, Easing.OutQuint); - Trail = true; - } - else - { - HyperDashDirection = 0; - this.FadeColour(Color4.White, transition_length, Easing.OutQuint); - this.FadeTo(1, transition_length, Easing.OutQuint); - } - } - } - - public bool OnPressed(CatchAction action) - { - switch (action) - { - case CatchAction.MoveLeft: - currentDirection--; - return true; - case CatchAction.MoveRight: - currentDirection++; - return true; - case CatchAction.Dash: - Dashing = true; - return true; - } - - return false; - } - - public bool OnReleased(CatchAction action) - { - switch (action) - { - case CatchAction.MoveLeft: - currentDirection++; - return true; - case CatchAction.MoveRight: - currentDirection--; - return true; - case CatchAction.Dash: - Dashing = false; - return true; - } - - return false; - } - - /// - /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. - /// - public const double BASE_SPEED = 1.0 / 512; - - protected override void Update() - { - base.Update(); - - if (currentDirection == 0) return; - - var direction = Math.Sign(currentDirection); - - double dashModifier = Dashing ? 1 : 0.5; - - if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection))) - dashModifier = hyperDashModifier; - - Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y); - X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); - } - - /// - /// Drop any fruit off the plate. - /// - public void Drop() - { - var fruit = caughtFruit.ToArray(); - - foreach (var f in fruit) - { - if (ExplodingFruitTarget != null) - { - f.Anchor = Anchor.TopLeft; - f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget); - - caughtFruit.Remove(f); - - ExplodingFruitTarget.Add(f); - } - - f.MoveToY(f.Y + 75, 750, Easing.InSine); - f.FadeOut(750); - f.Expire(); - } - } - - /// - /// Explode any fruit off the plate. - /// - public void Explode() - { - var fruit = caughtFruit.ToArray(); - - foreach (var f in fruit) - { - var originalX = f.X * Scale.X; - - if (ExplodingFruitTarget != null) - { - f.Anchor = Anchor.TopLeft; - f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget); - - caughtFruit.Remove(f); - - ExplodingFruitTarget.Add(f); - } - - f.MoveToY(f.Y - 50, 250, Easing.OutSine) - .Then() - .MoveToY(f.Y + 50, 500, Easing.InSine); - - f.MoveToX(f.X + originalX * 6, 1000); - f.FadeOut(750); - - f.Expire(); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Bindings; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class CatcherArea : Container + { + public const float CATCHER_SIZE = 172; + + protected readonly Catcher MovableCatcher; + + public Func> GetVisualRepresentation; + + public Container ExplodingFruitTarget + { + set { MovableCatcher.ExplodingFruitTarget = value; } + } + + public CatcherArea(BeatmapDifficulty difficulty = null) + { + RelativeSizeAxes = Axes.X; + Height = CATCHER_SIZE; + Child = MovableCatcher = new Catcher(difficulty) + { + AdditiveTarget = this, + }; + } + + private DrawableCatchHitObject lastPlateableFruit; + + public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement) + { + if (judgement.IsHit && fruit.CanBePlated) + { + var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject); + + if (caughtFruit == null) return; + + caughtFruit.RelativePositionAxes = Axes.None; + caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0); + + caughtFruit.Anchor = Anchor.TopCentre; + caughtFruit.Origin = Anchor.Centre; + caughtFruit.Scale *= 0.7f; + caughtFruit.LifetimeEnd = double.MaxValue; + + MovableCatcher.Add(caughtFruit); + + lastPlateableFruit = caughtFruit; + } + + if (fruit.HitObject.LastInCombo) + { + if (judgement.IsHit) + { + // this is required to make this run after the last caught fruit runs UpdateState at least once. + // TODO: find a better alternative + if (lastPlateableFruit.IsLoaded) + MovableCatcher.Explode(); + else + lastPlateableFruit.OnLoadComplete = _ => { MovableCatcher.Explode(); }; + } + else + MovableCatcher.Drop(); + } + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState; + + if (state?.CatcherX != null) + MovableCatcher.X = state.CatcherX.Value; + } + + public bool OnReleased(CatchAction action) => false; + + public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); + + public class Catcher : Container, IKeyBindingHandler + { + private Texture texture; + + private Container caughtFruit; + + public Container ExplodingFruitTarget; + + public Container AdditiveTarget; + + public Catcher(BeatmapDifficulty difficulty = null) + { + RelativePositionAxes = Axes.X; + X = 0.5f; + + Origin = Anchor.TopCentre; + Anchor = Anchor.TopLeft; + + Size = new Vector2(CATCHER_SIZE); + if (difficulty != null) + Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + texture = textures.Get(@"Play/Catch/fruit-catcher-idle"); + + Children = new Drawable[] + { + caughtFruit = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + }, + createCatcherSprite(), + }; + } + + private int currentDirection; + + private bool dashing; + + protected bool Dashing + { + get { return dashing; } + set + { + if (value == dashing) return; + + dashing = value; + + Trail |= dashing; + } + } + + private bool trail; + + /// + /// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met. + /// + protected bool Trail + { + get { return trail; } + set + { + if (value == trail) return; + + trail = value; + + if (Trail) + beginTrail(); + } + } + + private void beginTrail() + { + Trail &= dashing || HyperDashing; + Trail &= AdditiveTarget != null; + + if (!Trail) return; + + var additive = createCatcherSprite(); + + additive.Anchor = Anchor; + additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly. + additive.Position = Position; + additive.Scale = Scale; + additive.Colour = HyperDashing ? Color4.Red : Color4.White; + additive.RelativePositionAxes = RelativePositionAxes; + additive.Blending = BlendingMode.Additive; + + AdditiveTarget.Add(additive); + + additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); + + Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50); + } + + private Sprite createCatcherSprite() => new Sprite + { + Size = new Vector2(CATCHER_SIZE), + FillMode = FillMode.Fill, + Texture = texture, + OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly. + }; + + /// + /// Add a caught fruit to the catcher's stack. + /// + /// The fruit that was caught. + public void Add(DrawableHitObject fruit) + { + float ourRadius = fruit.DrawSize.X / 2 * fruit.Scale.X; + float theirRadius = 0; + + const float allowance = 6; + + while (caughtFruit.Any(f => + f.LifetimeEnd == double.MaxValue && + Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2))) + { + float diff = (ourRadius + theirRadius) / allowance; + fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff; + fruit.Y -= RNG.NextSingle() * diff; + } + + fruit.X = MathHelper.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2); + + caughtFruit.Add(fruit); + } + + /// + /// Let the catcher attempt to catch a fruit. + /// + /// The fruit to catch. + /// Whether the catch is possible. + public bool AttemptCatch(CatchHitObject fruit) + { + double halfCatcherWidth = CATCHER_SIZE * Math.Abs(Scale.X) * 0.5f; + + // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. + var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; + var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH; + + var validCatch = + catchObjectPosition >= catcherPosition - halfCatcherWidth && + catchObjectPosition <= catcherPosition + halfCatcherWidth; + + if (validCatch && fruit.HyperDash) + { + HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; + HyperDashDirection = fruit.HyperDashTarget.X - fruit.X; + } + else + HyperDashModifier = 1; + + return validCatch; + } + + /// + /// Whether we are hypderdashing or not. + /// + public bool HyperDashing => hyperDashModifier != 1; + + private double hyperDashModifier = 1; + + /// + /// The direction in which hyperdash is allowed. 0 allows both directions. + /// + public double HyperDashDirection; + + /// + /// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1. + /// + public double HyperDashModifier + { + get { return hyperDashModifier; } + set + { + if (value == hyperDashModifier) return; + hyperDashModifier = value; + + const float transition_length = 180; + + if (HyperDashing) + { + this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint); + this.FadeTo(0.2f, transition_length, Easing.OutQuint); + Trail = true; + } + else + { + HyperDashDirection = 0; + this.FadeColour(Color4.White, transition_length, Easing.OutQuint); + this.FadeTo(1, transition_length, Easing.OutQuint); + } + } + } + + public bool OnPressed(CatchAction action) + { + switch (action) + { + case CatchAction.MoveLeft: + currentDirection--; + return true; + case CatchAction.MoveRight: + currentDirection++; + return true; + case CatchAction.Dash: + Dashing = true; + return true; + } + + return false; + } + + public bool OnReleased(CatchAction action) + { + switch (action) + { + case CatchAction.MoveLeft: + currentDirection++; + return true; + case CatchAction.MoveRight: + currentDirection--; + return true; + case CatchAction.Dash: + Dashing = false; + return true; + } + + return false; + } + + /// + /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. + /// + public const double BASE_SPEED = 1.0 / 512; + + protected override void Update() + { + base.Update(); + + if (currentDirection == 0) return; + + var direction = Math.Sign(currentDirection); + + double dashModifier = Dashing ? 1 : 0.5; + + if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection))) + dashModifier = hyperDashModifier; + + Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y); + X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); + } + + /// + /// Drop any fruit off the plate. + /// + public void Drop() + { + var fruit = caughtFruit.ToArray(); + + foreach (var f in fruit) + { + if (ExplodingFruitTarget != null) + { + f.Anchor = Anchor.TopLeft; + f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget); + + caughtFruit.Remove(f); + + ExplodingFruitTarget.Add(f); + } + + f.MoveToY(f.Y + 75, 750, Easing.InSine); + f.FadeOut(750); + f.Expire(); + } + } + + /// + /// Explode any fruit off the plate. + /// + public void Explode() + { + var fruit = caughtFruit.ToArray(); + + foreach (var f in fruit) + { + var originalX = f.X * Scale.X; + + if (ExplodingFruitTarget != null) + { + f.Anchor = Anchor.TopLeft; + f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget); + + caughtFruit.Remove(f); + + ExplodingFruitTarget.Add(f); + } + + f.MoveToY(f.Y - 50, 250, Easing.OutSine) + .Then() + .MoveToY(f.Y + 50, 500, Easing.InSine); + + f.MoveToX(f.X + originalX * 6, 1000); + f.FadeOut(750); + + f.Expire(); + } + } + } + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 9d55ab643d..81c537e53c 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -1,60 +1,60 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Tests.Beatmaps; - -namespace osu.Game.Rulesets.Mania.Tests -{ - public class ManiaBeatmapConversionTest : BeatmapConversionTest - { - protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; - - private bool isForCurrentRuleset; - - [NonParallelizable] - [TestCase("basic", false)] - public void Test(string name, bool isForCurrentRuleset) - { - this.isForCurrentRuleset = isForCurrentRuleset; - base.Test(name); - } - - protected override IEnumerable CreateConvertValue(HitObject hitObject) - { - yield return new ConvertValue - { - StartTime = hitObject.StartTime, - EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, - Column = ((ManiaHitObject)hitObject).Column - }; - } - - protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap); - } - - public struct ConvertValue : IEquatable - { - /// - /// A sane value to account for osu!stable using ints everwhere. - /// - private const float conversion_lenience = 2; - - public double StartTime; - public double EndTime; - public int Column; - - public bool Equals(ConvertValue other) - => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) - && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) - && Column == other.Column; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Mania.Tests +{ + public class ManiaBeatmapConversionTest : BeatmapConversionTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + + private bool isForCurrentRuleset; + + [NonParallelizable] + [TestCase("basic", false)] + public void Test(string name, bool isForCurrentRuleset) + { + this.isForCurrentRuleset = isForCurrentRuleset; + base.Test(name); + } + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + yield return new ConvertValue + { + StartTime = hitObject.StartTime, + EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, + Column = ((ManiaHitObject)hitObject).Column + }; + } + + protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap); + } + + public struct ConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everwhere. + /// + private const float conversion_lenience = 2; + + public double StartTime; + public double EndTime; + public int Column; + + public bool Equals(ConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) + && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) + && Column == other.Column; + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseAutoGeneration.cs index 2453d8281a..bab3a4db32 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCaseAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCaseAutoGeneration.cs @@ -1,179 +1,179 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using NUnit.Framework; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Replays; -using osu.Game.Rulesets.Replays; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Mania.Tests -{ - [TestFixture] - public class TestCaseAutoGeneration : OsuTestCase - { - [Test] - public void TestSingleNote() - { - // | | - // | - | - // | | - - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); - beatmap.HitObjects.Add(new Note { StartTime = 1000 }); - - var generated = new ManiaAutoGenerator(beatmap).Generate(); - - Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); - Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released"); - } - - [Test] - public void TestSingleHoldNote() - { - // | | - // | * | - // | * | - // | * | - // | | - - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); - beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); - - var generated = new ManiaAutoGenerator(beatmap).Generate(); - - Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released"); - } - - [Test] - public void TestSingleNoteChord() - { - // | | | - // | - | - | - // | | | - - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); - beatmap.HitObjects.Add(new Note { StartTime = 1000 }); - beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 }); - - var generated = new ManiaAutoGenerator(beatmap).Generate(); - - Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); - Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); - } - - [Test] - public void TestHoldNoteChord() - { - // | | | - // | * | * | - // | * | * | - // | * | * | - // | | | - - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); - beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); - beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 }); - - var generated = new ManiaAutoGenerator(beatmap).Generate(); - - Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); - } - - [Test] - public void TestSingleNoteStair() - { - // | | | - // | | - | - // | - | | - // | | | - - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); - beatmap.HitObjects.Add(new Note { StartTime = 1000 }); - beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 }); - - var generated = new ManiaAutoGenerator(beatmap).Generate(); - - Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); - Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time"); - Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time"); - Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released"); - Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released"); - } - - [Test] - public void TestHoldNoteStair() - { - // | | | - // | | * | - // | * | * | - // | * | * | - // | * | | - // | | | - - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); - beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); - beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 }); - - var generated = new ManiaAutoGenerator(beatmap).Generate(); - - Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time"); - Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time"); - Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); - Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released"); - Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released"); - Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released"); - } - - [Test] - public void TestHoldNoteWithReleasePress() - { - // | | | - // | * | - | - // | * | | - // | * | | - // | | | - - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); - beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY }); - beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 }); - - var generated = new ManiaAutoGenerator(beatmap).Generate(); - - Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames"); - Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); - Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time"); - Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released"); - Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed"); - Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released"); - } - - private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action)); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Replays; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class TestCaseAutoGeneration : OsuTestCase + { + [Test] + public void TestSingleNote() + { + // | | + // | - | + // | | + + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); + beatmap.HitObjects.Add(new Note { StartTime = 1000 }); + + var generated = new ManiaAutoGenerator(beatmap).Generate(); + + Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released"); + } + + [Test] + public void TestSingleHoldNote() + { + // | | + // | * | + // | * | + // | * | + // | | + + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); + + var generated = new ManiaAutoGenerator(beatmap).Generate(); + + Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released"); + } + + [Test] + public void TestSingleNoteChord() + { + // | | | + // | - | - | + // | | | + + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + beatmap.HitObjects.Add(new Note { StartTime = 1000 }); + beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap).Generate(); + + Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); + } + + [Test] + public void TestHoldNoteChord() + { + // | | | + // | * | * | + // | * | * | + // | * | * | + // | | | + + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap).Generate(); + + Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); + } + + [Test] + public void TestSingleNoteStair() + { + // | | | + // | | - | + // | - | | + // | | | + + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + beatmap.HitObjects.Add(new Note { StartTime = 1000 }); + beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap).Generate(); + + Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time"); + Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time"); + Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released"); + } + + [Test] + public void TestHoldNoteStair() + { + // | | | + // | | * | + // | * | * | + // | * | * | + // | * | | + // | | | + + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap).Generate(); + + Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time"); + Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time"); + Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released"); + Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released"); + } + + [Test] + public void TestHoldNoteWithReleasePress() + { + // | | | + // | * | - | + // | * | | + // | * | | + // | | | + + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY }); + beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap).Generate(); + + Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); + Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released"); + } + + private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action)); + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseManiaHitObjects.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseManiaHitObjects.cs index fe8749e830..281c2789af 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCaseManiaHitObjects.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCaseManiaHitObjects.cs @@ -1,96 +1,96 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Tests.Visual; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Mania.Tests -{ - [TestFixture] - public class TestCaseManiaHitObjects : OsuTestCase - { - public TestCaseManiaHitObjects() - { - Add(new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - // Imagine that the containers containing the drawable notes are the "columns" - Children = new Drawable[] - { - new Container - { - Name = "Normal note column", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Width = 50, - Children = new[] - { - new Container - { - Name = "Timing section", - RelativeSizeAxes = Axes.Both, - RelativeChildSize = new Vector2(1, 10000), - Children = new[] - { - new DrawableNote(new Note(), ManiaAction.Key1) - { - Y = 5000, - LifetimeStart = double.MinValue, - LifetimeEnd = double.MaxValue, - AccentColour = Color4.Red - }, - new DrawableNote(new Note(), ManiaAction.Key1) - { - Y = 6000, - LifetimeStart = double.MinValue, - LifetimeEnd = double.MaxValue, - AccentColour = Color4.Red - } - } - } - } - }, - new Container - { - Name = "Hold note column", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Width = 50, - Children = new[] - { - new Container - { - Name = "Timing section", - RelativeSizeAxes = Axes.Both, - RelativeChildSize = new Vector2(1, 10000), - Children = new[] - { - new DrawableHoldNote(new HoldNote { Duration = 1000 } , ManiaAction.Key1) - { - Y = 5000, - Height = 1000, - LifetimeStart = double.MinValue, - LifetimeEnd = double.MaxValue, - AccentColour = Color4.Red - } - } - } - } - } - } - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Tests.Visual; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class TestCaseManiaHitObjects : OsuTestCase + { + public TestCaseManiaHitObjects() + { + Add(new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + // Imagine that the containers containing the drawable notes are the "columns" + Children = new Drawable[] + { + new Container + { + Name = "Normal note column", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Width = 50, + Children = new[] + { + new Container + { + Name = "Timing section", + RelativeSizeAxes = Axes.Both, + RelativeChildSize = new Vector2(1, 10000), + Children = new[] + { + new DrawableNote(new Note(), ManiaAction.Key1) + { + Y = 5000, + LifetimeStart = double.MinValue, + LifetimeEnd = double.MaxValue, + AccentColour = Color4.Red + }, + new DrawableNote(new Note(), ManiaAction.Key1) + { + Y = 6000, + LifetimeStart = double.MinValue, + LifetimeEnd = double.MaxValue, + AccentColour = Color4.Red + } + } + } + } + }, + new Container + { + Name = "Hold note column", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Width = 50, + Children = new[] + { + new Container + { + Name = "Timing section", + RelativeSizeAxes = Axes.Both, + RelativeChildSize = new Vector2(1, 10000), + Children = new[] + { + new DrawableHoldNote(new HoldNote { Duration = 1000 } , ManiaAction.Key1) + { + Y = 5000, + Height = 1000, + LifetimeStart = double.MinValue, + LifetimeEnd = double.MaxValue, + AccentColour = Color4.Red + } + } + } + } + } + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseManiaPlayfield.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseManiaPlayfield.cs index 4793b1ce94..053f478027 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCaseManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCaseManiaPlayfield.cs @@ -1,193 +1,193 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Timing; -using osu.Game.Configuration; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Configuration; -using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Scoring; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Mania.Tests -{ - [TestFixture] - public class TestCaseManiaPlayfield : OsuTestCase - { - private const double start_time = 500; - private const double duration = 500; - - protected override double TimePerAction => 200; - - private RulesetInfo maniaRuleset; - - public TestCaseManiaPlayfield() - { - var rng = new Random(1337); - - AddStep("1 column", () => createPlayfield(1)); - AddStep("4 columns", () => createPlayfield(4)); - AddStep("5 columns", () => createPlayfield(5)); - AddStep("8 columns", () => createPlayfield(8)); - AddStep("4 + 4 columns", () => - { - var stages = new List - { - new StageDefinition { Columns = 4 }, - new StageDefinition { Columns = 4 }, - }; - createPlayfield(stages); - }); - - AddStep("2 + 4 + 2 columns", () => - { - var stages = new List - { - new StageDefinition { Columns = 2 }, - new StageDefinition { Columns = 4 }, - new StageDefinition { Columns = 2 }, - }; - createPlayfield(stages); - }); - - AddStep("1 + 8 + 1 columns", () => - { - var stages = new List - { - new StageDefinition { Columns = 1 }, - new StageDefinition { Columns = 8 }, - new StageDefinition { Columns = 1 }, - }; - createPlayfield(stages); - }); - - AddStep("Reversed", () => createPlayfield(4, true)); - - AddStep("Notes with input", () => createPlayfieldWithNotes()); - AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true)); - AddStep("Notes with gravity", () => createPlayfieldWithNotes()); - AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true)); - - AddStep("Hit explosion", () => - { - var playfield = createPlayfield(4); - - int col = rng.Next(0, 4); - - var note = new DrawableNote(new Note { Column = col }, ManiaAction.Key1) - { - AccentColour = playfield.Columns.ElementAt(col).AccentColour - }; - - playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect }); - playfield.Columns[col].OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect }); - }); - } - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets, SettingsStore settings) - { - maniaRuleset = rulesets.GetRuleset(3); - - dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4)); - } - - private ManiaPlayfield createPlayfield(int cols, bool inverted = false) - { - var stages = new List - { - new StageDefinition { Columns = cols }, - }; - - return createPlayfield(stages, inverted); - } - - private ManiaPlayfield createPlayfield(List stages, bool inverted = false) - { - Clear(); - - var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both }; - Add(inputManager); - - ManiaPlayfield playfield; - - inputManager.Add(playfield = new ManiaPlayfield(stages) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }); - - playfield.Inverted.Value = inverted; - - return playfield; - } - - private void createPlayfieldWithNotes(bool inverted = false) - { - Clear(); - - var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; - - var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both }; - Add(inputManager); - - ManiaPlayfield playfield; - var stages = new List - { - new StageDefinition { Columns = 4 }, - }; - - inputManager.Add(playfield = new ManiaPlayfield(stages) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Clock = new FramedClock(rateAdjustClock) - }); - - playfield.Inverted.Value = inverted; - - for (double t = start_time; t <= start_time + duration; t += 100) - { - playfield.Add(new DrawableNote(new Note - { - StartTime = t, - Column = 0 - }, ManiaAction.Key1)); - - playfield.Add(new DrawableNote(new Note - { - StartTime = t, - Column = 3 - }, ManiaAction.Key4)); - } - - playfield.Add(new DrawableHoldNote(new HoldNote - { - StartTime = start_time, - Duration = duration, - Column = 1 - }, ManiaAction.Key2)); - - playfield.Add(new DrawableHoldNote(new HoldNote - { - StartTime = start_time, - Duration = duration, - Column = 2 - }, ManiaAction.Key3)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Timing; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Configuration; +using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Scoring; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class TestCaseManiaPlayfield : OsuTestCase + { + private const double start_time = 500; + private const double duration = 500; + + protected override double TimePerAction => 200; + + private RulesetInfo maniaRuleset; + + public TestCaseManiaPlayfield() + { + var rng = new Random(1337); + + AddStep("1 column", () => createPlayfield(1)); + AddStep("4 columns", () => createPlayfield(4)); + AddStep("5 columns", () => createPlayfield(5)); + AddStep("8 columns", () => createPlayfield(8)); + AddStep("4 + 4 columns", () => + { + var stages = new List + { + new StageDefinition { Columns = 4 }, + new StageDefinition { Columns = 4 }, + }; + createPlayfield(stages); + }); + + AddStep("2 + 4 + 2 columns", () => + { + var stages = new List + { + new StageDefinition { Columns = 2 }, + new StageDefinition { Columns = 4 }, + new StageDefinition { Columns = 2 }, + }; + createPlayfield(stages); + }); + + AddStep("1 + 8 + 1 columns", () => + { + var stages = new List + { + new StageDefinition { Columns = 1 }, + new StageDefinition { Columns = 8 }, + new StageDefinition { Columns = 1 }, + }; + createPlayfield(stages); + }); + + AddStep("Reversed", () => createPlayfield(4, true)); + + AddStep("Notes with input", () => createPlayfieldWithNotes()); + AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true)); + AddStep("Notes with gravity", () => createPlayfieldWithNotes()); + AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true)); + + AddStep("Hit explosion", () => + { + var playfield = createPlayfield(4); + + int col = rng.Next(0, 4); + + var note = new DrawableNote(new Note { Column = col }, ManiaAction.Key1) + { + AccentColour = playfield.Columns.ElementAt(col).AccentColour + }; + + playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect }); + playfield.Columns[col].OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect }); + }); + } + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets, SettingsStore settings) + { + maniaRuleset = rulesets.GetRuleset(3); + + dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4)); + } + + private ManiaPlayfield createPlayfield(int cols, bool inverted = false) + { + var stages = new List + { + new StageDefinition { Columns = cols }, + }; + + return createPlayfield(stages, inverted); + } + + private ManiaPlayfield createPlayfield(List stages, bool inverted = false) + { + Clear(); + + var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both }; + Add(inputManager); + + ManiaPlayfield playfield; + + inputManager.Add(playfield = new ManiaPlayfield(stages) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + playfield.Inverted.Value = inverted; + + return playfield; + } + + private void createPlayfieldWithNotes(bool inverted = false) + { + Clear(); + + var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; + + var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both }; + Add(inputManager); + + ManiaPlayfield playfield; + var stages = new List + { + new StageDefinition { Columns = 4 }, + }; + + inputManager.Add(playfield = new ManiaPlayfield(stages) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Clock = new FramedClock(rateAdjustClock) + }); + + playfield.Inverted.Value = inverted; + + for (double t = start_time; t <= start_time + duration; t += 100) + { + playfield.Add(new DrawableNote(new Note + { + StartTime = t, + Column = 0 + }, ManiaAction.Key1)); + + playfield.Add(new DrawableNote(new Note + { + StartTime = t, + Column = 3 + }, ManiaAction.Key4)); + } + + playfield.Add(new DrawableHoldNote(new HoldNote + { + StartTime = start_time, + Duration = duration, + Column = 1 + }, ManiaAction.Key2)); + + playfield.Add(new DrawableHoldNote(new HoldNote + { + StartTime = start_time, + Duration = duration, + Column = 2 + }, ManiaAction.Key3)); + } + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Mania.Tests/TestCasePerformancePoints.cs index 3c776a2f4c..c15a6dd688 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCasePerformancePoints.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; - -namespace osu.Game.Rulesets.Mania.Tests -{ - [TestFixture] - public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints - { - public TestCasePerformancePoints() - : base(new ManiaRuleset()) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints + { + public TestCasePerformancePoints() + : base(new ManiaRuleset()) + { + } + } +} 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 187d5e47b9..02040fd23f 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -1,10 +1,10 @@ - - - - WinExe - netcoreapp2.0;net461 - - - - + + + + WinExe + netcoreapp2.0;net461 + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs index 0a248658a8..6af3719f83 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.UI; - -namespace osu.Game.Rulesets.Mania.Beatmaps -{ - public class ManiaBeatmap : Beatmap - { - /// - /// The definitions for each stage in a . - /// - public List Stages = new List(); - - /// - /// Total number of columns represented by all stages in this . - /// - public int TotalColumns => Stages.Sum(g => g.Columns); - - /// - /// Creates a new . - /// - /// The initial stages. - public ManiaBeatmap(StageDefinition defaultStage) - { - Stages.Add(defaultStage); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.UI; + +namespace osu.Game.Rulesets.Mania.Beatmaps +{ + public class ManiaBeatmap : Beatmap + { + /// + /// The definitions for each stage in a . + /// + public List Stages = new List(); + + /// + /// Total number of columns represented by all stages in this . + /// + public int TotalColumns => Stages.Sum(g => g.Columns); + + /// + /// Creates a new . + /// + /// The initial stages. + public ManiaBeatmap(StageDefinition defaultStage) + { + Stages.Add(defaultStage); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 4734e40803..60b92cb7b3 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -1,225 +1,225 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mania.Objects; -using System; -using System.Linq; -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Mania.Beatmaps.Patterns; -using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; -using OpenTK; -using osu.Game.Audio; - -namespace osu.Game.Rulesets.Mania.Beatmaps -{ - public class ManiaBeatmapConverter : BeatmapConverter - { - /// - /// Maximum number of previous notes to consider for density calculation. - /// - private const int max_notes_for_density = 7; - - protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; - - public int TargetColumns; - public readonly bool IsForCurrentRuleset; - - private Pattern lastPattern = new Pattern(); - private FastRandom random; - - private ManiaBeatmap beatmap; - - public ManiaBeatmapConverter(bool isForCurrentRuleset, Beatmap original) - { - IsForCurrentRuleset = isForCurrentRuleset; - - var roundedCircleSize = Math.Round(original.BeatmapInfo.BaseDifficulty.CircleSize); - var roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty); - - if (isForCurrentRuleset) - TargetColumns = (int)Math.Max(1, roundedCircleSize); - else - { - float percentSliderOrSpinner = (float)original.HitObjects.Count(h => h is IHasEndTime) / original.HitObjects.Count; - if (percentSliderOrSpinner < 0.2) - TargetColumns = 7; - else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) - TargetColumns = roundedOverallDifficulty > 5 ? 7 : 6; - else if (percentSliderOrSpinner > 0.6) - TargetColumns = roundedOverallDifficulty > 4 ? 5 : 4; - else - TargetColumns = Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7)); - } - } - - protected override Beatmap ConvertBeatmap(Beatmap original) - { - BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty; - - int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); - random = new FastRandom(seed); - - return base.ConvertBeatmap(original); - } - - protected override Beatmap CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns }); - - protected override IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap) - { - var maniaOriginal = original as ManiaHitObject; - if (maniaOriginal != null) - { - yield return maniaOriginal; - yield break; - } - - var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap); - - if (objects == null) - yield break; - - foreach (ManiaHitObject obj in objects) - yield return obj; - } - - private readonly List prevNoteTimes = new List(max_notes_for_density); - private double density = int.MaxValue; - private void computeDensity(double newNoteTime) - { - if (prevNoteTimes.Count == max_notes_for_density) - prevNoteTimes.RemoveAt(0); - prevNoteTimes.Add(newNoteTime); - - density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count; - } - - private double lastTime; - private Vector2 lastPosition; - private PatternType lastStair; - private void recordNote(double time, Vector2 position) - { - lastTime = time; - lastPosition = position; - } - - /// - /// Method that generates hit objects for osu!mania specific beatmaps. - /// - /// The original hit object. - /// The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap. - /// The hit objects generated. - private IEnumerable generateSpecific(HitObject original, Beatmap originalBeatmap) - { - var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap); - - Pattern newPattern = generator.Generate(); - lastPattern = newPattern; - - return newPattern.HitObjects; - } - - /// - /// Method that generates hit objects for non-osu!mania beatmaps. - /// - /// The original hit object. - /// The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap. - /// The hit objects generated. - private IEnumerable generateConverted(HitObject original, Beatmap originalBeatmap) - { - var endTimeData = original as IHasEndTime; - var distanceData = original as IHasDistance; - var positionData = original as IHasPosition; - - Patterns.PatternGenerator conversion = null; - - if (distanceData != null) - conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap); - else if (endTimeData != null) - conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap); - else if (positionData != null) - { - computeDensity(original.StartTime); - - conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap); - - recordNote(original.StartTime, positionData.Position); - } - - if (conversion == null) - return null; - - Pattern newPattern = conversion.Generate(); - - lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern; - lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair; - - return newPattern.HitObjects; - } - - /// - /// A pattern generator for osu!mania-specific beatmaps. - /// - private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator - { - public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) - : base(random, hitObject, beatmap, previousPattern, originalBeatmap) - { - } - - public override Pattern Generate() - { - var endTimeData = HitObject as IHasEndTime; - var positionData = HitObject as IHasXPosition; - - int column = GetColumn(positionData?.X ?? 0); - - var pattern = new Pattern(); - - if (endTimeData != null) - { - pattern.Add(new HoldNote - { - StartTime = HitObject.StartTime, - Duration = endTimeData.Duration, - Column = column, - Head = { Samples = sampleInfoListAt(HitObject.StartTime) }, - Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) }, - }); - } - else if (positionData != null) - { - pattern.Add(new Note - { - StartTime = HitObject.StartTime, - Samples = HitObject.Samples, - Column = column - }); - } - - return pattern; - } - - /// - /// Retrieves the sample info list at a point in time. - /// - /// The time to retrieve the sample info list from. - /// - private List sampleInfoListAt(double time) - { - var curveData = HitObject as IHasCurve; - - if (curveData == null) - return HitObject.Samples; - - double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount(); - - int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); - return curveData.RepeatSamples[index]; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mania.Objects; +using System; +using System.Linq; +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Mania.Beatmaps.Patterns; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using OpenTK; +using osu.Game.Audio; + +namespace osu.Game.Rulesets.Mania.Beatmaps +{ + public class ManiaBeatmapConverter : BeatmapConverter + { + /// + /// Maximum number of previous notes to consider for density calculation. + /// + private const int max_notes_for_density = 7; + + protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; + + public int TargetColumns; + public readonly bool IsForCurrentRuleset; + + private Pattern lastPattern = new Pattern(); + private FastRandom random; + + private ManiaBeatmap beatmap; + + public ManiaBeatmapConverter(bool isForCurrentRuleset, Beatmap original) + { + IsForCurrentRuleset = isForCurrentRuleset; + + var roundedCircleSize = Math.Round(original.BeatmapInfo.BaseDifficulty.CircleSize); + var roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty); + + if (isForCurrentRuleset) + TargetColumns = (int)Math.Max(1, roundedCircleSize); + else + { + float percentSliderOrSpinner = (float)original.HitObjects.Count(h => h is IHasEndTime) / original.HitObjects.Count; + if (percentSliderOrSpinner < 0.2) + TargetColumns = 7; + else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) + TargetColumns = roundedOverallDifficulty > 5 ? 7 : 6; + else if (percentSliderOrSpinner > 0.6) + TargetColumns = roundedOverallDifficulty > 4 ? 5 : 4; + else + TargetColumns = Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7)); + } + } + + protected override Beatmap ConvertBeatmap(Beatmap original) + { + BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty; + + int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); + random = new FastRandom(seed); + + return base.ConvertBeatmap(original); + } + + protected override Beatmap CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns }); + + protected override IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap) + { + var maniaOriginal = original as ManiaHitObject; + if (maniaOriginal != null) + { + yield return maniaOriginal; + yield break; + } + + var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap); + + if (objects == null) + yield break; + + foreach (ManiaHitObject obj in objects) + yield return obj; + } + + private readonly List prevNoteTimes = new List(max_notes_for_density); + private double density = int.MaxValue; + private void computeDensity(double newNoteTime) + { + if (prevNoteTimes.Count == max_notes_for_density) + prevNoteTimes.RemoveAt(0); + prevNoteTimes.Add(newNoteTime); + + density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count; + } + + private double lastTime; + private Vector2 lastPosition; + private PatternType lastStair; + private void recordNote(double time, Vector2 position) + { + lastTime = time; + lastPosition = position; + } + + /// + /// Method that generates hit objects for osu!mania specific beatmaps. + /// + /// The original hit object. + /// The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap. + /// The hit objects generated. + private IEnumerable generateSpecific(HitObject original, Beatmap originalBeatmap) + { + var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap); + + Pattern newPattern = generator.Generate(); + lastPattern = newPattern; + + return newPattern.HitObjects; + } + + /// + /// Method that generates hit objects for non-osu!mania beatmaps. + /// + /// The original hit object. + /// The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap. + /// The hit objects generated. + private IEnumerable generateConverted(HitObject original, Beatmap originalBeatmap) + { + var endTimeData = original as IHasEndTime; + var distanceData = original as IHasDistance; + var positionData = original as IHasPosition; + + Patterns.PatternGenerator conversion = null; + + if (distanceData != null) + conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap); + else if (endTimeData != null) + conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap); + else if (positionData != null) + { + computeDensity(original.StartTime); + + conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap); + + recordNote(original.StartTime, positionData.Position); + } + + if (conversion == null) + return null; + + Pattern newPattern = conversion.Generate(); + + lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern; + lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair; + + return newPattern.HitObjects; + } + + /// + /// A pattern generator for osu!mania-specific beatmaps. + /// + private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator + { + public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) + : base(random, hitObject, beatmap, previousPattern, originalBeatmap) + { + } + + public override Pattern Generate() + { + var endTimeData = HitObject as IHasEndTime; + var positionData = HitObject as IHasXPosition; + + int column = GetColumn(positionData?.X ?? 0); + + var pattern = new Pattern(); + + if (endTimeData != null) + { + pattern.Add(new HoldNote + { + StartTime = HitObject.StartTime, + Duration = endTimeData.Duration, + Column = column, + Head = { Samples = sampleInfoListAt(HitObject.StartTime) }, + Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) }, + }); + } + else if (positionData != null) + { + pattern.Add(new Note + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Column = column + }); + } + + return pattern; + } + + /// + /// Retrieves the sample info list at a point in time. + /// + /// The time to retrieve the sample info list from. + /// + private List sampleInfoListAt(double time) + { + var curveData = HitObject as IHasCurve; + + if (curveData == null) + return HitObject.Samples; + + double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount(); + + int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); + return curveData.RepeatSamples[index]; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 28cf119833..3b5c028bfd 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -1,491 +1,491 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy -{ - /// - /// A pattern generator for IHasDistance hit objects. - /// - internal class DistanceObjectPatternGenerator : PatternGenerator - { - /// - /// Base osu! slider scoring distance. - /// - private const float osu_base_scoring_distance = 100; - - private readonly double endTime; - private readonly double segmentDuration; - private readonly int spanCount; - - private PatternType convertType; - - public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) - : base(random, hitObject, beatmap, previousPattern, originalBeatmap) - { - convertType = PatternType.None; - if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) - convertType = PatternType.LowProbability; - - var distanceData = hitObject as IHasDistance; - var repeatsData = hitObject as IHasRepeats; - - spanCount = repeatsData?.SpanCount() ?? 1; - - TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime); - - // The true distance, accounting for any repeats - double distance = (distanceData?.Distance ?? 0) * spanCount; - // The velocity of the osu! hit object - calculated as the velocity of a slider - double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength; - // The duration of the osu! hit object - double osuDuration = distance / osuVelocity; - - endTime = hitObject.StartTime + osuDuration; - segmentDuration = (endTime - HitObject.StartTime) / spanCount; - } - - public override Pattern Generate() - { - if (spanCount > 1) - { - if (segmentDuration <= 90) - return generateRandomHoldNotes(HitObject.StartTime, 1); - - if (segmentDuration <= 120) - { - convertType |= PatternType.ForceNotStack; - return generateRandomNotes(HitObject.StartTime, spanCount + 1); - } - - if (segmentDuration <= 160) - return generateStair(HitObject.StartTime); - - if (segmentDuration <= 200 && ConversionDifficulty > 3) - return generateRandomMultipleNotes(HitObject.StartTime); - - double duration = endTime - HitObject.StartTime; - if (duration >= 4000) - return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0); - - if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart) - return generateTiledHoldNotes(HitObject.StartTime); - - return generateHoldAndNormalNotes(HitObject.StartTime); - } - - if (segmentDuration <= 110) - { - if (PreviousPattern.ColumnWithObjects < TotalColumns) - convertType |= PatternType.ForceNotStack; - else - convertType &= ~PatternType.ForceNotStack; - return generateRandomNotes(HitObject.StartTime, segmentDuration < 80 ? 1 : 2); - } - - if (ConversionDifficulty > 6.5) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0); - return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03); - } - - if (ConversionDifficulty > 4) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0); - return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0); - } - - if (ConversionDifficulty > 2.5) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0); - return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0); - } - - if ((convertType & PatternType.LowProbability) > 0) - return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0); - return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0); - } - - /// - /// Generates random hold notes that start at an span the same amount of rows. - /// - /// Start time of each hold note. - /// Number of hold notes. - /// The containing the hit objects. - private Pattern generateRandomHoldNotes(double startTime, int noteCount) - { - // - - - - - // ■ - ■ ■ - // □ - □ □ - // ■ - ■ ■ - - var pattern = new Pattern(); - - int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects; - int nextColumn = Random.Next(RandomStart, TotalColumns); - for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) - { - while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column - nextColumn = Random.Next(RandomStart, TotalColumns); - addToPattern(pattern, nextColumn, startTime, endTime); - } - - // This is can't be combined with the above loop due to RNG - for (int i = 0; i < noteCount - usableColumns; i++) - { - while (pattern.ColumnHasObject(nextColumn)) - nextColumn = Random.Next(RandomStart, TotalColumns); - addToPattern(pattern, nextColumn, startTime, endTime); - } - - return pattern; - } - - /// - /// Generates random notes, with one note per row and no stacking. - /// - /// The start time. - /// The number of notes. - /// The containing the hit objects. - private Pattern generateRandomNotes(double startTime, int noteCount) - { - // - - - - - // x - - - - // - - x - - // - - - x - // x - - - - - var pattern = new Pattern(); - - int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns) - { - while (PreviousPattern.ColumnHasObject(nextColumn)) - nextColumn = Random.Next(RandomStart, TotalColumns); - } - - int lastColumn = nextColumn; - for (int i = 0; i < noteCount; i++) - { - addToPattern(pattern, nextColumn, startTime, startTime); - while (nextColumn == lastColumn) - nextColumn = Random.Next(RandomStart, TotalColumns); - - lastColumn = nextColumn; - startTime += segmentDuration; - } - - return pattern; - } - - /// - /// Generates a stair of notes, with one note per row. - /// - /// The start time. - /// The containing the hit objects. - private Pattern generateStair(double startTime) - { - // - - - - - // x - - - - // - x - - - // - - x - - // - - - x - // - - x - - // - x - - - // x - - - - - var pattern = new Pattern(); - - int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - bool increasing = Random.NextDouble() > 0.5; - - for (int i = 0; i <= spanCount; i++) - { - addToPattern(pattern, column, startTime, startTime); - startTime += segmentDuration; - - // Check if we're at the borders of the stage, and invert the pattern if so - if (increasing) - { - if (column >= TotalColumns - 1) - { - increasing = false; - column--; - } - else - column++; - } - else - { - if (column <= RandomStart) - { - increasing = true; - column++; - } - else - column--; - } - } - - return pattern; - } - - /// - /// Generates random notes with 1-2 notes per row and no stacking. - /// - /// The start time. - /// The containing the hit objects. - private Pattern generateRandomMultipleNotes(double startTime) - { - // - - - - - // x - - - - // - x x - - // - - - x - // x - x - - - var pattern = new Pattern(); - - bool legacy = TotalColumns >= 4 && TotalColumns <= 8; - int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0)); - - int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - for (int i = 0; i <= spanCount; i++) - { - addToPattern(pattern, nextColumn, startTime, startTime); - - nextColumn += interval; - if (nextColumn >= TotalColumns - RandomStart) - nextColumn = nextColumn - TotalColumns - RandomStart + (legacy ? 1 : 0); - nextColumn += RandomStart; - - // If we're in 2K, let's not add many consecutive doubles - if (TotalColumns > 2) - addToPattern(pattern, nextColumn, startTime, startTime); - - nextColumn = Random.Next(RandomStart, TotalColumns); - startTime += segmentDuration; - } - - return pattern; - } - - /// - /// Generates random hold notes. The amount of hold notes generated is determined by probabilities. - /// - /// The hold note start time. - /// The probability required for 2 hold notes to be generated. - /// The probability required for 3 hold notes to be generated. - /// The probability required for 4 hold notes to be generated. - /// The containing the hit objects. - private Pattern generateNRandomNotes(double startTime, double p2, double p3, double p4) - { - // - - - - - // ■ - ■ ■ - // □ - □ □ - // ■ - ■ ■ - - switch (TotalColumns) - { - case 2: - p2 = 0; - p3 = 0; - p4 = 0; - break; - case 3: - p2 = Math.Min(p2, 0.1); - p3 = 0; - p4 = 0; - break; - case 4: - p2 = Math.Min(p2, 0.3); - p3 = Math.Min(p3, 0.04); - p4 = 0; - break; - case 5: - p2 = Math.Min(p2, 0.34); - p3 = Math.Min(p3, 0.1); - p4 = Math.Min(p4, 0.03); - break; - } - - bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; - - bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0; - canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample); - - if (canGenerateTwoNotes) - p2 = 1; - - return generateRandomHoldNotes(startTime, GetRandomNoteCount(p2, p3, p4)); - } - - /// - /// Generates tiled hold notes. You can think of this as a stair of hold notes. - /// - /// The first hold note start time. - /// The containing the hit objects. - private Pattern generateTiledHoldNotes(double startTime) - { - // - - - - - // ■ ■ ■ ■ - // □ □ □ □ - // □ □ □ □ - // □ □ □ ■ - // □ □ ■ - - // □ ■ - - - // ■ - - - - - var pattern = new Pattern(); - - int columnRepeat = Math.Min(spanCount, TotalColumns); - - int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns) - { - while (PreviousPattern.ColumnHasObject(nextColumn)) - nextColumn = Random.Next(RandomStart, TotalColumns); - } - - for (int i = 0; i < columnRepeat; i++) - { - while (pattern.ColumnHasObject(nextColumn)) - nextColumn = Random.Next(RandomStart, TotalColumns); - - addToPattern(pattern, nextColumn, startTime, endTime); - startTime += segmentDuration; - } - - return pattern; - } - - /// - /// Generates a hold note alongside normal notes. - /// - /// The start time of notes. - /// The containing the hit objects. - private Pattern generateHoldAndNormalNotes(double startTime) - { - // - - - - - // ■ x x - - // ■ - x x - // ■ x - x - // ■ - x x - - var pattern = new Pattern(); - - int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns) - { - while (PreviousPattern.ColumnHasObject(holdColumn)) - holdColumn = Random.Next(RandomStart, TotalColumns); - } - - // Create the hold note - addToPattern(pattern, holdColumn, startTime, endTime); - - int nextColumn = Random.Next(RandomStart, TotalColumns); - int noteCount; - if (ConversionDifficulty > 6.5) - noteCount = GetRandomNoteCount(0.63, 0); - else if (ConversionDifficulty > 4) - noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0); - else if (ConversionDifficulty > 2.5) - noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0); - else - noteCount = 0; - noteCount = Math.Min(TotalColumns - 1, noteCount); - - bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); - - var rowPattern = new Pattern(); - for (int i = 0; i <= spanCount; i++) - { - if (!(ignoreHead && startTime == HitObject.StartTime)) - { - for (int j = 0; j < noteCount; j++) - { - while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn) - nextColumn = Random.Next(RandomStart, TotalColumns); - addToPattern(rowPattern, nextColumn, startTime, startTime); - } - } - - pattern.Add(rowPattern); - rowPattern.Clear(); - - startTime += segmentDuration; - } - - return pattern; - } - - /// - /// Retrieves the sample info list at a point in time. - /// - /// The time to retrieve the sample info list from. - /// - private List sampleInfoListAt(double time) - { - var curveData = HitObject as IHasCurve; - - if (curveData == null) - return HitObject.Samples; - - double segmentTime = (endTime - HitObject.StartTime) / spanCount; - - int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); - return curveData.RepeatSamples[index]; - } - - /// - /// Constructs and adds a note to a pattern. - /// - /// The pattern to add to. - /// The column to add the note to. - /// The start time of the note. - /// The end time of the note (set to for a non-hold note). - private void addToPattern(Pattern pattern, int column, double startTime, double endTime) - { - ManiaHitObject newObject; - - if (startTime == endTime) - { - newObject = new Note - { - StartTime = startTime, - Samples = sampleInfoListAt(startTime), - Column = column - }; - } - else - { - var holdNote = new HoldNote - { - StartTime = startTime, - Column = column, - Duration = endTime - startTime, - Head = { Samples = sampleInfoListAt(startTime) }, - Tail = { Samples = sampleInfoListAt(endTime) } - }; - - newObject = holdNote; - } - - pattern.Add(newObject); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy +{ + /// + /// A pattern generator for IHasDistance hit objects. + /// + internal class DistanceObjectPatternGenerator : PatternGenerator + { + /// + /// Base osu! slider scoring distance. + /// + private const float osu_base_scoring_distance = 100; + + private readonly double endTime; + private readonly double segmentDuration; + private readonly int spanCount; + + private PatternType convertType; + + public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) + : base(random, hitObject, beatmap, previousPattern, originalBeatmap) + { + convertType = PatternType.None; + if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) + convertType = PatternType.LowProbability; + + var distanceData = hitObject as IHasDistance; + var repeatsData = hitObject as IHasRepeats; + + spanCount = repeatsData?.SpanCount() ?? 1; + + TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime); + + // The true distance, accounting for any repeats + double distance = (distanceData?.Distance ?? 0) * spanCount; + // The velocity of the osu! hit object - calculated as the velocity of a slider + double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength; + // The duration of the osu! hit object + double osuDuration = distance / osuVelocity; + + endTime = hitObject.StartTime + osuDuration; + segmentDuration = (endTime - HitObject.StartTime) / spanCount; + } + + public override Pattern Generate() + { + if (spanCount > 1) + { + if (segmentDuration <= 90) + return generateRandomHoldNotes(HitObject.StartTime, 1); + + if (segmentDuration <= 120) + { + convertType |= PatternType.ForceNotStack; + return generateRandomNotes(HitObject.StartTime, spanCount + 1); + } + + if (segmentDuration <= 160) + return generateStair(HitObject.StartTime); + + if (segmentDuration <= 200 && ConversionDifficulty > 3) + return generateRandomMultipleNotes(HitObject.StartTime); + + double duration = endTime - HitObject.StartTime; + if (duration >= 4000) + return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0); + + if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart) + return generateTiledHoldNotes(HitObject.StartTime); + + return generateHoldAndNormalNotes(HitObject.StartTime); + } + + if (segmentDuration <= 110) + { + if (PreviousPattern.ColumnWithObjects < TotalColumns) + convertType |= PatternType.ForceNotStack; + else + convertType &= ~PatternType.ForceNotStack; + return generateRandomNotes(HitObject.StartTime, segmentDuration < 80 ? 1 : 2); + } + + if (ConversionDifficulty > 6.5) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0); + return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03); + } + + if (ConversionDifficulty > 4) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0); + return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0); + } + + if (ConversionDifficulty > 2.5) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0); + return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0); + } + + if ((convertType & PatternType.LowProbability) > 0) + return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0); + return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0); + } + + /// + /// Generates random hold notes that start at an span the same amount of rows. + /// + /// Start time of each hold note. + /// Number of hold notes. + /// The containing the hit objects. + private Pattern generateRandomHoldNotes(double startTime, int noteCount) + { + // - - - - + // ■ - ■ ■ + // □ - □ □ + // ■ - ■ ■ + + var pattern = new Pattern(); + + int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects; + int nextColumn = Random.Next(RandomStart, TotalColumns); + for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) + { + while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column + nextColumn = Random.Next(RandomStart, TotalColumns); + addToPattern(pattern, nextColumn, startTime, endTime); + } + + // This is can't be combined with the above loop due to RNG + for (int i = 0; i < noteCount - usableColumns; i++) + { + while (pattern.ColumnHasObject(nextColumn)) + nextColumn = Random.Next(RandomStart, TotalColumns); + addToPattern(pattern, nextColumn, startTime, endTime); + } + + return pattern; + } + + /// + /// Generates random notes, with one note per row and no stacking. + /// + /// The start time. + /// The number of notes. + /// The containing the hit objects. + private Pattern generateRandomNotes(double startTime, int noteCount) + { + // - - - - + // x - - - + // - - x - + // - - - x + // x - - - + + var pattern = new Pattern(); + + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns) + { + while (PreviousPattern.ColumnHasObject(nextColumn)) + nextColumn = Random.Next(RandomStart, TotalColumns); + } + + int lastColumn = nextColumn; + for (int i = 0; i < noteCount; i++) + { + addToPattern(pattern, nextColumn, startTime, startTime); + while (nextColumn == lastColumn) + nextColumn = Random.Next(RandomStart, TotalColumns); + + lastColumn = nextColumn; + startTime += segmentDuration; + } + + return pattern; + } + + /// + /// Generates a stair of notes, with one note per row. + /// + /// The start time. + /// The containing the hit objects. + private Pattern generateStair(double startTime) + { + // - - - - + // x - - - + // - x - - + // - - x - + // - - - x + // - - x - + // - x - - + // x - - - + + var pattern = new Pattern(); + + int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + bool increasing = Random.NextDouble() > 0.5; + + for (int i = 0; i <= spanCount; i++) + { + addToPattern(pattern, column, startTime, startTime); + startTime += segmentDuration; + + // Check if we're at the borders of the stage, and invert the pattern if so + if (increasing) + { + if (column >= TotalColumns - 1) + { + increasing = false; + column--; + } + else + column++; + } + else + { + if (column <= RandomStart) + { + increasing = true; + column++; + } + else + column--; + } + } + + return pattern; + } + + /// + /// Generates random notes with 1-2 notes per row and no stacking. + /// + /// The start time. + /// The containing the hit objects. + private Pattern generateRandomMultipleNotes(double startTime) + { + // - - - - + // x - - - + // - x x - + // - - - x + // x - x - + + var pattern = new Pattern(); + + bool legacy = TotalColumns >= 4 && TotalColumns <= 8; + int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0)); + + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + for (int i = 0; i <= spanCount; i++) + { + addToPattern(pattern, nextColumn, startTime, startTime); + + nextColumn += interval; + if (nextColumn >= TotalColumns - RandomStart) + nextColumn = nextColumn - TotalColumns - RandomStart + (legacy ? 1 : 0); + nextColumn += RandomStart; + + // If we're in 2K, let's not add many consecutive doubles + if (TotalColumns > 2) + addToPattern(pattern, nextColumn, startTime, startTime); + + nextColumn = Random.Next(RandomStart, TotalColumns); + startTime += segmentDuration; + } + + return pattern; + } + + /// + /// Generates random hold notes. The amount of hold notes generated is determined by probabilities. + /// + /// The hold note start time. + /// The probability required for 2 hold notes to be generated. + /// The probability required for 3 hold notes to be generated. + /// The probability required for 4 hold notes to be generated. + /// The containing the hit objects. + private Pattern generateNRandomNotes(double startTime, double p2, double p3, double p4) + { + // - - - - + // ■ - ■ ■ + // □ - □ □ + // ■ - ■ ■ + + switch (TotalColumns) + { + case 2: + p2 = 0; + p3 = 0; + p4 = 0; + break; + case 3: + p2 = Math.Min(p2, 0.1); + p3 = 0; + p4 = 0; + break; + case 4: + p2 = Math.Min(p2, 0.3); + p3 = Math.Min(p3, 0.04); + p4 = 0; + break; + case 5: + p2 = Math.Min(p2, 0.34); + p3 = Math.Min(p3, 0.1); + p4 = Math.Min(p4, 0.03); + break; + } + + bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; + + bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0; + canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample); + + if (canGenerateTwoNotes) + p2 = 1; + + return generateRandomHoldNotes(startTime, GetRandomNoteCount(p2, p3, p4)); + } + + /// + /// Generates tiled hold notes. You can think of this as a stair of hold notes. + /// + /// The first hold note start time. + /// The containing the hit objects. + private Pattern generateTiledHoldNotes(double startTime) + { + // - - - - + // ■ ■ ■ ■ + // □ □ □ □ + // □ □ □ □ + // □ □ □ ■ + // □ □ ■ - + // □ ■ - - + // ■ - - - + + var pattern = new Pattern(); + + int columnRepeat = Math.Min(spanCount, TotalColumns); + + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns) + { + while (PreviousPattern.ColumnHasObject(nextColumn)) + nextColumn = Random.Next(RandomStart, TotalColumns); + } + + for (int i = 0; i < columnRepeat; i++) + { + while (pattern.ColumnHasObject(nextColumn)) + nextColumn = Random.Next(RandomStart, TotalColumns); + + addToPattern(pattern, nextColumn, startTime, endTime); + startTime += segmentDuration; + } + + return pattern; + } + + /// + /// Generates a hold note alongside normal notes. + /// + /// The start time of notes. + /// The containing the hit objects. + private Pattern generateHoldAndNormalNotes(double startTime) + { + // - - - - + // ■ x x - + // ■ - x x + // ■ x - x + // ■ - x x + + var pattern = new Pattern(); + + int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns) + { + while (PreviousPattern.ColumnHasObject(holdColumn)) + holdColumn = Random.Next(RandomStart, TotalColumns); + } + + // Create the hold note + addToPattern(pattern, holdColumn, startTime, endTime); + + int nextColumn = Random.Next(RandomStart, TotalColumns); + int noteCount; + if (ConversionDifficulty > 6.5) + noteCount = GetRandomNoteCount(0.63, 0); + else if (ConversionDifficulty > 4) + noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0); + else if (ConversionDifficulty > 2.5) + noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0); + else + noteCount = 0; + noteCount = Math.Min(TotalColumns - 1, noteCount); + + bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); + + var rowPattern = new Pattern(); + for (int i = 0; i <= spanCount; i++) + { + if (!(ignoreHead && startTime == HitObject.StartTime)) + { + for (int j = 0; j < noteCount; j++) + { + while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn) + nextColumn = Random.Next(RandomStart, TotalColumns); + addToPattern(rowPattern, nextColumn, startTime, startTime); + } + } + + pattern.Add(rowPattern); + rowPattern.Clear(); + + startTime += segmentDuration; + } + + return pattern; + } + + /// + /// Retrieves the sample info list at a point in time. + /// + /// The time to retrieve the sample info list from. + /// + private List sampleInfoListAt(double time) + { + var curveData = HitObject as IHasCurve; + + if (curveData == null) + return HitObject.Samples; + + double segmentTime = (endTime - HitObject.StartTime) / spanCount; + + int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); + return curveData.RepeatSamples[index]; + } + + /// + /// Constructs and adds a note to a pattern. + /// + /// The pattern to add to. + /// The column to add the note to. + /// The start time of the note. + /// The end time of the note (set to for a non-hold note). + private void addToPattern(Pattern pattern, int column, double startTime, double endTime) + { + ManiaHitObject newObject; + + if (startTime == endTime) + { + newObject = new Note + { + StartTime = startTime, + Samples = sampleInfoListAt(startTime), + Column = column + }; + } + else + { + var holdNote = new HoldNote + { + StartTime = startTime, + Column = column, + Duration = endTime - startTime, + Head = { Samples = sampleInfoListAt(startTime) }, + Tail = { Samples = sampleInfoListAt(endTime) } + }; + + newObject = holdNote; + } + + pattern.Add(newObject); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index ffbabba75a..743e230cb2 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -1,102 +1,102 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using System.Linq; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy -{ - internal class EndTimeObjectPatternGenerator : PatternGenerator - { - private readonly double endTime; - - public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap) - : base(random, hitObject, beatmap, new Pattern(), originalBeatmap) - { - var endtimeData = HitObject as IHasEndTime; - - endTime = endtimeData?.EndTime ?? 0; - } - - public override Pattern Generate() - { - var pattern = new Pattern(); - - bool generateHold = endTime - HitObject.StartTime >= 100; - - if (TotalColumns == 8) - { - if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000) - addToPattern(pattern, 0, generateHold); - else - addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold); - } - else if (TotalColumns > 0) - addToPattern(pattern, getNextRandomColumn(0), generateHold); - - return pattern; - } - - /// - /// Picks a random column after a column. - /// - /// The starting column. - /// A random column after . - private int getNextRandomColumn(int start) - { - int nextColumn = Random.Next(start, TotalColumns); - - while (PreviousPattern.ColumnHasObject(nextColumn)) - nextColumn = Random.Next(start, TotalColumns); - - return nextColumn; - } - - /// - /// Constructs and adds a note to a pattern. - /// - /// The pattern to add to. - /// The column to add the note to. - /// Whether to add a hold note. - private void addToPattern(Pattern pattern, int column, bool holdNote) - { - ManiaHitObject newObject; - - if (holdNote) - { - var hold = new HoldNote - { - StartTime = HitObject.StartTime, - Column = column, - Duration = endTime - HitObject.StartTime - }; - - if (hold.Head.Samples == null) - hold.Head.Samples = new List(); - - hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL }); - - hold.Tail.Samples = HitObject.Samples; - - newObject = hold; - } - else - { - newObject = new Note - { - StartTime = HitObject.StartTime, - Samples = HitObject.Samples, - Column = column - }; - } - - pattern.Add(newObject); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using System.Linq; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy +{ + internal class EndTimeObjectPatternGenerator : PatternGenerator + { + private readonly double endTime; + + public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap) + : base(random, hitObject, beatmap, new Pattern(), originalBeatmap) + { + var endtimeData = HitObject as IHasEndTime; + + endTime = endtimeData?.EndTime ?? 0; + } + + public override Pattern Generate() + { + var pattern = new Pattern(); + + bool generateHold = endTime - HitObject.StartTime >= 100; + + if (TotalColumns == 8) + { + if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000) + addToPattern(pattern, 0, generateHold); + else + addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold); + } + else if (TotalColumns > 0) + addToPattern(pattern, getNextRandomColumn(0), generateHold); + + return pattern; + } + + /// + /// Picks a random column after a column. + /// + /// The starting column. + /// A random column after . + private int getNextRandomColumn(int start) + { + int nextColumn = Random.Next(start, TotalColumns); + + while (PreviousPattern.ColumnHasObject(nextColumn)) + nextColumn = Random.Next(start, TotalColumns); + + return nextColumn; + } + + /// + /// Constructs and adds a note to a pattern. + /// + /// The pattern to add to. + /// The column to add the note to. + /// Whether to add a hold note. + private void addToPattern(Pattern pattern, int column, bool holdNote) + { + ManiaHitObject newObject; + + if (holdNote) + { + var hold = new HoldNote + { + StartTime = HitObject.StartTime, + Column = column, + Duration = endTime - HitObject.StartTime + }; + + if (hold.Head.Samples == null) + hold.Head.Samples = new List(); + + hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL }); + + hold.Tail.Samples = HitObject.Samples; + + newObject = hold; + } + else + { + newObject = new Note + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Column = column + }; + } + + pattern.Add(newObject); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index e126534c54..652c92dd78 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -1,401 +1,401 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using OpenTK; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy -{ - internal class HitObjectPatternGenerator : PatternGenerator - { - public PatternType StairType { get; private set; } - - private readonly PatternType convertType; - - public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap) - : base(random, hitObject, beatmap, previousPattern, originalBeatmap) - { - if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime)); - if (density < 0) throw new ArgumentOutOfRangeException(nameof(density)); - - StairType = lastStair; - - TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime); - - var positionData = hitObject as IHasPosition; - - float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length; - double timeSeparation = hitObject.StartTime - previousTime; - - if (timeSeparation <= 80) - { - // More than 187 BPM - convertType |= PatternType.ForceNotStack | PatternType.KeepSingle; - } - else if (timeSeparation <= 95) - { - // More than 157 BPM - convertType |= PatternType.ForceNotStack | PatternType.KeepSingle | lastStair; - } - else if (timeSeparation <= 105) - { - // More than 140 BPM - convertType |= PatternType.ForceNotStack | PatternType.LowProbability; - } - else if (timeSeparation <= 125) - { - // More than 120 BPM - convertType |= PatternType.ForceNotStack; - } - else if (timeSeparation <= 135 && positionSeparation < 20) - { - // More than 111 BPM stream - convertType |= PatternType.Cycle | PatternType.KeepSingle; - } - else if (timeSeparation <= 150 && positionSeparation < 20) - { - // More than 100 BPM stream - convertType |= PatternType.ForceStack | PatternType.LowProbability; - } - else if (positionSeparation < 20 && density >= timingPoint.BeatLength / 2.5) - { - // Low density stream - convertType |= PatternType.Reverse | PatternType.LowProbability; - } - else if (density < timingPoint.BeatLength / 2.5 || effectPoint.KiaiMode) - { - // High density - } - else - convertType |= PatternType.LowProbability; - } - - public override Pattern Generate() - { - int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0; - - if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any()) - { - // Generate a new pattern by copying the last hit objects in reverse-column order - var pattern = new Pattern(); - - for (int i = RandomStart; i < TotalColumns; i++) - if (PreviousPattern.ColumnHasObject(i)) - addToPattern(pattern, RandomStart + TotalColumns - i - 1); - - return pattern; - } - - if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1 - // If we convert to 7K + 1, let's not overload the special key - && (TotalColumns != 8 || lastColumn != 0) - // Make sure the last column was not the centre column - && (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2)) - { - // Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object) - var pattern = new Pattern(); - - int column = RandomStart + TotalColumns - lastColumn - 1; - addToPattern(pattern, column); - - return pattern; - } - - if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any()) - { - // Generate a new pattern by placing on the already filled columns - var pattern = new Pattern(); - - for (int i = RandomStart; i < TotalColumns; i++) - if (PreviousPattern.ColumnHasObject(i)) - addToPattern(pattern, i); - - return pattern; - } - - if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1) - { - // Generate a new pattern by placing on the next column, cycling back to the start if there is no "next" - var pattern = new Pattern(); - - int targetColumn = lastColumn + 1; - if (targetColumn == TotalColumns) - { - targetColumn = RandomStart; - StairType = PatternType.ReverseStair; - } - - addToPattern(pattern, targetColumn); - return pattern; - } - - if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1) - { - // Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous" - var pattern = new Pattern(); - - int targetColumn = lastColumn - 1; - if (targetColumn == RandomStart - 1) - { - targetColumn = TotalColumns - 1; - StairType = PatternType.Stair; - } - - addToPattern(pattern, targetColumn); - return pattern; - } - - if ((convertType & PatternType.KeepSingle) > 0) - return generateRandomNotes(1); - - if ((convertType & PatternType.Mirror) > 0) - { - if (ConversionDifficulty > 6.5) - return generateRandomPatternWithMirrored(0.12, 0.38, 0.12); - if (ConversionDifficulty > 4) - return generateRandomPatternWithMirrored(0.12, 0.17, 0); - return generateRandomPatternWithMirrored(0.12, 0, 0); - } - - if (ConversionDifficulty > 6.5) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateRandomPattern(0.78, 0.42, 0, 0); - return generateRandomPattern(1, 0.62, 0, 0); - } - - if (ConversionDifficulty > 4) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateRandomPattern(0.35, 0.08, 0, 0); - return generateRandomPattern(0.52, 0.15, 0, 0); - } - - if (ConversionDifficulty > 2) - { - if ((convertType & PatternType.LowProbability) > 0) - return generateRandomPattern(0.18, 0, 0, 0); - return generateRandomPattern(0.45, 0, 0, 0); - } - - return generateRandomPattern(0, 0, 0, 0); - } - - /// - /// Generates random notes. - /// - /// This will generate as many as it can up to , accounting for - /// any stacks if is forcing no stacks. - /// - /// - /// The amount of notes to generate. - /// The containing the hit objects. - private Pattern generateRandomNotes(int noteCount) - { - var pattern = new Pattern(); - - bool allowStacking = (convertType & PatternType.ForceNotStack) == 0; - - if (!allowStacking) - noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects); - - int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - for (int i = 0; i < noteCount; i++) - { - while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking) - { - if ((convertType & PatternType.Gathered) > 0) - { - nextColumn++; - if (nextColumn == TotalColumns) - nextColumn = RandomStart; - } - else - nextColumn = Random.Next(RandomStart, TotalColumns); - } - - addToPattern(pattern, nextColumn); - } - - return pattern; - } - - /// - /// Whether this hit object can generate a note in the special column. - /// - private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH); - - /// - /// Generates a random pattern. - /// - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// Probability for 4 notes to be generated. - /// Probability for 5 notes to be generated. - /// The containing the hit objects. - private Pattern generateRandomPattern(double p2, double p3, double p4, double p5) - { - var pattern = new Pattern(); - - pattern.Add(generateRandomNotes(getRandomNoteCount(p2, p3, p4, p5))); - - if (RandomStart > 0 && hasSpecialColumn) - addToPattern(pattern, 0); - - return pattern; - } - - /// - /// Generates a random pattern which has both normal and mirrored notes. - /// - /// The probability for a note to be added to the centre column. - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// The containing the hit objects. - private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3) - { - var pattern = new Pattern(); - - bool addToCentre; - int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); - - int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2; - int nextColumn = Random.Next(RandomStart, columnLimit); - for (int i = 0; i < noteCount; i++) - { - while (pattern.ColumnHasObject(nextColumn)) - nextColumn = Random.Next(RandomStart, columnLimit); - - // Add normal note - addToPattern(pattern, nextColumn); - // Add mirrored note - addToPattern(pattern, RandomStart + TotalColumns - nextColumn - 1); - } - - if (addToCentre) - addToPattern(pattern, TotalColumns / 2); - - if (RandomStart > 0 && hasSpecialColumn) - addToPattern(pattern, 0); - - return pattern; - } - - /// - /// Generates a count of notes to be generated from a list of probabilities. - /// - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// Probability for 4 notes to be generated. - /// Probability for 5 notes to be generated. - /// The amount of notes to be generated. - private int getRandomNoteCount(double p2, double p3, double p4, double p5) - { - switch (TotalColumns) - { - case 2: - p2 = 0; - p3 = 0; - p4 = 0; - p5 = 0; - break; - case 3: - p2 = Math.Min(p2, 0.1); - p3 = 0; - p4 = 0; - p5 = 0; - break; - case 4: - p2 = Math.Min(p2, 0.23); - p3 = Math.Min(p3, 0.04); - p4 = 0; - p5 = 0; - break; - case 5: - p3 = Math.Min(p3, 0.15); - p4 = Math.Min(p4, 0.03); - p5 = 0; - break; - } - - if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)) - p2 = 1; - - return GetRandomNoteCount(p2, p3, p4, p5); - } - - /// - /// Generates a count of notes to be generated from a list of probabilities. - /// - /// The probability for a note to be added to the centre column. - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// Whether to add a note to the centre column. - /// The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count. - private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre) - { - addToCentre = false; - - if ((convertType & PatternType.ForceNotStack) > 0) - return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3); - - switch (TotalColumns) - { - case 2: - centreProbability = 0; - p2 = 0; - p3 = 0; - break; - case 3: - centreProbability = Math.Min(centreProbability, 0.03); - p2 = 0; - p3 = 0; - break; - case 4: - centreProbability = 0; - p2 = Math.Min(p2 * 2, 0.2); - p3 = 0; - break; - case 5: - centreProbability = Math.Min(centreProbability, 0.03); - p3 = 0; - break; - case 6: - centreProbability = 0; - p2 = Math.Min(p2 * 2, 0.5); - p3 = Math.Min(p3 * 2, 0.15); - break; - } - - double centreVal = Random.NextDouble(); - int noteCount = GetRandomNoteCount(p2, p3); - - addToCentre = TotalColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability; - return noteCount; - } - - /// - /// Constructs and adds a note to a pattern. - /// - /// The pattern to add to. - /// The column to add the note to. - private void addToPattern(Pattern pattern, int column) - { - pattern.Add(new Note - { - StartTime = HitObject.StartTime, - Samples = HitObject.Samples, - Column = column - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using OpenTK; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy +{ + internal class HitObjectPatternGenerator : PatternGenerator + { + public PatternType StairType { get; private set; } + + private readonly PatternType convertType; + + public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap) + : base(random, hitObject, beatmap, previousPattern, originalBeatmap) + { + if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime)); + if (density < 0) throw new ArgumentOutOfRangeException(nameof(density)); + + StairType = lastStair; + + TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime); + + var positionData = hitObject as IHasPosition; + + float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length; + double timeSeparation = hitObject.StartTime - previousTime; + + if (timeSeparation <= 80) + { + // More than 187 BPM + convertType |= PatternType.ForceNotStack | PatternType.KeepSingle; + } + else if (timeSeparation <= 95) + { + // More than 157 BPM + convertType |= PatternType.ForceNotStack | PatternType.KeepSingle | lastStair; + } + else if (timeSeparation <= 105) + { + // More than 140 BPM + convertType |= PatternType.ForceNotStack | PatternType.LowProbability; + } + else if (timeSeparation <= 125) + { + // More than 120 BPM + convertType |= PatternType.ForceNotStack; + } + else if (timeSeparation <= 135 && positionSeparation < 20) + { + // More than 111 BPM stream + convertType |= PatternType.Cycle | PatternType.KeepSingle; + } + else if (timeSeparation <= 150 && positionSeparation < 20) + { + // More than 100 BPM stream + convertType |= PatternType.ForceStack | PatternType.LowProbability; + } + else if (positionSeparation < 20 && density >= timingPoint.BeatLength / 2.5) + { + // Low density stream + convertType |= PatternType.Reverse | PatternType.LowProbability; + } + else if (density < timingPoint.BeatLength / 2.5 || effectPoint.KiaiMode) + { + // High density + } + else + convertType |= PatternType.LowProbability; + } + + public override Pattern Generate() + { + int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0; + + if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any()) + { + // Generate a new pattern by copying the last hit objects in reverse-column order + var pattern = new Pattern(); + + for (int i = RandomStart; i < TotalColumns; i++) + if (PreviousPattern.ColumnHasObject(i)) + addToPattern(pattern, RandomStart + TotalColumns - i - 1); + + return pattern; + } + + if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1 + // If we convert to 7K + 1, let's not overload the special key + && (TotalColumns != 8 || lastColumn != 0) + // Make sure the last column was not the centre column + && (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2)) + { + // Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object) + var pattern = new Pattern(); + + int column = RandomStart + TotalColumns - lastColumn - 1; + addToPattern(pattern, column); + + return pattern; + } + + if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any()) + { + // Generate a new pattern by placing on the already filled columns + var pattern = new Pattern(); + + for (int i = RandomStart; i < TotalColumns; i++) + if (PreviousPattern.ColumnHasObject(i)) + addToPattern(pattern, i); + + return pattern; + } + + if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1) + { + // Generate a new pattern by placing on the next column, cycling back to the start if there is no "next" + var pattern = new Pattern(); + + int targetColumn = lastColumn + 1; + if (targetColumn == TotalColumns) + { + targetColumn = RandomStart; + StairType = PatternType.ReverseStair; + } + + addToPattern(pattern, targetColumn); + return pattern; + } + + if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1) + { + // Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous" + var pattern = new Pattern(); + + int targetColumn = lastColumn - 1; + if (targetColumn == RandomStart - 1) + { + targetColumn = TotalColumns - 1; + StairType = PatternType.Stair; + } + + addToPattern(pattern, targetColumn); + return pattern; + } + + if ((convertType & PatternType.KeepSingle) > 0) + return generateRandomNotes(1); + + if ((convertType & PatternType.Mirror) > 0) + { + if (ConversionDifficulty > 6.5) + return generateRandomPatternWithMirrored(0.12, 0.38, 0.12); + if (ConversionDifficulty > 4) + return generateRandomPatternWithMirrored(0.12, 0.17, 0); + return generateRandomPatternWithMirrored(0.12, 0, 0); + } + + if (ConversionDifficulty > 6.5) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.78, 0.42, 0, 0); + return generateRandomPattern(1, 0.62, 0, 0); + } + + if (ConversionDifficulty > 4) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.35, 0.08, 0, 0); + return generateRandomPattern(0.52, 0.15, 0, 0); + } + + if (ConversionDifficulty > 2) + { + if ((convertType & PatternType.LowProbability) > 0) + return generateRandomPattern(0.18, 0, 0, 0); + return generateRandomPattern(0.45, 0, 0, 0); + } + + return generateRandomPattern(0, 0, 0, 0); + } + + /// + /// Generates random notes. + /// + /// This will generate as many as it can up to , accounting for + /// any stacks if is forcing no stacks. + /// + /// + /// The amount of notes to generate. + /// The containing the hit objects. + private Pattern generateRandomNotes(int noteCount) + { + var pattern = new Pattern(); + + bool allowStacking = (convertType & PatternType.ForceNotStack) == 0; + + if (!allowStacking) + noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects); + + int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); + for (int i = 0; i < noteCount; i++) + { + while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking) + { + if ((convertType & PatternType.Gathered) > 0) + { + nextColumn++; + if (nextColumn == TotalColumns) + nextColumn = RandomStart; + } + else + nextColumn = Random.Next(RandomStart, TotalColumns); + } + + addToPattern(pattern, nextColumn); + } + + return pattern; + } + + /// + /// Whether this hit object can generate a note in the special column. + /// + private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH); + + /// + /// Generates a random pattern. + /// + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Probability for 4 notes to be generated. + /// Probability for 5 notes to be generated. + /// The containing the hit objects. + private Pattern generateRandomPattern(double p2, double p3, double p4, double p5) + { + var pattern = new Pattern(); + + pattern.Add(generateRandomNotes(getRandomNoteCount(p2, p3, p4, p5))); + + if (RandomStart > 0 && hasSpecialColumn) + addToPattern(pattern, 0); + + return pattern; + } + + /// + /// Generates a random pattern which has both normal and mirrored notes. + /// + /// The probability for a note to be added to the centre column. + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// The containing the hit objects. + private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3) + { + var pattern = new Pattern(); + + bool addToCentre; + int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); + + int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2; + int nextColumn = Random.Next(RandomStart, columnLimit); + for (int i = 0; i < noteCount; i++) + { + while (pattern.ColumnHasObject(nextColumn)) + nextColumn = Random.Next(RandomStart, columnLimit); + + // Add normal note + addToPattern(pattern, nextColumn); + // Add mirrored note + addToPattern(pattern, RandomStart + TotalColumns - nextColumn - 1); + } + + if (addToCentre) + addToPattern(pattern, TotalColumns / 2); + + if (RandomStart > 0 && hasSpecialColumn) + addToPattern(pattern, 0); + + return pattern; + } + + /// + /// Generates a count of notes to be generated from a list of probabilities. + /// + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Probability for 4 notes to be generated. + /// Probability for 5 notes to be generated. + /// The amount of notes to be generated. + private int getRandomNoteCount(double p2, double p3, double p4, double p5) + { + switch (TotalColumns) + { + case 2: + p2 = 0; + p3 = 0; + p4 = 0; + p5 = 0; + break; + case 3: + p2 = Math.Min(p2, 0.1); + p3 = 0; + p4 = 0; + p5 = 0; + break; + case 4: + p2 = Math.Min(p2, 0.23); + p3 = Math.Min(p3, 0.04); + p4 = 0; + p5 = 0; + break; + case 5: + p3 = Math.Min(p3, 0.15); + p4 = Math.Min(p4, 0.03); + p5 = 0; + break; + } + + if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)) + p2 = 1; + + return GetRandomNoteCount(p2, p3, p4, p5); + } + + /// + /// Generates a count of notes to be generated from a list of probabilities. + /// + /// The probability for a note to be added to the centre column. + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Whether to add a note to the centre column. + /// The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count. + private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre) + { + addToCentre = false; + + if ((convertType & PatternType.ForceNotStack) > 0) + return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3); + + switch (TotalColumns) + { + case 2: + centreProbability = 0; + p2 = 0; + p3 = 0; + break; + case 3: + centreProbability = Math.Min(centreProbability, 0.03); + p2 = 0; + p3 = 0; + break; + case 4: + centreProbability = 0; + p2 = Math.Min(p2 * 2, 0.2); + p3 = 0; + break; + case 5: + centreProbability = Math.Min(centreProbability, 0.03); + p3 = 0; + break; + case 6: + centreProbability = 0; + p2 = Math.Min(p2 * 2, 0.5); + p3 = Math.Min(p3 * 2, 0.15); + break; + } + + double centreVal = Random.NextDouble(); + int noteCount = GetRandomNoteCount(p2, p3); + + addToCentre = TotalColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability; + return noteCount; + } + + /// + /// Constructs and adds a note to a pattern. + /// + /// The pattern to add to. + /// The column to add the note to. + private void addToPattern(Pattern pattern, int column) + { + pattern.Add(new Note + { + StartTime = HitObject.StartTime, + Samples = HitObject.Samples, + Column = column + }); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 501950cdcd..02306846a3 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -1,123 +1,123 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; -using osu.Game.Rulesets.Objects; -using OpenTK; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy -{ - /// - /// A pattern generator for legacy hit objects. - /// - internal abstract class PatternGenerator : Patterns.PatternGenerator - { - /// - /// The column index at which to start generating random notes. - /// - protected readonly int RandomStart; - - /// - /// The random number generator to use. - /// - protected readonly FastRandom Random; - - /// - /// The beatmap which is being converted from. - /// - protected readonly Beatmap OriginalBeatmap; - - protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) - : base(hitObject, beatmap, previousPattern) - { - if (random == null) throw new ArgumentNullException(nameof(random)); - if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap)); - - Random = random; - OriginalBeatmap = originalBeatmap; - - RandomStart = TotalColumns == 8 ? 1 : 0; - } - - /// - /// Converts an x-position into a column. - /// - /// The x-position. - /// Whether to treat as 7K + 1. - /// The column. - protected int GetColumn(float position, bool allowSpecial = false) - { - if (allowSpecial && TotalColumns == 8) - { - const float local_x_divisor = 512f / 7; - return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1; - } - - float localXDivisor = 512f / TotalColumns; - return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, TotalColumns - 1); - } - - /// - /// Generates a count of notes to be generated from probabilities. - /// - /// Probability for 2 notes to be generated. - /// Probability for 3 notes to be generated. - /// Probability for 4 notes to be generated. - /// Probability for 5 notes to be generated. - /// Probability for 6 notes to be generated. - /// The amount of notes to be generated. - protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0) - { - if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2)); - if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3)); - if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4)); - if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5)); - if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6)); - - double val = Random.NextDouble(); - if (val >= 1 - p6) - return 6; - if (val >= 1 - p5) - return 5; - if (val >= 1 - p4) - return 4; - if (val >= 1 - p3) - return 3; - return val >= 1 - p2 ? 2 : 1; - } - - private double? conversionDifficulty; - /// - /// A difficulty factor used for various conversion methods from osu!stable. - /// - protected double ConversionDifficulty - { - get - { - if (conversionDifficulty != null) - return conversionDifficulty.Value; - - HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault(); - HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault(); - - double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0); - drainTime -= OriginalBeatmap.TotalBreakTime; - - if (drainTime == 0) - drainTime = 10000000; - - // We need this in seconds - drainTime /= 1000; - - BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; - conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; - conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); - - return conversionDifficulty.Value; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.MathUtils; +using osu.Game.Rulesets.Objects; +using OpenTK; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy +{ + /// + /// A pattern generator for legacy hit objects. + /// + internal abstract class PatternGenerator : Patterns.PatternGenerator + { + /// + /// The column index at which to start generating random notes. + /// + protected readonly int RandomStart; + + /// + /// The random number generator to use. + /// + protected readonly FastRandom Random; + + /// + /// The beatmap which is being converted from. + /// + protected readonly Beatmap OriginalBeatmap; + + protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) + : base(hitObject, beatmap, previousPattern) + { + if (random == null) throw new ArgumentNullException(nameof(random)); + if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap)); + + Random = random; + OriginalBeatmap = originalBeatmap; + + RandomStart = TotalColumns == 8 ? 1 : 0; + } + + /// + /// Converts an x-position into a column. + /// + /// The x-position. + /// Whether to treat as 7K + 1. + /// The column. + protected int GetColumn(float position, bool allowSpecial = false) + { + if (allowSpecial && TotalColumns == 8) + { + const float local_x_divisor = 512f / 7; + return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1; + } + + float localXDivisor = 512f / TotalColumns; + return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, TotalColumns - 1); + } + + /// + /// Generates a count of notes to be generated from probabilities. + /// + /// Probability for 2 notes to be generated. + /// Probability for 3 notes to be generated. + /// Probability for 4 notes to be generated. + /// Probability for 5 notes to be generated. + /// Probability for 6 notes to be generated. + /// The amount of notes to be generated. + protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0) + { + if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2)); + if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3)); + if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4)); + if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5)); + if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6)); + + double val = Random.NextDouble(); + if (val >= 1 - p6) + return 6; + if (val >= 1 - p5) + return 5; + if (val >= 1 - p4) + return 4; + if (val >= 1 - p3) + return 3; + return val >= 1 - p2 ? 2 : 1; + } + + private double? conversionDifficulty; + /// + /// A difficulty factor used for various conversion methods from osu!stable. + /// + protected double ConversionDifficulty + { + get + { + if (conversionDifficulty != null) + return conversionDifficulty.Value; + + HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault(); + HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault(); + + double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0); + drainTime -= OriginalBeatmap.TotalBreakTime; + + if (drainTime == 0) + drainTime = 10000000; + + // We need this in seconds + drainTime /= 1000; + + BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; + conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; + conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); + + return conversionDifficulty.Value; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs index 2ead37e136..2eba2d5843 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs @@ -1,65 +1,65 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy -{ - /// - /// The type of pattern to generate. Used for legacy patterns. - /// - [Flags] - internal enum PatternType - { - None = 0, - /// - /// Keep the same as last row. - /// - ForceStack = 1 << 0, - /// - /// Keep different from last row. - /// - ForceNotStack = 1 << 1, - /// - /// Keep as single note at its original position. - /// - KeepSingle = 1 << 2, - /// - /// Use a lower random value. - /// - LowProbability = 1 << 3, - /// - /// Reserved. - /// - Alternate = 1 << 4, - /// - /// Ignore the repeat count. - /// - ForceSigSlider = 1 << 5, - /// - /// Convert slider to circle. - /// - ForceNotSlider = 1 << 6, - /// - /// Notes gathered together. - /// - Gathered = 1 << 7, - Mirror = 1 << 8, - /// - /// Change 0 -> 6. - /// - Reverse = 1 << 9, - /// - /// 1 -> 5 -> 1 -> 5 like reverse. - /// - Cycle = 1 << 10, - /// - /// Next note will be at column + 1. - /// - Stair = 1 << 11, - /// - /// Next note will be at column - 1. - /// - ReverseStair = 1 << 12 - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy +{ + /// + /// The type of pattern to generate. Used for legacy patterns. + /// + [Flags] + internal enum PatternType + { + None = 0, + /// + /// Keep the same as last row. + /// + ForceStack = 1 << 0, + /// + /// Keep different from last row. + /// + ForceNotStack = 1 << 1, + /// + /// Keep as single note at its original position. + /// + KeepSingle = 1 << 2, + /// + /// Use a lower random value. + /// + LowProbability = 1 << 3, + /// + /// Reserved. + /// + Alternate = 1 << 4, + /// + /// Ignore the repeat count. + /// + ForceSigSlider = 1 << 5, + /// + /// Convert slider to circle. + /// + ForceNotSlider = 1 << 6, + /// + /// Notes gathered together. + /// + Gathered = 1 << 7, + Mirror = 1 << 8, + /// + /// Change 0 -> 6. + /// + Reverse = 1 << 9, + /// + /// 1 -> 5 -> 1 -> 5 like reverse. + /// + Cycle = 1 << 10, + /// + /// Next note will be at column + 1. + /// + Stair = 1 << 11, + /// + /// Next note will be at column - 1. + /// + ReverseStair = 1 << 12 + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs index d4b0eafa64..32d2d096b2 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs @@ -1,57 +1,57 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Rulesets.Mania.Objects; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns -{ - /// - /// Creates a pattern containing hit objects. - /// - internal class Pattern - { - private readonly List hitObjects = new List(); - - /// - /// All the hit objects contained in this pattern. - /// - public IEnumerable HitObjects => hitObjects; - - /// - /// Check whether a column of this patterns contains a hit object. - /// - /// The column index. - /// Whether the column with index contains a hit object. - public bool ColumnHasObject(int column) => hitObjects.Exists(h => h.Column == column); - - /// - /// Amount of columns taken up by hit objects in this pattern. - /// - public int ColumnWithObjects => HitObjects.GroupBy(h => h.Column).Count(); - - /// - /// Adds a hit object to this pattern. - /// - /// The hit object to add. - public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject); - - /// - /// Copies hit object from another pattern to this one. - /// - /// The other pattern. - public void Add(Pattern other) => hitObjects.AddRange(other.HitObjects); - - /// - /// Clears this pattern, removing all hit objects. - /// - public void Clear() => hitObjects.Clear(); - - /// - /// Removes a hit object from this pattern. - /// - /// The hit object to remove. - public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Rulesets.Mania.Objects; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns +{ + /// + /// Creates a pattern containing hit objects. + /// + internal class Pattern + { + private readonly List hitObjects = new List(); + + /// + /// All the hit objects contained in this pattern. + /// + public IEnumerable HitObjects => hitObjects; + + /// + /// Check whether a column of this patterns contains a hit object. + /// + /// The column index. + /// Whether the column with index contains a hit object. + public bool ColumnHasObject(int column) => hitObjects.Exists(h => h.Column == column); + + /// + /// Amount of columns taken up by hit objects in this pattern. + /// + public int ColumnWithObjects => HitObjects.GroupBy(h => h.Column).Count(); + + /// + /// Adds a hit object to this pattern. + /// + /// The hit object to add. + public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject); + + /// + /// Copies hit object from another pattern to this one. + /// + /// The other pattern. + public void Add(Pattern other) => hitObjects.AddRange(other.HitObjects); + + /// + /// Clears this pattern, removing all hit objects. + /// + public void Clear() => hitObjects.Clear(); + + /// + /// Removes a hit object from this pattern. + /// + /// The hit object to remove. + public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject); + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs index 6862fd57c1..2bfcd52b6a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs @@ -1,50 +1,50 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns -{ - /// - /// Generator to create a pattern from a hit object. - /// - internal abstract class PatternGenerator - { - /// - /// The last pattern. - /// - protected readonly Pattern PreviousPattern; - - /// - /// The hit object to create the pattern for. - /// - protected readonly HitObject HitObject; - - /// - /// The beatmap which is a part of. - /// - protected readonly ManiaBeatmap Beatmap; - - protected readonly int TotalColumns; - - protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) - { - if (hitObject == null) throw new ArgumentNullException(nameof(hitObject)); - if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); - if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern)); - - HitObject = hitObject; - Beatmap = beatmap; - PreviousPattern = previousPattern; - - TotalColumns = Beatmap.TotalColumns; - } - - /// - /// Generates the pattern for , filled with hit objects. - /// - /// The containing the hit objects. - public abstract Pattern Generate(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns +{ + /// + /// Generator to create a pattern from a hit object. + /// + internal abstract class PatternGenerator + { + /// + /// The last pattern. + /// + protected readonly Pattern PreviousPattern; + + /// + /// The hit object to create the pattern for. + /// + protected readonly HitObject HitObject; + + /// + /// The beatmap which is a part of. + /// + protected readonly ManiaBeatmap Beatmap; + + protected readonly int TotalColumns; + + protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) + { + if (hitObject == null) throw new ArgumentNullException(nameof(hitObject)); + if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); + if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern)); + + HitObject = hitObject; + Beatmap = beatmap; + PreviousPattern = previousPattern; + + TotalColumns = Beatmap.TotalColumns; + } + + /// + /// Generates the pattern for , filled with hit objects. + /// + /// The containing the hit objects. + public abstract Pattern Generate(); + } +} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs index cb500735f7..afec607a7c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mania.UI; - -namespace osu.Game.Rulesets.Mania.Beatmaps -{ - /// - /// Defines properties for each stage in a . - /// - public struct StageDefinition - { - /// - /// The number of s which this stage contains. - /// - public int Columns; - - /// - /// Whether the column index is a special column for this stage. - /// - /// The 0-based column index. - /// Whether the column is a special column. - public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mania.UI; + +namespace osu.Game.Rulesets.Mania.Beatmaps +{ + /// + /// Defines properties for each stage in a . + /// + public struct StageDefinition + { + /// + /// The number of s which this stage contains. + /// + public int Columns; + + /// + /// Whether the column index is a special column for this stage. + /// + /// The 0-based column index. + /// Whether the column is a special column. + public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2; + } +} diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs index a4de360870..ea5f590bd1 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration.Tracking; -using osu.Game.Configuration; -using osu.Game.Rulesets.Configuration; - -namespace osu.Game.Rulesets.Mania.Configuration -{ - public class ManiaConfigManager : RulesetConfigManager - { - public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant) - : base(settings, ruleset, variant) - { - } - - protected override void InitialiseDefaults() - { - base.InitialiseDefaults(); - - Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0); - } - - public override TrackedSettings CreateTrackedSettings() => new TrackedSettings - { - new TrackedSetting(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms")) - }; - } - - public enum ManiaSetting - { - ScrollTime - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration.Tracking; +using osu.Game.Configuration; +using osu.Game.Rulesets.Configuration; + +namespace osu.Game.Rulesets.Mania.Configuration +{ + public class ManiaConfigManager : RulesetConfigManager + { + public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant) + : base(settings, ruleset, variant) + { + } + + protected override void InitialiseDefaults() + { + base.InitialiseDefaults(); + + Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0); + } + + public override TrackedSettings CreateTrackedSettings() => new TrackedSettings + { + new TrackedSetting(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms")) + }; + } + + public enum ManiaSetting + { + ScrollTime + } +} diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs index 361923266c..3a4beda77d 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Judgements -{ - public class HoldNoteTailJudgement : ManiaJudgement - { - /// - /// Whether the hold note has been released too early and shouldn't give full score for the release. - /// - public bool HasBroken; - - protected override int NumericResultFor(HitResult result) - { - switch (result) - { - default: - return base.NumericResultFor(result); - case HitResult.Great: - case HitResult.Perfect: - return base.NumericResultFor(HasBroken ? HitResult.Good : result); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Judgements +{ + public class HoldNoteTailJudgement : ManiaJudgement + { + /// + /// Whether the hold note has been released too early and shouldn't give full score for the release. + /// + public bool HasBroken; + + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return base.NumericResultFor(result); + case HitResult.Great: + case HitResult.Perfect: + return base.NumericResultFor(HasBroken ? HitResult.Good : result); + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs index 62df4a30cc..6eb5a79200 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Judgements -{ - public class HoldNoteTickJudgement : ManiaJudgement - { - public override bool AffectsCombo => false; - - protected override int NumericResultFor(HitResult result) => 20; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Judgements +{ + public class HoldNoteTickJudgement : ManiaJudgement + { + public override bool AffectsCombo => false; + + protected override int NumericResultFor(HitResult result) => 20; + } +} diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs index dae89bf88d..4e0649c708 100644 --- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Judgements -{ - public class ManiaJudgement : Judgement - { - protected override int NumericResultFor(HitResult result) - { - switch (result) - { - default: - return 0; - case HitResult.Meh: - return 50; - case HitResult.Ok: - return 100; - case HitResult.Good: - return 200; - case HitResult.Great: - case HitResult.Perfect: - return 300; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Judgements +{ + public class ManiaJudgement : Judgement + { + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Meh: + return 50; + case HitResult.Ok: + return 100; + case HitResult.Good: + return 200; + case HitResult.Great: + case HitResult.Perfect: + return 300; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index f4e3d54a3d..5eea346836 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -1,146 +1,146 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mods; -using System; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Mania -{ - internal class ManiaDifficultyCalculator : DifficultyCalculator - { - private const double star_scaling_factor = 0.018; - - /// - /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step. - /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. - /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. - /// - private const double strain_step = 400; - - /// - /// The weighting of each strain value decays to this number * it's previous value - /// - private const double decay_weight = 0.9; - - /// - /// HitObjects are stored as a member variable. - /// - private readonly List difficultyHitObjects = new List(); - - public ManiaDifficultyCalculator(Beatmap beatmap) - : base(beatmap) - { - } - - public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods) - : base(beatmap, mods) - { - } - - public override double Calculate(Dictionary categoryDifficulty = null) - { - // Fill our custom DifficultyHitObject class, that carries additional information - difficultyHitObjects.Clear(); - - int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7; - - foreach (var hitObject in Beatmap.HitObjects) - difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount)); - - // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. - difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); - - if (!calculateStrainValues()) - return 0; - - double starRating = calculateDifficulty() * star_scaling_factor; - - categoryDifficulty?.Add("Strain", starRating); - - return starRating; - } - - private bool calculateStrainValues() - { - // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - using (List.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) - { - if (!hitObjectsEnumerator.MoveNext()) - return false; - - ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current; - - // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. - while (hitObjectsEnumerator.MoveNext()) - { - var next = hitObjectsEnumerator.Current; - next?.CalculateStrains(current, TimeRate); - current = next; - } - - return true; - } - } - - private double calculateDifficulty() - { - double actualStrainStep = strain_step * TimeRate; - - // Find the highest strain value within each strain step - List highestStrains = new List(); - double intervalEndTime = actualStrainStep; - double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval - - ManiaHitObjectDifficulty previousHitObject = null; - foreach (var hitObject in difficultyHitObjects) - { - // While we are beyond the current interval push the currently available maximum to our strain list - while (hitObject.BaseHitObject.StartTime > intervalEndTime) - { - highestStrains.Add(maximumStrain); - - // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay - // until the beginning of the next interval. - if (previousHitObject == null) - { - maximumStrain = 0; - } - else - { - double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); - double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); - maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay; - } - - // Go to the next time interval - intervalEndTime += actualStrainStep; - } - - // Obtain maximum strain - double strain = hitObject.IndividualStrain + hitObject.OverallStrain; - maximumStrain = Math.Max(strain, maximumStrain); - - previousHitObject = hitObject; - } - - // Build the weighted sum over the highest strains for each interval - double difficulty = 0; - double weight = 1; - highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - foreach (double strain in highestStrains) - { - difficulty += weight * strain; - weight *= decay_weight; - } - - return difficulty; - } - - protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mods; +using System; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Mania +{ + internal class ManiaDifficultyCalculator : DifficultyCalculator + { + private const double star_scaling_factor = 0.018; + + /// + /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step. + /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. + /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. + /// + private const double strain_step = 400; + + /// + /// The weighting of each strain value decays to this number * it's previous value + /// + private const double decay_weight = 0.9; + + /// + /// HitObjects are stored as a member variable. + /// + private readonly List difficultyHitObjects = new List(); + + public ManiaDifficultyCalculator(Beatmap beatmap) + : base(beatmap) + { + } + + public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods) + : base(beatmap, mods) + { + } + + public override double Calculate(Dictionary categoryDifficulty = null) + { + // Fill our custom DifficultyHitObject class, that carries additional information + difficultyHitObjects.Clear(); + + int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7; + + foreach (var hitObject in Beatmap.HitObjects) + difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount)); + + // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. + difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); + + if (!calculateStrainValues()) + return 0; + + double starRating = calculateDifficulty() * star_scaling_factor; + + categoryDifficulty?.Add("Strain", starRating); + + return starRating; + } + + private bool calculateStrainValues() + { + // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. + using (List.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) + { + if (!hitObjectsEnumerator.MoveNext()) + return false; + + ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current; + + // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. + while (hitObjectsEnumerator.MoveNext()) + { + var next = hitObjectsEnumerator.Current; + next?.CalculateStrains(current, TimeRate); + current = next; + } + + return true; + } + } + + private double calculateDifficulty() + { + double actualStrainStep = strain_step * TimeRate; + + // Find the highest strain value within each strain step + List highestStrains = new List(); + double intervalEndTime = actualStrainStep; + double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval + + ManiaHitObjectDifficulty previousHitObject = null; + foreach (var hitObject in difficultyHitObjects) + { + // While we are beyond the current interval push the currently available maximum to our strain list + while (hitObject.BaseHitObject.StartTime > intervalEndTime) + { + highestStrains.Add(maximumStrain); + + // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay + // until the beginning of the next interval. + if (previousHitObject == null) + { + maximumStrain = 0; + } + else + { + double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); + double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); + maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay; + } + + // Go to the next time interval + intervalEndTime += actualStrainStep; + } + + // Obtain maximum strain + double strain = hitObject.IndividualStrain + hitObject.OverallStrain; + maximumStrain = Math.Max(strain, maximumStrain); + + previousHitObject = hitObject; + } + + // Build the weighted sum over the highest strains for each interval + double difficulty = 0; + double weight = 1; + highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + foreach (double strain in highestStrains) + { + difficulty += weight * strain; + weight *= decay_weight; + } + + return difficulty; + } + + protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap); + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaInputManager.cs b/osu.Game.Rulesets.Mania/ManiaInputManager.cs index 01e2821540..61356d96dc 100644 --- a/osu.Game.Rulesets.Mania/ManiaInputManager.cs +++ b/osu.Game.Rulesets.Mania/ManiaInputManager.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Mania -{ - public class ManiaInputManager : RulesetInputManager - { - public ManiaInputManager(RulesetInfo ruleset, int variant) - : base(ruleset, variant, SimultaneousBindingMode.Unique) - { - } - } - - public enum ManiaAction - { - [Description("Special 1")] - Special1 = 1, - [Description("Special 2")] - Special2, - - // This offsets the start value of normal keys in-case we add more special keys - // above at a later time, without breaking replays/configs. - [Description("Key 1")] - Key1 = 10, - [Description("Key 2")] - Key2, - [Description("Key 3")] - Key3, - [Description("Key 4")] - Key4, - [Description("Key 5")] - Key5, - [Description("Key 6")] - Key6, - [Description("Key 7")] - Key7, - [Description("Key 8")] - Key8, - [Description("Key 9")] - Key9, - [Description("Key 10")] - Key10, - [Description("Key 11")] - Key11, - [Description("Key 12")] - Key12, - [Description("Key 13")] - Key13, - [Description("Key 14")] - Key14, - [Description("Key 15")] - Key15, - [Description("Key 16")] - Key16, - [Description("Key 17")] - Key17, - [Description("Key 18")] - Key18, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mania +{ + public class ManiaInputManager : RulesetInputManager + { + public ManiaInputManager(RulesetInfo ruleset, int variant) + : base(ruleset, variant, SimultaneousBindingMode.Unique) + { + } + } + + public enum ManiaAction + { + [Description("Special 1")] + Special1 = 1, + [Description("Special 2")] + Special2, + + // This offsets the start value of normal keys in-case we add more special keys + // above at a later time, without breaking replays/configs. + [Description("Key 1")] + Key1 = 10, + [Description("Key 2")] + Key2, + [Description("Key 3")] + Key3, + [Description("Key 4")] + Key4, + [Description("Key 5")] + Key5, + [Description("Key 6")] + Key6, + [Description("Key 7")] + Key7, + [Description("Key 8")] + Key8, + [Description("Key 9")] + Key9, + [Description("Key 10")] + Key10, + [Description("Key 11")] + Key11, + [Description("Key 12")] + Key12, + [Description("Key 13")] + Key13, + [Description("Key 14")] + Key14, + [Description("Key 15")] + Key15, + [Description("Key 16")] + Key16, + [Description("Key 17")] + Key17, + [Description("Key 18")] + Key18, + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index e135e14001..7f37f55d14 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -1,312 +1,312 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mania.Replays; -using osu.Game.Rulesets.Replays.Types; - -namespace osu.Game.Rulesets.Mania -{ - public class ManiaRuleset : Ruleset - { - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset); - - public override IEnumerable GetModsFor(ModType type) - { - switch (type) - { - case ModType.DifficultyReduction: - return new Mod[] - { - new ManiaModEasy(), - new ManiaModNoFail(), - new MultiMod - { - Mods = new Mod[] - { - new ManiaModHalfTime(), - new ManiaModDaycore(), - }, - }, - }; - - case ModType.DifficultyIncrease: - return new Mod[] - { - new ManiaModHardRock(), - new MultiMod - { - Mods = new Mod[] - { - new ManiaModSuddenDeath(), - new ManiaModPerfect(), - }, - }, - new MultiMod - { - Mods = new Mod[] - { - new ManiaModDoubleTime(), - new ManiaModNightcore(), - }, - }, - new MultiMod - { - Mods = new Mod[] - { - new ManiaModFadeIn(), - new ManiaModHidden(), - } - }, - new ManiaModFlashlight(), - }; - - case ModType.Special: - return new Mod[] - { - new MultiMod - { - Mods = new Mod[] - { - new ManiaModKey4(), - new ManiaModKey5(), - new ManiaModKey6(), - new ManiaModKey7(), - new ManiaModKey8(), - new ManiaModKey9(), - new ManiaModKey1(), - new ManiaModKey2(), - new ManiaModKey3(), - }, - }, - new ManiaModRandom(), - new ManiaModDualStages(), - new ManiaModMirror(), - new MultiMod - { - Mods = new Mod[] - { - new ManiaModAutoplay(), - new ModCinema(), - }, - }, - }; - - default: - return new Mod[] { }; - } - } - - public override string Description => "osu!mania"; - - public override string ShortName => "mania"; - - public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; - - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods); - - public override int? LegacyID => 3; - - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); - - public ManiaRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } - - public override IEnumerable AvailableVariants - { - get - { - for (int i = 1; i <= 9; i++) - yield return (int)PlayfieldType.Single + i; - for (int i = 2; i <= 18; i += 2) - yield return (int)PlayfieldType.Dual + i; - } - } - - public override IEnumerable GetDefaultKeyBindings(int variant = 0) - { - switch (getPlayfieldType(variant)) - { - case PlayfieldType.Single: - return new VariantMappingGenerator - { - LeftKeys = new[] - { - InputKey.A, - InputKey.S, - InputKey.D, - InputKey.F - }, - RightKeys = new[] - { - InputKey.J, - InputKey.K, - InputKey.L, - InputKey.Semicolon - }, - SpecialKey = InputKey.Space, - SpecialAction = ManiaAction.Special1, - NormalActionStart = ManiaAction.Key1, - }.GenerateKeyBindingsFor(variant, out _); - case PlayfieldType.Dual: - int keys = getDualStageKeyCount(variant); - - var stage1Bindings = new VariantMappingGenerator - { - LeftKeys = new[] - { - InputKey.Number1, - InputKey.Number2, - InputKey.Number3, - InputKey.Number4, - }, - RightKeys = new[] - { - InputKey.Z, - InputKey.X, - InputKey.C, - InputKey.V - }, - SpecialKey = InputKey.Tilde, - SpecialAction = ManiaAction.Special1, - NormalActionStart = ManiaAction.Key1 - }.GenerateKeyBindingsFor(keys, out var nextNormal); - - var stage2Bindings = new VariantMappingGenerator - { - LeftKeys = new[] - { - InputKey.Number7, - InputKey.Number8, - InputKey.Number9, - InputKey.Number0 - }, - RightKeys = new[] - { - InputKey.O, - InputKey.P, - InputKey.BracketLeft, - InputKey.BracketRight - }, - SpecialKey = InputKey.BackSlash, - SpecialAction = ManiaAction.Special2, - NormalActionStart = nextNormal - }.GenerateKeyBindingsFor(keys, out _); - - return stage1Bindings.Concat(stage2Bindings); - } - - return new KeyBinding[0]; - } - - public override string GetVariantName(int variant) - { - switch (getPlayfieldType(variant)) - { - default: - return $"{variant}K"; - case PlayfieldType.Dual: - { - var keys = getDualStageKeyCount(variant); - return $"{keys}K + {keys}K"; - } - } - } - - /// - /// Finds the number of keys for each stage in a variant. - /// - /// The variant. - private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2; - - /// - /// Finds the that corresponds to a variant value. - /// - /// The variant value. - /// The that corresponds to . - private PlayfieldType getPlayfieldType(int variant) - { - return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast().OrderByDescending(i => i).First(v => variant >= v); - } - - private class VariantMappingGenerator - { - /// - /// All the s available to the left hand. - /// - public InputKey[] LeftKeys; - - /// - /// All the s available to the right hand. - /// - public InputKey[] RightKeys; - - /// - /// The for the special key. - /// - public InputKey SpecialKey; - - /// - /// The at which the normal columns should begin. - /// - public ManiaAction NormalActionStart; - - /// - /// The for the special column. - /// - public ManiaAction SpecialAction; - - /// - /// Generates a list of s for a specific number of columns. - /// - /// The number of columns that need to be bound. - /// The next to use for normal columns. - /// The keybindings. - public IEnumerable GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction) - { - ManiaAction currentNormalAction = NormalActionStart; - - var bindings = new List(); - - for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++) - bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++)); - - for (int i = 0; i < columns / 2; i++) - bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++)); - - if (columns % 2 == 1) - bindings.Add(new KeyBinding(SpecialKey, SpecialAction)); - - nextNormalAction = currentNormalAction; - return bindings; - } - } - } - - public enum PlayfieldType - { - /// - /// Columns are grouped into a single stage. - /// Number of columns in this stage lies at (item - Single). - /// - Single = 0, - /// - /// Columns are grouped into two stages. - /// Overall number of columns lies at (item - Dual), further computation is required for - /// number of columns in each individual stage. - /// - Dual = 1000, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Replays.Types; + +namespace osu.Game.Rulesets.Mania +{ + public class ManiaRuleset : Ruleset + { + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset); + + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new ManiaModEasy(), + new ManiaModNoFail(), + new MultiMod + { + Mods = new Mod[] + { + new ManiaModHalfTime(), + new ManiaModDaycore(), + }, + }, + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new ManiaModHardRock(), + new MultiMod + { + Mods = new Mod[] + { + new ManiaModSuddenDeath(), + new ManiaModPerfect(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new ManiaModDoubleTime(), + new ManiaModNightcore(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new ManiaModFadeIn(), + new ManiaModHidden(), + } + }, + new ManiaModFlashlight(), + }; + + case ModType.Special: + return new Mod[] + { + new MultiMod + { + Mods = new Mod[] + { + new ManiaModKey4(), + new ManiaModKey5(), + new ManiaModKey6(), + new ManiaModKey7(), + new ManiaModKey8(), + new ManiaModKey9(), + new ManiaModKey1(), + new ManiaModKey2(), + new ManiaModKey3(), + }, + }, + new ManiaModRandom(), + new ManiaModDualStages(), + new ManiaModMirror(), + new MultiMod + { + Mods = new Mod[] + { + new ManiaModAutoplay(), + new ModCinema(), + }, + }, + }; + + default: + return new Mod[] { }; + } + } + + public override string Description => "osu!mania"; + + public override string ShortName => "mania"; + + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; + + public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods); + + public override int? LegacyID => 3; + + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); + + public ManiaRuleset(RulesetInfo rulesetInfo = null) + : base(rulesetInfo) + { + } + + public override IEnumerable AvailableVariants + { + get + { + for (int i = 1; i <= 9; i++) + yield return (int)PlayfieldType.Single + i; + for (int i = 2; i <= 18; i += 2) + yield return (int)PlayfieldType.Dual + i; + } + } + + public override IEnumerable GetDefaultKeyBindings(int variant = 0) + { + switch (getPlayfieldType(variant)) + { + case PlayfieldType.Single: + return new VariantMappingGenerator + { + LeftKeys = new[] + { + InputKey.A, + InputKey.S, + InputKey.D, + InputKey.F + }, + RightKeys = new[] + { + InputKey.J, + InputKey.K, + InputKey.L, + InputKey.Semicolon + }, + SpecialKey = InputKey.Space, + SpecialAction = ManiaAction.Special1, + NormalActionStart = ManiaAction.Key1, + }.GenerateKeyBindingsFor(variant, out _); + case PlayfieldType.Dual: + int keys = getDualStageKeyCount(variant); + + var stage1Bindings = new VariantMappingGenerator + { + LeftKeys = new[] + { + InputKey.Number1, + InputKey.Number2, + InputKey.Number3, + InputKey.Number4, + }, + RightKeys = new[] + { + InputKey.Z, + InputKey.X, + InputKey.C, + InputKey.V + }, + SpecialKey = InputKey.Tilde, + SpecialAction = ManiaAction.Special1, + NormalActionStart = ManiaAction.Key1 + }.GenerateKeyBindingsFor(keys, out var nextNormal); + + var stage2Bindings = new VariantMappingGenerator + { + LeftKeys = new[] + { + InputKey.Number7, + InputKey.Number8, + InputKey.Number9, + InputKey.Number0 + }, + RightKeys = new[] + { + InputKey.O, + InputKey.P, + InputKey.BracketLeft, + InputKey.BracketRight + }, + SpecialKey = InputKey.BackSlash, + SpecialAction = ManiaAction.Special2, + NormalActionStart = nextNormal + }.GenerateKeyBindingsFor(keys, out _); + + return stage1Bindings.Concat(stage2Bindings); + } + + return new KeyBinding[0]; + } + + public override string GetVariantName(int variant) + { + switch (getPlayfieldType(variant)) + { + default: + return $"{variant}K"; + case PlayfieldType.Dual: + { + var keys = getDualStageKeyCount(variant); + return $"{keys}K + {keys}K"; + } + } + } + + /// + /// Finds the number of keys for each stage in a variant. + /// + /// The variant. + private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2; + + /// + /// Finds the that corresponds to a variant value. + /// + /// The variant value. + /// The that corresponds to . + private PlayfieldType getPlayfieldType(int variant) + { + return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast().OrderByDescending(i => i).First(v => variant >= v); + } + + private class VariantMappingGenerator + { + /// + /// All the s available to the left hand. + /// + public InputKey[] LeftKeys; + + /// + /// All the s available to the right hand. + /// + public InputKey[] RightKeys; + + /// + /// The for the special key. + /// + public InputKey SpecialKey; + + /// + /// The at which the normal columns should begin. + /// + public ManiaAction NormalActionStart; + + /// + /// The for the special column. + /// + public ManiaAction SpecialAction; + + /// + /// Generates a list of s for a specific number of columns. + /// + /// The number of columns that need to be bound. + /// The next to use for normal columns. + /// The keybindings. + public IEnumerable GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction) + { + ManiaAction currentNormalAction = NormalActionStart; + + var bindings = new List(); + + for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++) + bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++)); + + for (int i = 0; i < columns / 2; i++) + bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++)); + + if (columns % 2 == 1) + bindings.Add(new KeyBinding(SpecialKey, SpecialAction)); + + nextNormalAction = currentNormalAction; + return bindings; + } + } + } + + public enum PlayfieldType + { + /// + /// Columns are grouped into a single stage. + /// Number of columns in this stage lies at (item - Single). + /// + Single = 0, + /// + /// Columns are grouped into two stages. + /// Overall number of columns lies at (item - Dual), further computation is required for + /// number of columns in each individual stage. + /// + Dual = 1000, + } +} diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs index c8277af415..a3efd5c2bd 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs @@ -1,91 +1,91 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Rulesets.Mania.MathUtils -{ - /// - /// A PRNG specified in http://heliosphan.org/fastrandom.html. - /// - internal class FastRandom - { - private const double int_to_real = 1.0 / (int.MaxValue + 1.0); - private const uint int_mask = 0x7FFFFFFF; - private const uint y = 842502087; - private const uint z = 3579807591; - private const uint w = 273326509; - private uint _x, _y = y, _z = z, _w = w; - - public FastRandom(int seed) - { - _x = (uint)seed; - } - - public FastRandom() - : this(Environment.TickCount) - { - } - - /// - /// Generates a random unsigned integer within the range [, ). - /// - /// The random value. - public uint NextUInt() - { - uint t = _x ^ _x << 11; - _x = _y; - _y = _z; - _z = _w; - return _w = _w ^ _w >> 19 ^ t ^ t >> 8; - } - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The random value. - public int Next() => (int)(int_mask & NextUInt()); - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The upper bound. - /// The random value. - public int Next(int upperBound) => (int)(NextDouble() * upperBound); - - /// - /// Generates a random integer value within the range [, ). - /// - /// The lower bound of the range. - /// The upper bound of the range. - /// The random value. - public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); - - /// - /// Generates a random double value within the range [0, 1). - /// - /// The random value. - public double NextDouble() => int_to_real * Next(); - - private uint bitBuffer; - private int bitIndex = 32; - - /// - /// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls. - /// - /// The random value. - public bool NextBool() - { - if (bitIndex == 32) - { - bitBuffer = NextUInt(); - bitIndex = 1; - - return (bitBuffer & 1) == 1; - } - - bitIndex++; - return ((bitBuffer >>= 1) & 1) == 1; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Rulesets.Mania.MathUtils +{ + /// + /// A PRNG specified in http://heliosphan.org/fastrandom.html. + /// + internal class FastRandom + { + private const double int_to_real = 1.0 / (int.MaxValue + 1.0); + private const uint int_mask = 0x7FFFFFFF; + private const uint y = 842502087; + private const uint z = 3579807591; + private const uint w = 273326509; + private uint _x, _y = y, _z = z, _w = w; + + public FastRandom(int seed) + { + _x = (uint)seed; + } + + public FastRandom() + : this(Environment.TickCount) + { + } + + /// + /// Generates a random unsigned integer within the range [, ). + /// + /// The random value. + public uint NextUInt() + { + uint t = _x ^ _x << 11; + _x = _y; + _y = _z; + _z = _w; + return _w = _w ^ _w >> 19 ^ t ^ t >> 8; + } + + /// + /// Generates a random integer value within the range [0, ). + /// + /// The random value. + public int Next() => (int)(int_mask & NextUInt()); + + /// + /// Generates a random integer value within the range [0, ). + /// + /// The upper bound. + /// The random value. + public int Next(int upperBound) => (int)(NextDouble() * upperBound); + + /// + /// Generates a random integer value within the range [, ). + /// + /// The lower bound of the range. + /// The upper bound of the range. + /// The random value. + public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); + + /// + /// Generates a random double value within the range [0, 1). + /// + /// The random value. + public double NextDouble() => int_to_real * Next(); + + private uint bitBuffer; + private int bitIndex = 32; + + /// + /// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls. + /// + /// The random value. + public bool NextBool() + { + if (bitIndex == 32) + { + bitBuffer = NextUInt(); + bitIndex = 1; + + return (bitBuffer & 1) == 1; + } + + bitIndex++; + return ((bitBuffer >>= 1) & 1) == 1; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/IPlayfieldTypeMod.cs b/osu.Game.Rulesets.Mania/Mods/IPlayfieldTypeMod.cs index 93d98b5d83..e08c9aa2a8 100644 --- a/osu.Game.Rulesets.Mania/Mods/IPlayfieldTypeMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/IPlayfieldTypeMod.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public interface IPlayfieldTypeMod : IApplicableMod - { - /// - /// The which this requires. - /// - PlayfieldType PlayfieldType { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public interface IPlayfieldTypeMod : IApplicableMod + { + /// + /// The which this requires. + /// + PlayfieldType PlayfieldType { get; } + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs index aafebb61ec..dbd30121a8 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter - { - public override string ShortenedName => Name; - public abstract int KeyCount { get; } - public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier - public override bool Ranked => true; - - public void ApplyToBeatmapConverter(BeatmapConverter beatmapConverter) - { - var mbc = (ManiaBeatmapConverter)beatmapConverter; - - // Although this can work, for now let's not allow keymods for mania-specific beatmaps - if (mbc.IsForCurrentRuleset) - return; - - mbc.TargetColumns = KeyCount; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter + { + public override string ShortenedName => Name; + public abstract int KeyCount { get; } + public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier + public override bool Ranked => true; + + public void ApplyToBeatmapConverter(BeatmapConverter beatmapConverter) + { + var mbc = (ManiaBeatmapConverter)beatmapConverter; + + // Although this can work, for now let's not allow keymods for mania-specific beatmaps + if (mbc.IsForCurrentRuleset) + return; + + mbc.TargetColumns = KeyCount; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs index 9ceb0ab7ea..89792956bb 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Replays; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; -using osu.Game.Users; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModAutoplay : ModAutoplay - { - protected override Score CreateReplayScore(Beatmap beatmap) - { - return new Score - { - User = new User { Username = "osu!topus!" }, - Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Users; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModAutoplay : ModAutoplay + { + protected override Score CreateReplayScore(Beatmap beatmap) + { + return new Score + { + User = new User { Username = "osu!topus!" }, + Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(), + }; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs index 99f49e6620..506879bb96 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModDaycore : ModDaycore - { - public override double ScoreMultiplier => 0.5; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModDaycore : ModDaycore + { + public override double ScoreMultiplier => 0.5; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs index a9d77988c8..bd93f0db38 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModDoubleTime : ModDoubleTime - { - public override double ScoreMultiplier => 1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs index a1f9e0290e..197b37b3f5 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs @@ -1,52 +1,52 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer - { - public override string Name => "Dual Stages"; - public override string ShortenedName => "DS"; - public override string Description => @"Double the stages, double the fun!"; - public override double ScoreMultiplier => 0; - - public void ApplyToBeatmapConverter(BeatmapConverter beatmapConverter) - { - var mbc = (ManiaBeatmapConverter)beatmapConverter; - - // Although this can work, for now let's not allow keymods for mania-specific beatmaps - if (mbc.IsForCurrentRuleset) - return; - - mbc.TargetColumns *= 2; - } - - public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) - { - var mrc = (ManiaRulesetContainer)rulesetContainer; - - // Although this can work, for now let's not allow keymods for mania-specific beatmaps - if (mrc.IsForCurrentRuleset) - return; - - var newDefinitions = new List(); - foreach (var existing in mrc.Beatmap.Stages) - { - newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 }); - newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 }); - } - - mrc.Beatmap.Stages = newDefinitions; - } - - public PlayfieldType PlayfieldType => PlayfieldType.Dual; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer + { + public override string Name => "Dual Stages"; + public override string ShortenedName => "DS"; + public override string Description => @"Double the stages, double the fun!"; + public override double ScoreMultiplier => 0; + + public void ApplyToBeatmapConverter(BeatmapConverter beatmapConverter) + { + var mbc = (ManiaBeatmapConverter)beatmapConverter; + + // Although this can work, for now let's not allow keymods for mania-specific beatmaps + if (mbc.IsForCurrentRuleset) + return; + + mbc.TargetColumns *= 2; + } + + public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + var mrc = (ManiaRulesetContainer)rulesetContainer; + + // Although this can work, for now let's not allow keymods for mania-specific beatmaps + if (mrc.IsForCurrentRuleset) + return; + + var newDefinitions = new List(); + foreach (var existing in mrc.Beatmap.Stages) + { + newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 }); + newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 }); + } + + mrc.Beatmap.Stages = newDefinitions; + } + + public PlayfieldType PlayfieldType => PlayfieldType.Dual; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs index 0b3e851c64..8c85846ed1 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModEasy.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModEasy : ModEasy - { - public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModEasy : ModEasy + { + public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!"; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs index ca5667a400..08815ede09 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModFadeIn : Mod - { - public override string Name => "Fade In"; - public override string ShortenedName => "FI"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; - public override ModType Type => ModType.DifficultyIncrease; - public override string Description => @"Keys appear out of nowhere!"; - public override double ScoreMultiplier => 1; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModFadeIn : Mod + { + public override string Name => "Fade In"; + public override string ShortenedName => "FI"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; + public override ModType Type => ModType.DifficultyIncrease; + public override string Description => @"Keys appear out of nowhere!"; + public override double ScoreMultiplier => 1; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs index 8d8693d11f..d7a1bc4fbe 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModFlashlight : ModFlashlight - { - public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(ModHidden) }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModFlashlight : ModFlashlight + { + public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(ModHidden) }; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs index c00bb4275a..978554362d 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModHalfTime : ModHalfTime - { - public override double ScoreMultiplier => 0.5; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModHalfTime : ModHalfTime + { + public override double ScoreMultiplier => 0.5; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs index 8b77ea4c25..7b766cab85 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHardRock.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModHardRock : ModHardRock - { - public override double ScoreMultiplier => 1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModHardRock : ModHardRock + { + public override double ScoreMultiplier => 1; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs index 9317dba19f..2ef68a35fa 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModHidden : ModHidden - { - public override string Description => @"Keys fade out before you hit them!"; - public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModHidden : ModHidden + { + public override string Description => @"Keys fade out before you hit them!"; + public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs index c0107e3758..c2a4ed444f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey1.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey1 : ManiaKeyMod - { - public override int KeyCount => 1; - public override string Name => "One Key"; - public override string ShortenedName => "1K"; - public override string Description => @"Play with one key."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey1 : ManiaKeyMod + { + public override int KeyCount => 1; + public override string Name => "One Key"; + public override string ShortenedName => "1K"; + public override string Description => @"Play with one key."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs index 11dbe0ba76..3d78ad449b 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey2.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey2 : ManiaKeyMod - { - public override int KeyCount => 2; - public override string Name => "Two Keys"; - public override string ShortenedName => "2K"; - public override string Description => @"Play with two keys."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey2 : ManiaKeyMod + { + public override int KeyCount => 2; + public override string Name => "Two Keys"; + public override string ShortenedName => "2K"; + public override string Description => @"Play with two keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs index 94ad53d8ea..a96375a81d 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey3.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey3 : ManiaKeyMod - { - public override int KeyCount => 3; - public override string Name => "Three Keys"; - public override string ShortenedName => "3K"; - public override string Description => @"Play with three keys."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey3 : ManiaKeyMod + { + public override int KeyCount => 3; + public override string Name => "Three Keys"; + public override string ShortenedName => "3K"; + public override string Description => @"Play with three keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs index d9c27c5ef1..2bd3d08648 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey4.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey4 : ManiaKeyMod - { - public override int KeyCount => 4; - public override string Name => "Four Keys"; - public override string ShortenedName => "4K"; - public override string Description => @"Play with four keys."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey4 : ManiaKeyMod + { + public override int KeyCount => 4; + public override string Name => "Four Keys"; + public override string ShortenedName => "4K"; + public override string Description => @"Play with four keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs index e54bae93a7..e59b2d6e59 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey5.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey5 : ManiaKeyMod - { - public override int KeyCount => 5; - public override string Name => "Five Keys"; - public override string ShortenedName => "5K"; - public override string Description => @"Play with five keys."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey5 : ManiaKeyMod + { + public override int KeyCount => 5; + public override string Name => "Five Keys"; + public override string ShortenedName => "5K"; + public override string Description => @"Play with five keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs index 9c3bdf46b9..6a05317f46 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey6.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey6 : ManiaKeyMod - { - public override int KeyCount => 6; - public override string Name => "Six Keys"; - public override string ShortenedName => "6K"; - public override string Description => @"Play with six keys."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey6 : ManiaKeyMod + { + public override int KeyCount => 6; + public override string Name => "Six Keys"; + public override string ShortenedName => "6K"; + public override string Description => @"Play with six keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs index f17ac80be5..7280c345b8 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey7.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey7 : ManiaKeyMod - { - public override int KeyCount => 7; - public override string Name => "Seven Keys"; - public override string ShortenedName => "7K"; - public override string Description => @"Play with seven keys."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey7 : ManiaKeyMod + { + public override int KeyCount => 7; + public override string Name => "Seven Keys"; + public override string ShortenedName => "7K"; + public override string Description => @"Play with seven keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs index 36a6fc838f..dddef0fa9d 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey8.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey8 : ManiaKeyMod - { - public override int KeyCount => 8; - public override string Name => "Eight Keys"; - public override string ShortenedName => "8K"; - public override string Description => @"Play with eight keys."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey8 : ManiaKeyMod + { + public override int KeyCount => 8; + public override string Name => "Eight Keys"; + public override string ShortenedName => "8K"; + public override string Description => @"Play with eight keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs index 10f03e2480..4ef38503a4 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKey9.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModKey9 : ManiaKeyMod - { - public override int KeyCount => 9; - public override string Name => "Nine Keys"; - public override string ShortenedName => "9K"; - public override string Description => @"Play with nine keys."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModKey9 : ManiaKeyMod + { + public override int KeyCount => 9; + public override string Name => "Nine Keys"; + public override string ShortenedName => "9K"; + public override string Description => @"Play with nine keys."; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index cfa5ef88b8..4192ec78da 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; -using System.Linq; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModMirror : Mod, IApplicableToRulesetContainer - { - public override string Name => "Mirror"; - public override string ShortenedName => "MR"; - public override ModType Type => ModType.Special; - public override double ScoreMultiplier => 1; - public override bool Ranked => true; - - public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) - { - var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; - - rulesetContainer.Objects.OfType().ForEach(h => h.Column = availableColumns - 1 - h.Column); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using System.Linq; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModMirror : Mod, IApplicableToRulesetContainer + { + public override string Name => "Mirror"; + public override string ShortenedName => "MR"; + public override ModType Type => ModType.Special; + public override double ScoreMultiplier => 1; + public override bool Ranked => true; + + public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; + + rulesetContainer.Objects.OfType().ForEach(h => h.Column = availableColumns - 1 - h.Column); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs index a007224b74..b3e73be4ee 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModNightcore : ModNightcore - { - public override double ScoreMultiplier => 1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModNightcore : ModNightcore + { + public override double ScoreMultiplier => 1; + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModNoFail.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModNoFail.cs index c9c50f9919..c22549bfc7 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModNoFail.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModNoFail.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModNoFail : ModNoFail - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModNoFail : ModNoFail + { + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModPerfect.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModPerfect.cs index 2c0bd5f8c3..abada804fb 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModPerfect.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModPerfect : ModPerfect - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModPerfect : ModPerfect + { + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs index df0f9a5437..5af898287a 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.MathUtils; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModRandom : Mod, IApplicableToRulesetContainer - { - public override string Name => "Random"; - public override string ShortenedName => "RD"; - public override FontAwesome Icon => FontAwesome.fa_osu_dice; - public override string Description => @"Shuffle around the keys!"; - public override double ScoreMultiplier => 0; - - public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) - { - var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; - var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList(); - - rulesetContainer.Objects.OfType().ForEach(h => h.Column = shuffledColumns[h.Column]); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.MathUtils; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModRandom : Mod, IApplicableToRulesetContainer + { + public override string Name => "Random"; + public override string ShortenedName => "RD"; + public override FontAwesome Icon => FontAwesome.fa_osu_dice; + public override string Description => @"Shuffle around the keys!"; + public override double ScoreMultiplier => 0; + + public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; + var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList(); + + rulesetContainer.Objects.OfType().ForEach(h => h.Column = shuffledColumns[h.Column]); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModSuddenDeath.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModSuddenDeath.cs index 9edf131195..05843b3af8 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModSuddenDeath.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModSuddenDeath.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Mods -{ - public class ManiaModSuddenDeath : ModSuddenDeath - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModSuddenDeath : ModSuddenDeath + { + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/BarLine.cs b/osu.Game.Rulesets.Mania/Objects/BarLine.cs index e5cba161a2..0ceb10cc89 100644 --- a/osu.Game.Rulesets.Mania/Objects/BarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/BarLine.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Mania.Objects -{ - public class BarLine : ManiaHitObject - { - /// - /// The control point which this bar line is part of. - /// - public TimingControlPoint ControlPoint; - - /// - /// The index of the beat which this bar line represents within the control point. - /// This is a "major" bar line if % == 0. - /// - public int BeatIndex; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Mania.Objects +{ + public class BarLine : ManiaHitObject + { + /// + /// The control point which this bar line is part of. + /// + public TimingControlPoint ControlPoint; + + /// + /// The index of the beat which this bar line represents within the control point. + /// This is a "major" bar line if % == 0. + /// + public int BeatIndex; + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index 83d67c855e..2147c5a761 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -1,74 +1,74 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables -{ - /// - /// Visualises a . Although this derives DrawableManiaHitObject, - /// this does not handle input/sound like a normal hit object. - /// - public class DrawableBarLine : DrawableManiaHitObject - { - /// - /// Height of major bar line triangles. - /// - private const float triangle_height = 12; - - /// - /// Offset of the major bar line triangles from the sides of the bar line. - /// - private const float triangle_offset = 9; - - public DrawableBarLine(BarLine barLine) - : base(barLine) - { - RelativeSizeAxes = Axes.X; - Height = 1; - - AddInternal(new Box - { - Name = "Bar line", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.Both, - }); - - bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0; - - if (isMajor) - { - AddInternal(new EquilateralTriangle - { - Name = "Left triangle", - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopCentre, - Size = new Vector2(triangle_height), - X = -triangle_offset, - Rotation = 90 - }); - - AddInternal(new EquilateralTriangle - { - Name = "Right triangle", - Anchor = Anchor.BottomRight, - Origin = Anchor.TopCentre, - Size = new Vector2(triangle_height), - X = triangle_offset, - Rotation = -90 - }); - } - - if (!isMajor && barLine.BeatIndex % 2 == 1) - Alpha = 0.2f; - } - - protected override void UpdateState(ArmedState state) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables +{ + /// + /// Visualises a . Although this derives DrawableManiaHitObject, + /// this does not handle input/sound like a normal hit object. + /// + public class DrawableBarLine : DrawableManiaHitObject + { + /// + /// Height of major bar line triangles. + /// + private const float triangle_height = 12; + + /// + /// Offset of the major bar line triangles from the sides of the bar line. + /// + private const float triangle_offset = 9; + + public DrawableBarLine(BarLine barLine) + : base(barLine) + { + RelativeSizeAxes = Axes.X; + Height = 1; + + AddInternal(new Box + { + Name = "Bar line", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + }); + + bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0; + + if (isMajor) + { + AddInternal(new EquilateralTriangle + { + Name = "Left triangle", + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopCentre, + Size = new Vector2(triangle_height), + X = -triangle_offset, + Rotation = 90 + }); + + AddInternal(new EquilateralTriangle + { + Name = "Right triangle", + Anchor = Anchor.BottomRight, + Origin = Anchor.TopCentre, + Size = new Vector2(triangle_height), + X = triangle_offset, + Rotation = -90 + }); + } + + if (!isMajor && barLine.BeatIndex % 2 == 1) + Alpha = 0.2f; + } + + protected override void UpdateState(ArmedState state) + { + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index c3d6a69a72..f8b2311a13 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -1,254 +1,254 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; -using OpenTK.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.Judgements; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables -{ - /// - /// Visualises a hit object. - /// - public class DrawableHoldNote : DrawableManiaHitObject, IKeyBindingHandler - { - private readonly DrawableNote head; - private readonly DrawableNote tail; - - private readonly GlowPiece glowPiece; - private readonly BodyPiece bodyPiece; - private readonly Container fullHeightContainer; - - /// - /// Time at which the user started holding this hold note. Null if the user is not holding this hold note. - /// - private double? holdStartTime; - - /// - /// Whether the hold note has been released too early and shouldn't give full score for the release. - /// - private bool hasBroken; - - public DrawableHoldNote(HoldNote hitObject, ManiaAction action) - : base(hitObject, action) - { - Container tickContainer; - RelativeSizeAxes = Axes.X; - - InternalChildren = new Drawable[] - { - // The hit object itself cannot be used for various elements because the tail overshoots it - // So a specialized container that is updated to contain the tail height is used - fullHeightContainer = new Container - { - RelativeSizeAxes = Axes.X, - Child = glowPiece = new GlowPiece() - }, - bodyPiece = new BodyPiece - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - }, - tickContainer = new Container - { - RelativeSizeAxes = Axes.Both, - ChildrenEnumerable = HitObject.NestedHitObjects.OfType().Select(tick => new DrawableHoldNoteTick(tick) - { - HoldStartTime = () => holdStartTime - }) - }, - head = new DrawableHeadNote(this, action) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - }, - tail = new DrawableTailNote(this, action) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - } - }; - - foreach (var tick in tickContainer) - AddNested(tick); - - AddNested(head); - AddNested(tail); - } - - public override Color4 AccentColour - { - get { return base.AccentColour; } - set - { - base.AccentColour = value; - - glowPiece.AccentColour = value; - bodyPiece.AccentColour = value; - head.AccentColour = value; - tail.AccentColour = value; - } - } - - protected override void UpdateState(ArmedState state) - { - } - - protected override void Update() - { - base.Update(); - - // Make the body piece not lie under the head note - bodyPiece.Y = head.Height; - bodyPiece.Height = DrawHeight - head.Height; - - // Make the fullHeightContainer "contain" the height of the tail note, keeping in mind - // that the tail note overshoots the height of this hit object - fullHeightContainer.Height = DrawHeight + tail.Height; - } - - public bool OnPressed(ManiaAction action) - { - // Make sure the action happened within the body of the hold note - if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime) - return false; - - if (action != Action) - return false; - - // The user has pressed during the body of the hold note, after the head note and its hit windows have passed - // and within the limited range of the above if-statement. This state will be managed by the head note if the - // user has pressed during the hit windows of the head note. - holdStartTime = Time.Current; - - return true; - } - - public bool OnReleased(ManiaAction action) - { - // Make sure that the user started holding the key during the hold note - if (!holdStartTime.HasValue) - return false; - - if (action != Action) - return false; - - holdStartTime = null; - - // 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; - } - - /// - /// The head note of a hold. - /// - private class DrawableHeadNote : DrawableNote - { - private readonly DrawableHoldNote holdNote; - - public DrawableHeadNote(DrawableHoldNote holdNote, ManiaAction action) - : base(holdNote.HitObject.Head, action) - { - this.holdNote = holdNote; - - GlowPiece.Alpha = 0; - } - - public override bool OnPressed(ManiaAction action) - { - if (!base.OnPressed(action)) - return false; - - // If the key has been released too early, the user should not receive full score for the release - if (Judgements.Any(j => j.Result == HitResult.Miss)) - holdNote.hasBroken = true; - - // The head note also handles early hits before the body, but we want accurate early hits to count as the body being held - // The body doesn't handle these early early hits, so we have to explicitly set the holding state here - holdNote.holdStartTime = Time.Current; - - return true; - } - - protected override void UpdateState(ArmedState state) - { - // The holdnote keeps scrolling through for now, so having the head disappear looks weird - } - } - - /// - /// The tail note of a hold. - /// - private class DrawableTailNote : DrawableNote - { - private readonly DrawableHoldNote holdNote; - - public DrawableTailNote(DrawableHoldNote holdNote, ManiaAction action) - : base(holdNote.HitObject.Tail, action) - { - this.holdNote = holdNote; - - GlowPiece.Alpha = 0; - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!userTriggered) - { - if (!HitObject.HitWindows.CanBeHit(timeOffset)) - { - AddJudgement(new HoldNoteTailJudgement - { - Result = HitResult.Miss, - HasBroken = holdNote.hasBroken - }); - } - - return; - } - - var result = HitObject.HitWindows.ResultFor(timeOffset); - if (result == HitResult.None) - return; - - AddJudgement(new HoldNoteTailJudgement - { - Result = result, - HasBroken = holdNote.hasBroken - }); - } - - protected override void UpdateState(ArmedState state) - { - // The holdnote keeps scrolling through, so having the tail disappear looks weird - } - - public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down - - public override bool OnReleased(ManiaAction action) - { - // Make sure that the user started holding the key during the hold note - if (!holdNote.holdStartTime.HasValue) - return false; - - if (action != Action) - return false; - - UpdateJudgement(true); - - // Handled by the hold note, which will set holding = false - return false; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; +using OpenTK.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania.Judgements; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables +{ + /// + /// Visualises a hit object. + /// + public class DrawableHoldNote : DrawableManiaHitObject, IKeyBindingHandler + { + private readonly DrawableNote head; + private readonly DrawableNote tail; + + private readonly GlowPiece glowPiece; + private readonly BodyPiece bodyPiece; + private readonly Container fullHeightContainer; + + /// + /// Time at which the user started holding this hold note. Null if the user is not holding this hold note. + /// + private double? holdStartTime; + + /// + /// Whether the hold note has been released too early and shouldn't give full score for the release. + /// + private bool hasBroken; + + public DrawableHoldNote(HoldNote hitObject, ManiaAction action) + : base(hitObject, action) + { + Container tickContainer; + RelativeSizeAxes = Axes.X; + + InternalChildren = new Drawable[] + { + // The hit object itself cannot be used for various elements because the tail overshoots it + // So a specialized container that is updated to contain the tail height is used + fullHeightContainer = new Container + { + RelativeSizeAxes = Axes.X, + Child = glowPiece = new GlowPiece() + }, + bodyPiece = new BodyPiece + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + }, + tickContainer = new Container + { + RelativeSizeAxes = Axes.Both, + ChildrenEnumerable = HitObject.NestedHitObjects.OfType().Select(tick => new DrawableHoldNoteTick(tick) + { + HoldStartTime = () => holdStartTime + }) + }, + head = new DrawableHeadNote(this, action) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + tail = new DrawableTailNote(this, action) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + } + }; + + foreach (var tick in tickContainer) + AddNested(tick); + + AddNested(head); + AddNested(tail); + } + + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + + glowPiece.AccentColour = value; + bodyPiece.AccentColour = value; + head.AccentColour = value; + tail.AccentColour = value; + } + } + + protected override void UpdateState(ArmedState state) + { + } + + protected override void Update() + { + base.Update(); + + // Make the body piece not lie under the head note + bodyPiece.Y = head.Height; + bodyPiece.Height = DrawHeight - head.Height; + + // Make the fullHeightContainer "contain" the height of the tail note, keeping in mind + // that the tail note overshoots the height of this hit object + fullHeightContainer.Height = DrawHeight + tail.Height; + } + + public bool OnPressed(ManiaAction action) + { + // Make sure the action happened within the body of the hold note + if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime) + return false; + + if (action != Action) + return false; + + // The user has pressed during the body of the hold note, after the head note and its hit windows have passed + // and within the limited range of the above if-statement. This state will be managed by the head note if the + // user has pressed during the hit windows of the head note. + holdStartTime = Time.Current; + + return true; + } + + public bool OnReleased(ManiaAction action) + { + // Make sure that the user started holding the key during the hold note + if (!holdStartTime.HasValue) + return false; + + if (action != Action) + return false; + + holdStartTime = null; + + // 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; + } + + /// + /// The head note of a hold. + /// + private class DrawableHeadNote : DrawableNote + { + private readonly DrawableHoldNote holdNote; + + public DrawableHeadNote(DrawableHoldNote holdNote, ManiaAction action) + : base(holdNote.HitObject.Head, action) + { + this.holdNote = holdNote; + + GlowPiece.Alpha = 0; + } + + public override bool OnPressed(ManiaAction action) + { + if (!base.OnPressed(action)) + return false; + + // If the key has been released too early, the user should not receive full score for the release + if (Judgements.Any(j => j.Result == HitResult.Miss)) + holdNote.hasBroken = true; + + // The head note also handles early hits before the body, but we want accurate early hits to count as the body being held + // The body doesn't handle these early early hits, so we have to explicitly set the holding state here + holdNote.holdStartTime = Time.Current; + + return true; + } + + protected override void UpdateState(ArmedState state) + { + // The holdnote keeps scrolling through for now, so having the head disappear looks weird + } + } + + /// + /// The tail note of a hold. + /// + private class DrawableTailNote : DrawableNote + { + private readonly DrawableHoldNote holdNote; + + public DrawableTailNote(DrawableHoldNote holdNote, ManiaAction action) + : base(holdNote.HitObject.Tail, action) + { + this.holdNote = holdNote; + + GlowPiece.Alpha = 0; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!userTriggered) + { + if (!HitObject.HitWindows.CanBeHit(timeOffset)) + { + AddJudgement(new HoldNoteTailJudgement + { + Result = HitResult.Miss, + HasBroken = holdNote.hasBroken + }); + } + + return; + } + + var result = HitObject.HitWindows.ResultFor(timeOffset); + if (result == HitResult.None) + return; + + AddJudgement(new HoldNoteTailJudgement + { + Result = result, + HasBroken = holdNote.hasBroken + }); + } + + protected override void UpdateState(ArmedState state) + { + // The holdnote keeps scrolling through, so having the tail disappear looks weird + } + + public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down + + public override bool OnReleased(ManiaAction action) + { + // Make sure that the user started holding the key during the hold note + if (!holdNote.holdStartTime.HasValue) + return false; + + if (action != Action) + return false; + + UpdateJudgement(true); + + // Handled by the hold note, which will set holding = false + return false; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index b50a5e897e..74c17ad0fb 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -1,111 +1,111 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables -{ - /// - /// Visualises a hit object. - /// - public class DrawableHoldNoteTick : DrawableManiaHitObject - { - /// - /// References the time at which the user started holding the hold note. - /// - public Func HoldStartTime; - - private readonly Container glowContainer; - - public DrawableHoldNoteTick(HoldNoteTick hitObject) - : base(hitObject) - { - Anchor = Anchor.TopCentre; - Origin = Anchor.TopCentre; - - RelativeSizeAxes = Axes.X; - Size = new Vector2(1); - - InternalChildren = new[] - { - glowContainer = new CircularContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - } - }; - } - - public override Color4 AccentColour - { - get { return base.AccentColour; } - set - { - base.AccentColour = value; - - glowContainer.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Radius = 2f, - Roundness = 15f, - Colour = value.Opacity(0.3f) - }; - } - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!userTriggered) - return; - - if (Time.Current < HitObject.StartTime) - return; - - if (HoldStartTime?.Invoke() > HitObject.StartTime) - return; - - AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect }); - } - - protected override void UpdateState(ArmedState state) - { - switch (State.Value) - { - case ArmedState.Hit: - AccentColour = Color4.Green; - break; - } - } - - protected override void Update() - { - if (AllJudged) - return; - - if (HoldStartTime?.Invoke() == null) - return; - - UpdateJudgement(true); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables +{ + /// + /// Visualises a hit object. + /// + public class DrawableHoldNoteTick : DrawableManiaHitObject + { + /// + /// References the time at which the user started holding the hold note. + /// + public Func HoldStartTime; + + private readonly Container glowContainer; + + public DrawableHoldNoteTick(HoldNoteTick hitObject) + : base(hitObject) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + + RelativeSizeAxes = Axes.X; + Size = new Vector2(1); + + InternalChildren = new[] + { + glowContainer = new CircularContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + } + }; + } + + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + + glowContainer.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 2f, + Roundness = 15f, + Colour = value.Opacity(0.3f) + }; + } + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!userTriggered) + return; + + if (Time.Current < HitObject.StartTime) + return; + + if (HoldStartTime?.Invoke() > HitObject.StartTime) + return; + + AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect }); + } + + protected override void UpdateState(ArmedState state) + { + switch (State.Value) + { + case ArmedState.Hit: + AccentColour = Color4.Green; + break; + } + } + + protected override void Update() + { + if (AllJudged) + return; + + if (HoldStartTime?.Invoke() == null) + return; + + UpdateJudgement(true); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 3aec8d25f9..db1fec40de 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables -{ - public abstract class DrawableManiaHitObject : DrawableHitObject - where TObject : ManiaHitObject - { - /// - /// The key that will trigger input for this hit object. - /// - protected ManiaAction Action { get; } - - public new TObject HitObject; - - protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null) - : base(hitObject) - { - Anchor = Anchor.TopCentre; - Origin = Anchor.TopCentre; - - HitObject = hitObject; - - if (action != null) - Action = action.Value; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables +{ + public abstract class DrawableManiaHitObject : DrawableHitObject + where TObject : ManiaHitObject + { + /// + /// The key that will trigger input for this hit object. + /// + protected ManiaAction Action { get; } + + public new TObject HitObject; + + protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null) + : base(hitObject) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + + HitObject = hitObject; + + if (action != null) + Action = action.Value; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index c171325fb2..0340e6bba3 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -1,95 +1,95 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables -{ - /// - /// Visualises a hit object. - /// - public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler - { - protected readonly GlowPiece GlowPiece; - - private readonly LaneGlowPiece laneGlowPiece; - private readonly NotePiece headPiece; - - public DrawableNote(Note hitObject, ManiaAction action) - : base(hitObject, action) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - InternalChildren = new Drawable[] - { - laneGlowPiece = new LaneGlowPiece - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }, - GlowPiece = new GlowPiece(), - headPiece = new NotePiece - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - } - }; - } - - public override Color4 AccentColour - { - get { return base.AccentColour; } - set - { - base.AccentColour = value; - laneGlowPiece.AccentColour = AccentColour; - GlowPiece.AccentColour = AccentColour; - headPiece.AccentColour = AccentColour; - } - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!userTriggered) - { - if (!HitObject.HitWindows.CanBeHit(timeOffset)) - AddJudgement(new ManiaJudgement { Result = HitResult.Miss }); - return; - } - - var result = HitObject.HitWindows.ResultFor(timeOffset); - if (result == HitResult.None) - return; - - AddJudgement(new ManiaJudgement { Result = result }); - } - - protected override void UpdateState(ArmedState state) - { - switch (state) - { - case ArmedState.Hit: - case ArmedState.Miss: - this.FadeOut(100).Expire(); - break; - } - } - - public virtual bool OnPressed(ManiaAction action) - { - if (action != Action) - return false; - - return UpdateJudgement(true); - } - - public virtual bool OnReleased(ManiaAction action) => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables +{ + /// + /// Visualises a hit object. + /// + public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler + { + protected readonly GlowPiece GlowPiece; + + private readonly LaneGlowPiece laneGlowPiece; + private readonly NotePiece headPiece; + + public DrawableNote(Note hitObject, ManiaAction action) + : base(hitObject, action) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChildren = new Drawable[] + { + laneGlowPiece = new LaneGlowPiece + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }, + GlowPiece = new GlowPiece(), + headPiece = new NotePiece + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + } + }; + } + + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + laneGlowPiece.AccentColour = AccentColour; + GlowPiece.AccentColour = AccentColour; + headPiece.AccentColour = AccentColour; + } + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!userTriggered) + { + if (!HitObject.HitWindows.CanBeHit(timeOffset)) + AddJudgement(new ManiaJudgement { Result = HitResult.Miss }); + return; + } + + var result = HitObject.HitWindows.ResultFor(timeOffset); + if (result == HitResult.None) + return; + + AddJudgement(new ManiaJudgement { Result = result }); + } + + protected override void UpdateState(ArmedState state) + { + switch (state) + { + case ArmedState.Hit: + case ArmedState.Miss: + this.FadeOut(100).Expire(); + break; + } + } + + public virtual bool OnPressed(ManiaAction action) + { + if (action != Action) + return false; + + return UpdateJudgement(true); + } + + public virtual bool OnReleased(ManiaAction action) => false; + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index d2e6547be4..17644a78a5 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -1,132 +1,132 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Caching; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces -{ - /// - /// Represents length-wise portion of a hold note. - /// - internal class BodyPiece : Container, IHasAccentColour - { - private readonly Container subtractionLayer; - - private readonly Drawable background; - private readonly BufferedContainer foreground; - private readonly BufferedContainer subtractionContainer; - - public BodyPiece() - { - Blending = BlendingMode.Additive; - - Children = new[] - { - background = new Box { RelativeSizeAxes = Axes.Both }, - foreground = new BufferedContainer - { - RelativeSizeAxes = Axes.Both, - CacheDrawnFrameBuffer = true, - Children = new Drawable[] - { - new Box { RelativeSizeAxes = Axes.Both }, - subtractionContainer = new BufferedContainer - { - RelativeSizeAxes = Axes.Both, - // This is needed because we're blending with another object - BackgroundColour = Color4.White.Opacity(0), - CacheDrawnFrameBuffer = true, - // The 'hole' is achieved by subtracting the result of this container with the parent - Blending = new BlendingParameters { AlphaEquation = BlendingEquation.ReverseSubtract }, - Child = subtractionLayer = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - // Height computed in Update - Width = 1, - Masking = true, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - } - } - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateAccentColour(); - } - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - if (accentColour == value) - return; - accentColour = value; - - updateAccentColour(); - } - } - - private Cached subtractionCache = new Cached(); - - public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) - { - if ((invalidation & Invalidation.DrawSize) > 0) - subtractionCache.Invalidate(); - - return base.Invalidate(invalidation, source, shallPropagate); - } - - protected override void Update() - { - base.Update(); - - if (!subtractionCache.IsValid) - { - subtractionLayer.Width = 5; - subtractionLayer.Height = Math.Max(0, DrawHeight - DrawWidth); - subtractionLayer.EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.White, - Type = EdgeEffectType.Glow, - Radius = DrawWidth - }; - - foreground.ForceRedraw(); - subtractionContainer.ForceRedraw(); - - subtractionCache.Validate(); - } - } - - private void updateAccentColour() - { - if (!IsLoaded) - return; - - foreground.Colour = AccentColour.Opacity(0.4f); - background.Colour = AccentColour.Opacity(0.2f); - - subtractionCache.Invalidate(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Caching; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces +{ + /// + /// Represents length-wise portion of a hold note. + /// + internal class BodyPiece : Container, IHasAccentColour + { + private readonly Container subtractionLayer; + + private readonly Drawable background; + private readonly BufferedContainer foreground; + private readonly BufferedContainer subtractionContainer; + + public BodyPiece() + { + Blending = BlendingMode.Additive; + + Children = new[] + { + background = new Box { RelativeSizeAxes = Axes.Both }, + foreground = new BufferedContainer + { + RelativeSizeAxes = Axes.Both, + CacheDrawnFrameBuffer = true, + Children = new Drawable[] + { + new Box { RelativeSizeAxes = Axes.Both }, + subtractionContainer = new BufferedContainer + { + RelativeSizeAxes = Axes.Both, + // This is needed because we're blending with another object + BackgroundColour = Color4.White.Opacity(0), + CacheDrawnFrameBuffer = true, + // The 'hole' is achieved by subtracting the result of this container with the parent + Blending = new BlendingParameters { AlphaEquation = BlendingEquation.ReverseSubtract }, + Child = subtractionLayer = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + // Height computed in Update + Width = 1, + Masking = true, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateAccentColour(); + } + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + if (accentColour == value) + return; + accentColour = value; + + updateAccentColour(); + } + } + + private Cached subtractionCache = new Cached(); + + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + if ((invalidation & Invalidation.DrawSize) > 0) + subtractionCache.Invalidate(); + + return base.Invalidate(invalidation, source, shallPropagate); + } + + protected override void Update() + { + base.Update(); + + if (!subtractionCache.IsValid) + { + subtractionLayer.Width = 5; + subtractionLayer.Height = Math.Max(0, DrawHeight - DrawWidth); + subtractionLayer.EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.White, + Type = EdgeEffectType.Glow, + Radius = DrawWidth + }; + + foreground.ForceRedraw(); + subtractionContainer.ForceRedraw(); + + subtractionCache.Validate(); + } + } + + private void updateAccentColour() + { + if (!IsLoaded) + return; + + foreground.Colour = AccentColour.Opacity(0.4f); + background.Colour = AccentColour.Opacity(0.2f); + + subtractionCache.Invalidate(); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs index 5f4a4a6ace..68c9ce07d4 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs @@ -1,65 +1,65 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces -{ - public class GlowPiece : CompositeDrawable, IHasAccentColour - { - private const float glow_alpha = 0.7f; - private const float glow_radius = 5; - - public GlowPiece() - { - RelativeSizeAxes = Axes.Both; - Masking = true; - - InternalChild = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - updateGlow(); - } - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - if (accentColour == value) - return; - accentColour = value; - - updateGlow(); - } - } - - private void updateGlow() - { - if (!IsLoaded) - return; - - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = AccentColour.Opacity(glow_alpha), - Radius = glow_radius, - Hollow = true - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces +{ + public class GlowPiece : CompositeDrawable, IHasAccentColour + { + private const float glow_alpha = 0.7f; + private const float glow_radius = 5; + + public GlowPiece() + { + RelativeSizeAxes = Axes.Both; + Masking = true; + + InternalChild = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateGlow(); + } + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + if (accentColour == value) + return; + accentColour = value; + + updateGlow(); + } + } + + private void updateGlow() + { + if (!IsLoaded) + return; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = AccentColour.Opacity(glow_alpha), + Radius = glow_radius, + Hollow = true + }; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs index ee83bc93d0..445abc28e2 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs @@ -1,85 +1,85 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces -{ - public class LaneGlowPiece : CompositeDrawable, IHasAccentColour - { - private const float total_height = 100; - private const float glow_height = 50; - private const float glow_alpha = 0.4f; - private const float edge_alpha = 0.3f; - - public LaneGlowPiece() - { - BypassAutoSizeAxes = Axes.Both; - RelativeSizeAxes = Axes.X; - Height = total_height; - - InternalChildren = new[] - { - new Container - { - Name = "Left edge", - RelativeSizeAxes = Axes.Y, - Width = 1, - Children = createGradient(edge_alpha) - }, - new Container - { - Name = "Right edge", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = 1, - Children = createGradient(edge_alpha) - }, - new Container - { - Name = "Glow", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Height = glow_height, - Children = createGradient(glow_alpha) - } - }; - } - - private Drawable[] createGradient(float alpha) => new Drawable[] - { - new Box - { - Name = "Top", - RelativeSizeAxes = Axes.Both, - Height = 0.5f, - Blending = BlendingMode.Additive, - Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha)) - }, - new Box - { - Name = "Bottom", - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Height = 0.5f, - Blending = BlendingMode.Additive, - Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent) - } - }; - - public Color4 AccentColour - { - get { return Colour; } - set { Colour = value; } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces +{ + public class LaneGlowPiece : CompositeDrawable, IHasAccentColour + { + private const float total_height = 100; + private const float glow_height = 50; + private const float glow_alpha = 0.4f; + private const float edge_alpha = 0.3f; + + public LaneGlowPiece() + { + BypassAutoSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; + Height = total_height; + + InternalChildren = new[] + { + new Container + { + Name = "Left edge", + RelativeSizeAxes = Axes.Y, + Width = 1, + Children = createGradient(edge_alpha) + }, + new Container + { + Name = "Right edge", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 1, + Children = createGradient(edge_alpha) + }, + new Container + { + Name = "Glow", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Height = glow_height, + Children = createGradient(glow_alpha) + } + }; + } + + private Drawable[] createGradient(float alpha) => new Drawable[] + { + new Box + { + Name = "Top", + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Blending = BlendingMode.Additive, + Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha)) + }, + new Box + { + Name = "Bottom", + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Blending = BlendingMode.Additive, + Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent) + } + }; + + public Color4 AccentColour + { + get { return Colour; } + set { Colour = value; } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs index e8a3377689..e23b24508b 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs @@ -1,59 +1,59 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces -{ - /// - /// Represents the static hit markers of notes. - /// - internal class NotePiece : Container, IHasAccentColour - { - private const float head_height = 10; - private const float head_colour_height = 6; - - private readonly Box colouredBox; - - public NotePiece() - { - RelativeSizeAxes = Axes.X; - Height = head_height; - - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both - }, - colouredBox = new Box - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - Height = head_colour_height, - Alpha = 0.2f - } - }; - } - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - if (accentColour == value) - return; - accentColour = value; - - colouredBox.Colour = AccentColour.Lighten(0.9f); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces +{ + /// + /// Represents the static hit markers of notes. + /// + internal class NotePiece : Container, IHasAccentColour + { + private const float head_height = 10; + private const float head_colour_height = 6; + + private readonly Box colouredBox; + + public NotePiece() + { + RelativeSizeAxes = Axes.X; + Height = head_height; + + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both + }, + colouredBox = new Box + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = head_colour_height, + Alpha = 0.2f + } + }; + } + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + if (accentColour == value) + return; + accentColour = value; + + colouredBox.Colour = AccentColour.Lighten(0.9f); + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 728c79b9cf..4cf22ccd39 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -1,117 +1,117 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Mania.Objects -{ - /// - /// Represents a hit object which requires pressing, holding, and releasing a key. - /// - public class HoldNote : ManiaHitObject, IHasEndTime - { - public double EndTime => StartTime + Duration; - - private double duration; - public double Duration - { - get { return duration; } - set - { - duration = value; - Tail.StartTime = EndTime; - } - } - - public override double StartTime - { - get { return base.StartTime; } - set - { - base.StartTime = value; - Head.StartTime = value; - Tail.StartTime = EndTime; - } - } - - public override int Column - { - get { return base.Column; } - set - { - base.Column = value; - Head.Column = value; - Tail.Column = value; - } - } - - /// - /// The head note of the hold. - /// - public readonly Note Head = new Note(); - - /// - /// The tail note of the hold. - /// - public readonly Note Tail = new TailNote(); - - /// - /// The time between ticks of this hold. - /// - private double tickSpacing = 50; - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate; - - Head.ApplyDefaults(controlPointInfo, difficulty); - Tail.ApplyDefaults(controlPointInfo, difficulty); - } - - protected override void CreateNestedHitObjects() - { - base.CreateNestedHitObjects(); - - createTicks(); - } - - private void createTicks() - { - if (tickSpacing == 0) - return; - - for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing) - { - AddNested(new HoldNoteTick - { - StartTime = t, - Column = Column - }); - } - } - - /// - /// The tail of the hold note. - /// - private class TailNote : Note - { - /// - /// Lenience of release hit windows. This is to make cases where the hold note release - /// is timed alongside presses of other hit objects less awkward. - /// - private const double release_window_lenience = 1.5; - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - HitWindows *= release_window_lenience; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Mania.Objects +{ + /// + /// Represents a hit object which requires pressing, holding, and releasing a key. + /// + public class HoldNote : ManiaHitObject, IHasEndTime + { + public double EndTime => StartTime + Duration; + + private double duration; + public double Duration + { + get { return duration; } + set + { + duration = value; + Tail.StartTime = EndTime; + } + } + + public override double StartTime + { + get { return base.StartTime; } + set + { + base.StartTime = value; + Head.StartTime = value; + Tail.StartTime = EndTime; + } + } + + public override int Column + { + get { return base.Column; } + set + { + base.Column = value; + Head.Column = value; + Tail.Column = value; + } + } + + /// + /// The head note of the hold. + /// + public readonly Note Head = new Note(); + + /// + /// The tail note of the hold. + /// + public readonly Note Tail = new TailNote(); + + /// + /// The time between ticks of this hold. + /// + private double tickSpacing = 50; + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate; + + Head.ApplyDefaults(controlPointInfo, difficulty); + Tail.ApplyDefaults(controlPointInfo, difficulty); + } + + protected override void CreateNestedHitObjects() + { + base.CreateNestedHitObjects(); + + createTicks(); + } + + private void createTicks() + { + if (tickSpacing == 0) + return; + + for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing) + { + AddNested(new HoldNoteTick + { + StartTime = t, + Column = Column + }); + } + } + + /// + /// The tail of the hold note. + /// + private class TailNote : Note + { + /// + /// Lenience of release hit windows. This is to make cases where the hold note release + /// is timed alongside presses of other hit objects less awkward. + /// + private const double release_window_lenience = 1.5; + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + HitWindows *= release_window_lenience; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs index ec502a803d..d078c15c92 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Objects -{ - /// - /// A scoring tick of a hold note. - /// - public class HoldNoteTick : ManiaHitObject - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Objects +{ + /// + /// A scoring tick of a hold note. + /// + public class HoldNoteTick : ManiaHitObject + { + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs index be93471bcd..4f0e02ff0d 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs @@ -1,23 +1,23 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mania.Objects.Types; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Mania.Objects -{ - public abstract class ManiaHitObject : HitObject, IHasColumn - { - public virtual int Column { get; set; } - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - HitWindows.AllowsPerfect = true; - HitWindows.AllowsOk = true; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.Objects.Types; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Mania.Objects +{ + public abstract class ManiaHitObject : HitObject, IHasColumn + { + public virtual int Column { get; set; } + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + HitWindows.AllowsPerfect = true; + HitWindows.AllowsOk = true; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs index 2b59279972..390d2b0218 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs @@ -1,113 +1,113 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using System; - -namespace osu.Game.Rulesets.Mania.Objects -{ - internal class ManiaHitObjectDifficulty - { - /// - /// Factor by how much individual / overall strain decays per second. - /// - /// - /// These values are results of tweaking a lot and taking into account general feedback. - /// - internal const double INDIVIDUAL_DECAY_BASE = 0.125; - internal const double OVERALL_DECAY_BASE = 0.30; - - internal ManiaHitObject BaseHitObject; - - private readonly int beatmapColumnCount; - - - private readonly double endTime; - private readonly double[] heldUntil; - - /// - /// Measures jacks or more generally: repeated presses of the same button - /// - private readonly double[] individualStrains; - - internal double IndividualStrain - { - get - { - return individualStrains[BaseHitObject.Column]; - } - - set - { - individualStrains[BaseHitObject.Column] = value; - } - } - - /// - /// Measures note density in a way - /// - internal double OverallStrain = 1; - - public ManiaHitObjectDifficulty(ManiaHitObject baseHitObject, int columnCount) - { - BaseHitObject = baseHitObject; - - endTime = (baseHitObject as IHasEndTime)?.EndTime ?? baseHitObject.StartTime; - - beatmapColumnCount = columnCount; - heldUntil = new double[beatmapColumnCount]; - individualStrains = new double[beatmapColumnCount]; - - for (int i = 0; i < beatmapColumnCount; ++i) - { - individualStrains[i] = 0; - heldUntil[i] = 0; - } - } - - internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate) - { - // TODO: Factor in holds - double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; - double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000); - double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000); - - double holdFactor = 1.0; // Factor to all additional strains in case something else is held - double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly - - // Fill up the heldUntil array - for (int i = 0; i < beatmapColumnCount; ++i) - { - heldUntil[i] = previousHitObject.heldUntil[i]; - - // If there is at least one other overlapping end or note, then we get an addition, buuuuuut... - if (BaseHitObject.StartTime < heldUntil[i] && endTime > heldUntil[i]) - { - holdAddition = 1.0; - } - - // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 - if (endTime == heldUntil[i]) - { - holdAddition = 0; - } - - // We give a slight bonus to everything if something is held meanwhile - if (heldUntil[i] > endTime) - { - holdFactor = 1.25; - } - - // Decay individual strains - individualStrains[i] = previousHitObject.individualStrains[i] * individualDecay; - } - - heldUntil[BaseHitObject.Column] = endTime; - - // Increase individual strain in own column - IndividualStrain += 2.0 * holdFactor; - - OverallStrain = previousHitObject.OverallStrain * overallDecay + (1.0 + holdAddition) * holdFactor; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using System; + +namespace osu.Game.Rulesets.Mania.Objects +{ + internal class ManiaHitObjectDifficulty + { + /// + /// Factor by how much individual / overall strain decays per second. + /// + /// + /// These values are results of tweaking a lot and taking into account general feedback. + /// + internal const double INDIVIDUAL_DECAY_BASE = 0.125; + internal const double OVERALL_DECAY_BASE = 0.30; + + internal ManiaHitObject BaseHitObject; + + private readonly int beatmapColumnCount; + + + private readonly double endTime; + private readonly double[] heldUntil; + + /// + /// Measures jacks or more generally: repeated presses of the same button + /// + private readonly double[] individualStrains; + + internal double IndividualStrain + { + get + { + return individualStrains[BaseHitObject.Column]; + } + + set + { + individualStrains[BaseHitObject.Column] = value; + } + } + + /// + /// Measures note density in a way + /// + internal double OverallStrain = 1; + + public ManiaHitObjectDifficulty(ManiaHitObject baseHitObject, int columnCount) + { + BaseHitObject = baseHitObject; + + endTime = (baseHitObject as IHasEndTime)?.EndTime ?? baseHitObject.StartTime; + + beatmapColumnCount = columnCount; + heldUntil = new double[beatmapColumnCount]; + individualStrains = new double[beatmapColumnCount]; + + for (int i = 0; i < beatmapColumnCount; ++i) + { + individualStrains[i] = 0; + heldUntil[i] = 0; + } + } + + internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate) + { + // TODO: Factor in holds + double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; + double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000); + double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000); + + double holdFactor = 1.0; // Factor to all additional strains in case something else is held + double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly + + // Fill up the heldUntil array + for (int i = 0; i < beatmapColumnCount; ++i) + { + heldUntil[i] = previousHitObject.heldUntil[i]; + + // If there is at least one other overlapping end or note, then we get an addition, buuuuuut... + if (BaseHitObject.StartTime < heldUntil[i] && endTime > heldUntil[i]) + { + holdAddition = 1.0; + } + + // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 + if (endTime == heldUntil[i]) + { + holdAddition = 0; + } + + // We give a slight bonus to everything if something is held meanwhile + if (heldUntil[i] > endTime) + { + holdFactor = 1.25; + } + + // Decay individual strains + individualStrains[i] = previousHitObject.individualStrains[i] * individualDecay; + } + + heldUntil[BaseHitObject.Column] = endTime; + + // Increase individual strain in own column + IndividualStrain += 2.0 * holdFactor; + + OverallStrain = previousHitObject.OverallStrain * overallDecay + (1.0 + holdAddition) * holdFactor; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs index 438116b363..975188e550 100644 --- a/osu.Game.Rulesets.Mania/Objects/Note.cs +++ b/osu.Game.Rulesets.Mania/Objects/Note.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Objects -{ - /// - /// Represents a hit object which has a single hit press. - /// - public class Note : ManiaHitObject - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Objects +{ + /// + /// Represents a hit object which has a single hit press. + /// + public class Note : ManiaHitObject + { + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Types/IHasColumn.cs b/osu.Game.Rulesets.Mania/Objects/Types/IHasColumn.cs index cdff2b0827..44a79de7db 100644 --- a/osu.Game.Rulesets.Mania/Objects/Types/IHasColumn.cs +++ b/osu.Game.Rulesets.Mania/Objects/Types/IHasColumn.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Objects.Types -{ - /// - /// A type of hit object which lies in one of a number of predetermined columns. - /// - public interface IHasColumn - { - /// - /// The column which the hit object lies in. - /// - int Column { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Objects.Types +{ + /// + /// A type of hit object which lies in one of a number of predetermined columns. + /// + public interface IHasColumn + { + /// + /// The column which the hit object lies in. + /// + int Column { get; } + } +} diff --git a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs index 515aeab9df..76ccfe324b 100644 --- a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Runtime.CompilerServices; - -// We publish our internal attributes to other sub-projects of the framework. -// Note, that we omit visual tests as they are meant to test the framework -// behavior "in the wild". - -[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")] -[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Dynamic")] +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Runtime.CompilerServices; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 5a992bb970..d006a3e1c7 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -1,98 +1,98 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Replays; -using osu.Game.Users; - -namespace osu.Game.Rulesets.Mania.Replays -{ - internal class ManiaAutoGenerator : AutoGenerator - { - public const double RELEASE_DELAY = 20; - - public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap; - - private readonly ManiaAction[] columnActions; - - public ManiaAutoGenerator(ManiaBeatmap beatmap) - : base(beatmap) - { - Replay = new Replay { User = new User { Username = @"Autoplay" } }; - - columnActions = new ManiaAction[Beatmap.TotalColumns]; - - var normalAction = ManiaAction.Key1; - var specialAction = ManiaAction.Special1; - int totalCounter = 0; - foreach (var stage in Beatmap.Stages) - { - for (int i = 0; i < stage.Columns; i++) - { - if (stage.IsSpecialColumn(i)) - columnActions[totalCounter] = specialAction++; - else - columnActions[totalCounter] = normalAction++; - totalCounter++; - } - } - } - - protected Replay Replay; - - public override Replay Generate() - { - // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled - Replay.Frames.Add(new ManiaReplayFrame(-100000, 0)); - - var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); - - var actions = new List(); - foreach (var group in pointGroups) - { - foreach (var point in group) - { - if (point is HitPoint) - actions.Add(columnActions[point.Column]); - if (point is ReleasePoint) - actions.Remove(columnActions[point.Column]); - } - - Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray())); - } - - return Replay; - } - - private IEnumerable generateActionPoints() - { - foreach (var obj in Beatmap.HitObjects) - { - yield return new HitPoint { Time = obj.StartTime, Column = obj.Column }; - yield return new ReleasePoint { Time = ((obj as IHasEndTime)?.EndTime ?? obj.StartTime) + RELEASE_DELAY, Column = obj.Column }; - } - } - - private interface IActionPoint - { - double Time { get; set; } - int Column { get; set; } - } - - private struct HitPoint : IActionPoint - { - public double Time { get; set; } - public int Column { get; set; } - } - - private struct ReleasePoint : IActionPoint - { - public double Time { get; set; } - public int Column { get; set; } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Replays; +using osu.Game.Users; + +namespace osu.Game.Rulesets.Mania.Replays +{ + internal class ManiaAutoGenerator : AutoGenerator + { + public const double RELEASE_DELAY = 20; + + public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap; + + private readonly ManiaAction[] columnActions; + + public ManiaAutoGenerator(ManiaBeatmap beatmap) + : base(beatmap) + { + Replay = new Replay { User = new User { Username = @"Autoplay" } }; + + columnActions = new ManiaAction[Beatmap.TotalColumns]; + + var normalAction = ManiaAction.Key1; + var specialAction = ManiaAction.Special1; + int totalCounter = 0; + foreach (var stage in Beatmap.Stages) + { + for (int i = 0; i < stage.Columns; i++) + { + if (stage.IsSpecialColumn(i)) + columnActions[totalCounter] = specialAction++; + else + columnActions[totalCounter] = normalAction++; + totalCounter++; + } + } + } + + protected Replay Replay; + + public override Replay Generate() + { + // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled + Replay.Frames.Add(new ManiaReplayFrame(-100000, 0)); + + var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); + + var actions = new List(); + foreach (var group in pointGroups) + { + foreach (var point in group) + { + if (point is HitPoint) + actions.Add(columnActions[point.Column]); + if (point is ReleasePoint) + actions.Remove(columnActions[point.Column]); + } + + Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray())); + } + + return Replay; + } + + private IEnumerable generateActionPoints() + { + foreach (var obj in Beatmap.HitObjects) + { + yield return new HitPoint { Time = obj.StartTime, Column = obj.Column }; + yield return new ReleasePoint { Time = ((obj as IHasEndTime)?.EndTime ?? obj.StartTime) + RELEASE_DELAY, Column = obj.Column }; + } + } + + private interface IActionPoint + { + double Time { get; set; } + int Column { get; set; } + } + + private struct HitPoint : IActionPoint + { + public double Time { get; set; } + public int Column { get; set; } + } + + private struct ReleasePoint : IActionPoint + { + public double Time { get; set; } + public int Column { get; set; } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs index 3541561418..c71db745e0 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Input; -using osu.Game.Rulesets.Replays; - -namespace osu.Game.Rulesets.Mania.Replays -{ - internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler - { - public ManiaFramedReplayInputHandler(Replay replay) - : base(replay) - { - } - - protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any(); - - public override List GetPendingStates() => new List { new ReplayState { PressedActions = CurrentFrame.Actions } }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Input; +using osu.Game.Rulesets.Replays; + +namespace osu.Game.Rulesets.Mania.Replays +{ + internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler + { + public ManiaFramedReplayInputHandler(Replay replay) + : base(replay) + { + } + + protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any(); + + public override List GetPendingStates() => new List { new ReplayState { PressedActions = CurrentFrame.Actions } }; + } +} diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 9990f89b99..8d86325dd9 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -1,59 +1,59 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Replays.Legacy; -using osu.Game.Rulesets.Replays.Types; - -namespace osu.Game.Rulesets.Mania.Replays -{ - public class ManiaReplayFrame : ReplayFrame, IConvertibleReplayFrame - { - public List Actions = new List(); - - public ManiaReplayFrame() - { - } - - public ManiaReplayFrame(double time, params ManiaAction[] actions) - : base(time) - { - Actions.AddRange(actions); - } - - public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) - { - // We don't need to fully convert, just create the converter - var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.RulesetID == 3, beatmap); - - // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling - // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage. - - var stage = new StageDefinition { Columns = converter.TargetColumns }; - - var normalAction = ManiaAction.Key1; - var specialAction = ManiaAction.Special1; - - int activeColumns = (int)(legacyFrame.MouseX ?? 0); - int counter = 0; - while (activeColumns > 0) - { - var isSpecial = stage.IsSpecialColumn(counter); - - if ((activeColumns & 1) > 0) - Actions.Add(isSpecial ? specialAction : normalAction); - - if (isSpecial) - specialAction++; - else - normalAction++; - - counter++; - activeColumns >>= 1; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; + +namespace osu.Game.Rulesets.Mania.Replays +{ + public class ManiaReplayFrame : ReplayFrame, IConvertibleReplayFrame + { + public List Actions = new List(); + + public ManiaReplayFrame() + { + } + + public ManiaReplayFrame(double time, params ManiaAction[] actions) + : base(time) + { + Actions.AddRange(actions); + } + + public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) + { + // We don't need to fully convert, just create the converter + var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.RulesetID == 3, beatmap); + + // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling + // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage. + + var stage = new StageDefinition { Columns = converter.TargetColumns }; + + var normalAction = ManiaAction.Key1; + var specialAction = ManiaAction.Special1; + + int activeColumns = (int)(legacyFrame.MouseX ?? 0); + int counter = 0; + while (activeColumns > 0) + { + var isSpecial = stage.IsSpecialColumn(counter); + + if ((activeColumns & 1) > 0) + Actions.Add(isSpecial ? specialAction : normalAction); + + if (isSpecial) + specialAction++; + else + normalAction++; + + counter++; + activeColumns >>= 1; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index c3b840b9f0..4c955a680e 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -1,173 +1,173 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Mania.Scoring -{ - internal class ManiaScoreProcessor : ScoreProcessor - { - /// - /// The hit HP multiplier at OD = 0. - /// - private const double hp_multiplier_min = 0.75; - - /// - /// The hit HP multiplier at OD = 0. - /// - private const double hp_multiplier_mid = 0.85; - - /// - /// The hit HP multiplier at OD = 0. - /// - private const double hp_multiplier_max = 1; - - /// - /// The default BAD hit HP increase. - /// - private const double hp_increase_bad = 0.005; - - /// - /// The default OK hit HP increase. - /// - private const double hp_increase_ok = 0.010; - - /// - /// The default GOOD hit HP increase. - /// - private const double hp_increase_good = 0.035; - - /// - /// The default tick hit HP increase. - /// - private const double hp_increase_tick = 0.040; - - /// - /// The default GREAT hit HP increase. - /// - private const double hp_increase_great = 0.055; - - /// - /// The default PERFECT hit HP increase. - /// - private const double hp_increase_perfect = 0.065; - - /// - /// The MISS HP multiplier at OD = 0. - /// - private const double hp_multiplier_miss_min = 0.5; - - /// - /// The MISS HP multiplier at OD = 5. - /// - private const double hp_multiplier_miss_mid = 0.75; - - /// - /// The MISS HP multiplier at OD = 10. - /// - private const double hp_multiplier_miss_max = 1; - - /// - /// The default MISS HP increase. - /// - private const double hp_increase_miss = -0.125; - - /// - /// The MISS HP multiplier. This is multiplied to the miss hp increase. - /// - private double hpMissMultiplier = 1; - - /// - /// The HIT HP multiplier. This is multiplied to hit hp increases. - /// - private double hpMultiplier = 1; - - public ManiaScoreProcessor() - { - } - - public ManiaScoreProcessor(RulesetContainer rulesetContainer) - : base(rulesetContainer) - { - } - - protected override void SimulateAutoplay(Beatmap beatmap) - { - BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty; - hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max); - hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max); - - while (true) - { - foreach (var obj in beatmap.HitObjects) - { - var holdNote = obj as HoldNote; - - if (holdNote != null) - { - // Head - AddJudgement(new ManiaJudgement { Result = HitResult.Perfect }); - - // Ticks - int tickCount = holdNote.NestedHitObjects.OfType().Count(); - for (int i = 0; i < tickCount; i++) - AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect }); - } - - AddJudgement(new ManiaJudgement { Result = HitResult.Perfect }); - } - - if (!HasFailed) - break; - - hpMultiplier *= 1.01; - hpMissMultiplier *= 0.98; - - Reset(false); - } - } - - protected override void OnNewJudgement(Judgement judgement) - { - base.OnNewJudgement(judgement); - - bool isTick = judgement is HoldNoteTickJudgement; - - if (isTick) - { - if (judgement.IsHit) - Health.Value += hpMultiplier * hp_increase_tick; - } - else - { - switch (judgement.Result) - { - case HitResult.Miss: - Health.Value += hpMissMultiplier * hp_increase_miss; - break; - case HitResult.Meh: - Health.Value += hpMultiplier * hp_increase_bad; - break; - case HitResult.Ok: - Health.Value += hpMultiplier * hp_increase_ok; - break; - case HitResult.Good: - Health.Value += hpMultiplier * hp_increase_good; - break; - case HitResult.Great: - Health.Value += hpMultiplier * hp_increase_great; - break; - case HitResult.Perfect: - Health.Value += hpMultiplier * hp_increase_perfect; - break; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mania.Scoring +{ + internal class ManiaScoreProcessor : ScoreProcessor + { + /// + /// The hit HP multiplier at OD = 0. + /// + private const double hp_multiplier_min = 0.75; + + /// + /// The hit HP multiplier at OD = 0. + /// + private const double hp_multiplier_mid = 0.85; + + /// + /// The hit HP multiplier at OD = 0. + /// + private const double hp_multiplier_max = 1; + + /// + /// The default BAD hit HP increase. + /// + private const double hp_increase_bad = 0.005; + + /// + /// The default OK hit HP increase. + /// + private const double hp_increase_ok = 0.010; + + /// + /// The default GOOD hit HP increase. + /// + private const double hp_increase_good = 0.035; + + /// + /// The default tick hit HP increase. + /// + private const double hp_increase_tick = 0.040; + + /// + /// The default GREAT hit HP increase. + /// + private const double hp_increase_great = 0.055; + + /// + /// The default PERFECT hit HP increase. + /// + private const double hp_increase_perfect = 0.065; + + /// + /// The MISS HP multiplier at OD = 0. + /// + private const double hp_multiplier_miss_min = 0.5; + + /// + /// The MISS HP multiplier at OD = 5. + /// + private const double hp_multiplier_miss_mid = 0.75; + + /// + /// The MISS HP multiplier at OD = 10. + /// + private const double hp_multiplier_miss_max = 1; + + /// + /// The default MISS HP increase. + /// + private const double hp_increase_miss = -0.125; + + /// + /// The MISS HP multiplier. This is multiplied to the miss hp increase. + /// + private double hpMissMultiplier = 1; + + /// + /// The HIT HP multiplier. This is multiplied to hit hp increases. + /// + private double hpMultiplier = 1; + + public ManiaScoreProcessor() + { + } + + public ManiaScoreProcessor(RulesetContainer rulesetContainer) + : base(rulesetContainer) + { + } + + protected override void SimulateAutoplay(Beatmap beatmap) + { + BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty; + hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max); + hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max); + + while (true) + { + foreach (var obj in beatmap.HitObjects) + { + var holdNote = obj as HoldNote; + + if (holdNote != null) + { + // Head + AddJudgement(new ManiaJudgement { Result = HitResult.Perfect }); + + // Ticks + int tickCount = holdNote.NestedHitObjects.OfType().Count(); + for (int i = 0; i < tickCount; i++) + AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect }); + } + + AddJudgement(new ManiaJudgement { Result = HitResult.Perfect }); + } + + if (!HasFailed) + break; + + hpMultiplier *= 1.01; + hpMissMultiplier *= 0.98; + + Reset(false); + } + } + + protected override void OnNewJudgement(Judgement judgement) + { + base.OnNewJudgement(judgement); + + bool isTick = judgement is HoldNoteTickJudgement; + + if (isTick) + { + if (judgement.IsHit) + Health.Value += hpMultiplier * hp_increase_tick; + } + else + { + switch (judgement.Result) + { + case HitResult.Miss: + Health.Value += hpMissMultiplier * hp_increase_miss; + break; + case HitResult.Meh: + Health.Value += hpMultiplier * hp_increase_bad; + break; + case HitResult.Ok: + Health.Value += hpMultiplier * hp_increase_ok; + break; + case HitResult.Good: + Health.Value += hpMultiplier * hp_increase_good; + break; + case HitResult.Great: + Health.Value += hpMultiplier * hp_increase_great; + break; + case HitResult.Perfect: + Health.Value += hpMultiplier * hp_increase_perfect; + break; + } + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 15c9a83b78..52d514221d 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -1,276 +1,276 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Colour; -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using System; -using System.Linq; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.UI.Scrolling; - -namespace osu.Game.Rulesets.Mania.UI -{ - public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour - { - private const float key_icon_size = 10; - private const float key_icon_corner_radius = 3; - private const float key_icon_border_radius = 2; - - private const float hit_target_height = 10; - private const float hit_target_bar_height = 2; - - private const float column_width = 45; - private const float special_column_width = 70; - - public ManiaAction Action; - - private readonly Box background; - private readonly Container hitTargetBar; - private readonly Container keyIcon; - - internal readonly Container TopLevelContainer; - private readonly Container explosionContainer; - - protected override Container Content => content; - private readonly Container content; - - private const float opacity_released = 0.1f; - private const float opacity_pressed = 0.25f; - - public Column() - : base(ScrollingDirection.Up) - { - RelativeSizeAxes = Axes.Y; - Width = column_width; - - InternalChildren = new Drawable[] - { - background = new Box - { - Name = "Background", - RelativeSizeAxes = Axes.Both, - Alpha = opacity_released - }, - new Container - { - Name = "Hit target + hit objects", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = ManiaStage.HIT_TARGET_POSITION }, - Children = new Drawable[] - { - new Container - { - Name = "Hit target", - RelativeSizeAxes = Axes.X, - Height = hit_target_height, - Children = new Drawable[] - { - new Box - { - Name = "Background", - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black - }, - hitTargetBar = new Container - { - Name = "Bar", - RelativeSizeAxes = Axes.X, - Height = hit_target_bar_height, - Masking = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both - } - } - } - } - }, - content = new Container - { - Name = "Hit objects", - RelativeSizeAxes = Axes.Both, - }, - // For column lighting, we need to capture input events before the notes - new InputTarget - { - Pressed = onPressed, - Released = onReleased - }, - explosionContainer = new Container - { - Name = "Hit explosions", - RelativeSizeAxes = Axes.Both - } - } - }, - new Container - { - Name = "Key", - RelativeSizeAxes = Axes.X, - Height = ManiaStage.HIT_TARGET_POSITION, - Children = new Drawable[] - { - new Box - { - Name = "Key gradient", - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)), - Alpha = 0.5f - }, - keyIcon = new Container - { - Name = "Key icon", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(key_icon_size), - Masking = true, - CornerRadius = key_icon_corner_radius, - BorderThickness = 2, - BorderColour = Color4.White, // Not true - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - } - } - }, - TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both } - }; - - TopLevelContainer.Add(explosionContainer.CreateProxy()); - } - - public override Axes RelativeSizeAxes => Axes.Y; - - private bool isSpecial; - public bool IsSpecial - { - get { return isSpecial; } - set - { - if (isSpecial == value) - return; - isSpecial = value; - - Width = isSpecial ? special_column_width : column_width; - } - } - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - if (accentColour == value) - return; - accentColour = value; - - background.Colour = accentColour; - - hitTargetBar.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Radius = 5, - Colour = accentColour.Opacity(0.5f), - }; - - keyIcon.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Radius = 5, - Colour = accentColour.Opacity(0.5f), - }; - } - } - - /// - /// Adds a DrawableHitObject to this Playfield. - /// - /// The DrawableHitObject to add. - public override void Add(DrawableHitObject hitObject) - { - hitObject.AccentColour = AccentColour; - hitObject.OnJudgement += OnJudgement; - - HitObjects.Add(hitObject); - } - - internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) - { - if (!judgement.IsHit) - return; - - explosionContainer.Add(new HitExplosion(judgedObject)); - } - - private bool onPressed(ManiaAction action) - { - if (action == Action) - { - background.FadeTo(opacity_pressed, 50, Easing.OutQuint); - keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint); - } - - return false; - } - - private bool onReleased(ManiaAction action) - { - if (action == Action) - { - background.FadeTo(opacity_released, 800, Easing.OutQuart); - keyIcon.ScaleTo(1f, 400, Easing.OutQuart); - } - - return false; - } - - /// - /// This is a simple container which delegates various input events that have to be captured before the notes. - /// - private class InputTarget : Container, IKeyBindingHandler - { - public Func Pressed; - public Func Released; - - public InputTarget() - { - RelativeSizeAxes = Axes.Both; - AlwaysPresent = true; - Alpha = 0; - } - - public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false; - public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false; - } - - public bool OnPressed(ManiaAction action) - { - if (action != Action) - return false; - - var hitObject = HitObjects.Objects.LastOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.FirstOrDefault(); - hitObject?.PlaySamples(); - - return true; - } - - public bool OnReleased(ManiaAction action) => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Colour; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using System; +using System.Linq; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Rulesets.Mania.UI +{ + public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour + { + private const float key_icon_size = 10; + private const float key_icon_corner_radius = 3; + private const float key_icon_border_radius = 2; + + private const float hit_target_height = 10; + private const float hit_target_bar_height = 2; + + private const float column_width = 45; + private const float special_column_width = 70; + + public ManiaAction Action; + + private readonly Box background; + private readonly Container hitTargetBar; + private readonly Container keyIcon; + + internal readonly Container TopLevelContainer; + private readonly Container explosionContainer; + + protected override Container Content => content; + private readonly Container content; + + private const float opacity_released = 0.1f; + private const float opacity_pressed = 0.25f; + + public Column() + : base(ScrollingDirection.Up) + { + RelativeSizeAxes = Axes.Y; + Width = column_width; + + InternalChildren = new Drawable[] + { + background = new Box + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + Alpha = opacity_released + }, + new Container + { + Name = "Hit target + hit objects", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = ManiaStage.HIT_TARGET_POSITION }, + Children = new Drawable[] + { + new Container + { + Name = "Hit target", + RelativeSizeAxes = Axes.X, + Height = hit_target_height, + Children = new Drawable[] + { + new Box + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + hitTargetBar = new Container + { + Name = "Bar", + RelativeSizeAxes = Axes.X, + Height = hit_target_bar_height, + Masking = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both + } + } + } + } + }, + content = new Container + { + Name = "Hit objects", + RelativeSizeAxes = Axes.Both, + }, + // For column lighting, we need to capture input events before the notes + new InputTarget + { + Pressed = onPressed, + Released = onReleased + }, + explosionContainer = new Container + { + Name = "Hit explosions", + RelativeSizeAxes = Axes.Both + } + } + }, + new Container + { + Name = "Key", + RelativeSizeAxes = Axes.X, + Height = ManiaStage.HIT_TARGET_POSITION, + Children = new Drawable[] + { + new Box + { + Name = "Key gradient", + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)), + Alpha = 0.5f + }, + keyIcon = new Container + { + Name = "Key icon", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(key_icon_size), + Masking = true, + CornerRadius = key_icon_corner_radius, + BorderThickness = 2, + BorderColour = Color4.White, // Not true + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + } + } + }, + TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both } + }; + + TopLevelContainer.Add(explosionContainer.CreateProxy()); + } + + public override Axes RelativeSizeAxes => Axes.Y; + + private bool isSpecial; + public bool IsSpecial + { + get { return isSpecial; } + set + { + if (isSpecial == value) + return; + isSpecial = value; + + Width = isSpecial ? special_column_width : column_width; + } + } + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + if (accentColour == value) + return; + accentColour = value; + + background.Colour = accentColour; + + hitTargetBar.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 5, + Colour = accentColour.Opacity(0.5f), + }; + + keyIcon.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 5, + Colour = accentColour.Opacity(0.5f), + }; + } + } + + /// + /// Adds a DrawableHitObject to this Playfield. + /// + /// The DrawableHitObject to add. + public override void Add(DrawableHitObject hitObject) + { + hitObject.AccentColour = AccentColour; + hitObject.OnJudgement += OnJudgement; + + HitObjects.Add(hitObject); + } + + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + { + if (!judgement.IsHit) + return; + + explosionContainer.Add(new HitExplosion(judgedObject)); + } + + private bool onPressed(ManiaAction action) + { + if (action == Action) + { + background.FadeTo(opacity_pressed, 50, Easing.OutQuint); + keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint); + } + + return false; + } + + private bool onReleased(ManiaAction action) + { + if (action == Action) + { + background.FadeTo(opacity_released, 800, Easing.OutQuart); + keyIcon.ScaleTo(1f, 400, Easing.OutQuart); + } + + return false; + } + + /// + /// This is a simple container which delegates various input events that have to be captured before the notes. + /// + private class InputTarget : Container, IKeyBindingHandler + { + public Func Pressed; + public Func Released; + + public InputTarget() + { + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + Alpha = 0; + } + + public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false; + public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false; + } + + public bool OnPressed(ManiaAction action) + { + if (action != Action) + return false; + + var hitObject = HitObjects.Objects.LastOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.FirstOrDefault(); + hitObject?.PlaySamples(); + + return true; + } + + public bool OnReleased(ManiaAction action) => false; + } +} diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index f50f077c76..6566d44ef5 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -1,42 +1,42 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Mania.UI -{ - internal class DrawableManiaJudgement : DrawableJudgement - { - public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject) - : base(judgement, judgedObject) - { - } - - [BackgroundDependencyLoader] - private void load() - { - if (JudgementText != null) - JudgementText.TextSize = 25; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - this.FadeInFromZero(50, Easing.OutQuint); - - if (Judgement.IsHit) - { - this.ScaleTo(0.8f); - this.ScaleTo(1, 250, Easing.OutElastic); - - this.Delay(50).FadeOut(200).ScaleTo(0.75f, 250); - } - - Expire(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.UI +{ + internal class DrawableManiaJudgement : DrawableJudgement + { + public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject) + : base(judgement, judgedObject) + { + } + + [BackgroundDependencyLoader] + private void load() + { + if (JudgementText != null) + JudgementText.TextSize = 25; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.FadeInFromZero(50, Easing.OutQuint); + + if (Judgement.IsHit) + { + this.ScaleTo(0.8f); + this.ScaleTo(1, 250, Easing.OutElastic); + + this.Delay(50).FadeOut(200).ScaleTo(0.75f, 250); + } + + Expire(); + } + } +} diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs index dcf54f0819..f01dfc0db1 100644 --- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs @@ -1,66 +1,66 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Mania.UI -{ - internal class HitExplosion : CompositeDrawable - { - private readonly Box inner; - - public HitExplosion(DrawableHitObject judgedObject) - { - bool isTick = judgedObject is DrawableHoldNoteTick; - - Anchor = Anchor.TopCentre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - Size = new Vector2(isTick ? 0.5f : 1); - FillMode = FillMode.Fit; - - Blending = BlendingMode.Additive; - - Color4 accent = isTick ? Color4.White : judgedObject.AccentColour; - - InternalChild = new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = 1, - BorderColour = accent, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = accent, - Radius = 10, - Hollow = true - }, - Child = inner = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = accent, - Alpha = 1, - AlwaysPresent = true, - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500); - inner.FadeOut(250); - - Expire(true); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.UI +{ + internal class HitExplosion : CompositeDrawable + { + private readonly Box inner; + + public HitExplosion(DrawableHitObject judgedObject) + { + bool isTick = judgedObject is DrawableHoldNoteTick; + + Anchor = Anchor.TopCentre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(isTick ? 0.5f : 1); + FillMode = FillMode.Fit; + + Blending = BlendingMode.Additive; + + Color4 accent = isTick ? Color4.White : judgedObject.AccentColour; + + InternalChild = new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = 1, + BorderColour = accent, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = accent, + Radius = 10, + Hollow = true + }, + Child = inner = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = accent, + Alpha = 1, + AlwaysPresent = true, + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500); + inner.FadeOut(250); + + Expire(true); + } + } +} diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index c3cbf81af1..4b936fc7f9 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -1,94 +1,94 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Rulesets.Mania.Objects; -using osu.Framework.Graphics.Containers; -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Mania.Configuration; -using osu.Game.Rulesets.UI.Scrolling; - -namespace osu.Game.Rulesets.Mania.UI -{ - public class ManiaPlayfield : ScrollingPlayfield - { - /// - /// Whether this playfield should be inverted. This flips everything inside the playfield. - /// - public readonly Bindable Inverted = new Bindable(true); - - public List Columns => stages.SelectMany(x => x.Columns).ToList(); - private readonly List stages = new List(); - - public ManiaPlayfield(List stageDefinitions) - : base(ScrollingDirection.Up) - { - if (stageDefinitions == null) - throw new ArgumentNullException(nameof(stageDefinitions)); - - if (stageDefinitions.Count <= 0) - throw new ArgumentException("Can't have zero or fewer stages."); - - Inverted.Value = true; - - GridContainer playfieldGrid; - InternalChild = playfieldGrid = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] { new Drawable[stageDefinitions.Count] } - }; - - var normalColumnAction = ManiaAction.Key1; - var specialColumnAction = ManiaAction.Special1; - int firstColumnIndex = 0; - for (int i = 0; i < stageDefinitions.Count; i++) - { - var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction); - newStage.VisibleTimeRange.BindTo(VisibleTimeRange); - newStage.Inverted.BindTo(Inverted); - - playfieldGrid.Content[0][i] = newStage; - - stages.Add(newStage); - AddNested(newStage); - - firstColumnIndex += newStage.Columns.Count; - } - } - - public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h); - - public void Add(BarLine barline) => stages.ForEach(s => s.Add(barline)); - - private ManiaStage getStageByColumn(int column) - { - int sum = 0; - foreach (var stage in stages) - { - sum = sum + stage.Columns.Count; - if (sum > column) - return stage; - } - - return null; - } - - [BackgroundDependencyLoader] - private void load(ManiaConfigManager maniaConfig) - { - maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange); - } - - internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) - { - getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Rulesets.Mania.Objects; +using osu.Framework.Graphics.Containers; +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Mania.Configuration; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Rulesets.Mania.UI +{ + public class ManiaPlayfield : ScrollingPlayfield + { + /// + /// Whether this playfield should be inverted. This flips everything inside the playfield. + /// + public readonly Bindable Inverted = new Bindable(true); + + public List Columns => stages.SelectMany(x => x.Columns).ToList(); + private readonly List stages = new List(); + + public ManiaPlayfield(List stageDefinitions) + : base(ScrollingDirection.Up) + { + if (stageDefinitions == null) + throw new ArgumentNullException(nameof(stageDefinitions)); + + if (stageDefinitions.Count <= 0) + throw new ArgumentException("Can't have zero or fewer stages."); + + Inverted.Value = true; + + GridContainer playfieldGrid; + InternalChild = playfieldGrid = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] { new Drawable[stageDefinitions.Count] } + }; + + var normalColumnAction = ManiaAction.Key1; + var specialColumnAction = ManiaAction.Special1; + int firstColumnIndex = 0; + for (int i = 0; i < stageDefinitions.Count; i++) + { + var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction); + newStage.VisibleTimeRange.BindTo(VisibleTimeRange); + newStage.Inverted.BindTo(Inverted); + + playfieldGrid.Content[0][i] = newStage; + + stages.Add(newStage); + AddNested(newStage); + + firstColumnIndex += newStage.Columns.Count; + } + } + + public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h); + + public void Add(BarLine barline) => stages.ForEach(s => s.Add(barline)); + + private ManiaStage getStageByColumn(int column) + { + int sum = 0; + foreach (var stage in stages) + { + sum = sum + stage.Columns.Count; + if (sum > column) + return stage; + } + + return null; + } + + [BackgroundDependencyLoader] + private void load(ManiaConfigManager maniaConfig) + { + maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange); + } + + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + { + getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement); + } + } +} diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 3ecfee1e8c..76afaf270f 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -1,111 +1,111 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Input; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Configuration; -using osu.Game.Input.Handlers; -using osu.Game.Rulesets.Configuration; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.Configuration; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Rulesets.Mania.Replays; -using osu.Game.Rulesets.Mania.Scoring; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.UI.Scrolling; -using OpenTK; - -namespace osu.Game.Rulesets.Mania.UI -{ - public class ManiaRulesetContainer : ScrollingRulesetContainer - { - public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap; - - public IEnumerable BarLines; - - public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) - { - // Generate the bar lines - double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - - var timingPoints = Beatmap.ControlPointInfo.TimingPoints; - var barLines = new List(); - - for (int i = 0; i < timingPoints.Count; i++) - { - TimingControlPoint point = timingPoints[i]; - - // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object - double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; - - int index = 0; - for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) - { - barLines.Add(new BarLine - { - StartTime = t, - ControlPoint = point, - BeatIndex = index - }); - } - } - - BarLines = barLines; - } - - [BackgroundDependencyLoader] - private void load() - { - BarLines.ForEach(Playfield.Add); - } - - protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - - public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); - - public override int Variant => (int)(Mods.OfType().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns; - - public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant); - - protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap); - - protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) - { - ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action; - - var holdNote = h as HoldNote; - if (holdNote != null) - return new DrawableHoldNote(holdNote, action); - - var note = h as Note; - if (note != null) - return new DrawableNote(note, action); - - return null; - } - - protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f); - - protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); - - protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; +using osu.Game.Input.Handlers; +using osu.Game.Rulesets.Configuration; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.Configuration; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; +using OpenTK; + +namespace osu.Game.Rulesets.Mania.UI +{ + public class ManiaRulesetContainer : ScrollingRulesetContainer + { + public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap; + + public IEnumerable BarLines; + + public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + // Generate the bar lines + double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; + + var timingPoints = Beatmap.ControlPointInfo.TimingPoints; + var barLines = new List(); + + for (int i = 0; i < timingPoints.Count; i++) + { + TimingControlPoint point = timingPoints[i]; + + // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object + double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; + + int index = 0; + for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) + { + barLines.Add(new BarLine + { + StartTime = t, + ControlPoint = point, + BeatIndex = index + }); + } + } + + BarLines = barLines; + } + + [BackgroundDependencyLoader] + private void load() + { + BarLines.ForEach(Playfield.Add); + } + + protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); + + public override int Variant => (int)(Mods.OfType().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns; + + public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant); + + protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap); + + protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) + { + ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action; + + var holdNote = h as HoldNote; + if (holdNote != null) + return new DrawableHoldNote(holdNote, action); + + var note = h as Note; + if (note != null) + return new DrawableNote(note, action); + + return null; + } + + protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f); + + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); + + protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant); + } +} diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs index d4ca704829..904be3a9e3 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs @@ -1,221 +1,221 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.UI.Scrolling; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Mania.UI -{ - /// - /// A collection of s. - /// - internal class ManiaStage : ScrollingPlayfield - { - public const float HIT_TARGET_POSITION = 50; - - /// - /// Whether this playfield should be inverted. This flips everything inside the playfield. - /// - public readonly Bindable Inverted = new Bindable(true); - - public IReadOnlyList Columns => columnFlow.Children; - private readonly FillFlowContainer columnFlow; - - protected override Container Content => content; - private readonly Container content; - - public Container Judgements => judgements; - private readonly JudgementContainer judgements; - - private readonly Container topLevelContainer; - - private List normalColumnColours = new List(); - private Color4 specialColumnColour; - - private readonly int firstColumnIndex; - - public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction) - : base(ScrollingDirection.Up) - { - this.firstColumnIndex = firstColumnIndex; - - Name = "Stage"; - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; - - InternalChildren = new Drawable[] - { - new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Children = new Drawable[] - { - new Container - { - Name = "Columns mask", - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - Children = new Drawable[] - { - new Box - { - Name = "Background", - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black - }, - columnFlow = new FillFlowContainer - { - Name = "Columns", - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Direction = FillDirection.Horizontal, - Padding = new MarginPadding { Left = 1, Right = 1 }, - Spacing = new Vector2(1, 0) - }, - } - }, - new Container - { - Name = "Barlines mask", - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Y, - Width = 1366, // Bar lines should only be masked on the vertical axis - BypassAutoSizeAxes = Axes.Both, - Masking = true, - Child = content = new Container - { - Name = "Bar lines", - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Y, - Padding = new MarginPadding { Top = HIT_TARGET_POSITION } - } - }, - judgements = new JudgementContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Y = HIT_TARGET_POSITION + 150, - BypassAutoSizeAxes = Axes.Both - }, - topLevelContainer = new Container { RelativeSizeAxes = Axes.Both } - } - } - }; - - for (int i = 0; i < definition.Columns; i++) - { - var isSpecial = definition.IsSpecialColumn(i); - var column = new Column - { - IsSpecial = isSpecial, - Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ - }; - - AddColumn(column); - } - - Inverted.ValueChanged += invertedChanged; - Inverted.TriggerChange(); - } - - private void invertedChanged(bool newValue) - { - Scale = new Vector2(1, newValue ? -1 : 1); - Judgements.Scale = Scale; - } - - public void AddColumn(Column c) - { - c.VisibleTimeRange.BindTo(VisibleTimeRange); - - topLevelContainer.Add(c.TopLevelContainer.CreateProxy()); - columnFlow.Add(c); - AddNested(c); - } - - public override void Add(DrawableHitObject h) - { - var maniaObject = (ManiaHitObject)h.HitObject; - int columnIndex = maniaObject.Column - firstColumnIndex; - Columns.ElementAt(columnIndex).Add(h); - h.OnJudgement += OnJudgement; - } - - public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline)); - - internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) - { - judgements.Clear(); - judgements.Add(new DrawableManiaJudgement(judgement, judgedObject) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - normalColumnColours = new List - { - colours.RedDark, - colours.GreenDark - }; - - specialColumnColour = colours.BlueDark; - - // Set the special column + colour + key - foreach (var column in Columns) - { - if (!column.IsSpecial) - continue; - - column.AccentColour = specialColumnColour; - } - - var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList(); - - // We'll set the colours of the non-special columns in a separate loop, because the non-special - // column colours are mirrored across their centre and special styles mess with this - for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++) - { - Color4 colour = normalColumnColours[i % normalColumnColours.Count]; - nonSpecialColumns[i].AccentColour = colour; - nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour; - } - } - - protected override void Update() - { - // Due to masking differences, it is not possible to get the width of the columns container automatically - // While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually - content.Width = columnFlow.Width; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Mania.UI +{ + /// + /// A collection of s. + /// + internal class ManiaStage : ScrollingPlayfield + { + public const float HIT_TARGET_POSITION = 50; + + /// + /// Whether this playfield should be inverted. This flips everything inside the playfield. + /// + public readonly Bindable Inverted = new Bindable(true); + + public IReadOnlyList Columns => columnFlow.Children; + private readonly FillFlowContainer columnFlow; + + protected override Container Content => content; + private readonly Container content; + + public Container Judgements => judgements; + private readonly JudgementContainer judgements; + + private readonly Container topLevelContainer; + + private List normalColumnColours = new List(); + private Color4 specialColumnColour; + + private readonly int firstColumnIndex; + + public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction) + : base(ScrollingDirection.Up) + { + this.firstColumnIndex = firstColumnIndex; + + Name = "Stage"; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; + + InternalChildren = new Drawable[] + { + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] + { + new Container + { + Name = "Columns mask", + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + Children = new Drawable[] + { + new Box + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + columnFlow = new FillFlowContainer + { + Name = "Columns", + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding { Left = 1, Right = 1 }, + Spacing = new Vector2(1, 0) + }, + } + }, + new Container + { + Name = "Barlines mask", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + Width = 1366, // Bar lines should only be masked on the vertical axis + BypassAutoSizeAxes = Axes.Both, + Masking = true, + Child = content = new Container + { + Name = "Bar lines", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding { Top = HIT_TARGET_POSITION } + } + }, + judgements = new JudgementContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Y = HIT_TARGET_POSITION + 150, + BypassAutoSizeAxes = Axes.Both + }, + topLevelContainer = new Container { RelativeSizeAxes = Axes.Both } + } + } + }; + + for (int i = 0; i < definition.Columns; i++) + { + var isSpecial = definition.IsSpecialColumn(i); + var column = new Column + { + IsSpecial = isSpecial, + Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ + }; + + AddColumn(column); + } + + Inverted.ValueChanged += invertedChanged; + Inverted.TriggerChange(); + } + + private void invertedChanged(bool newValue) + { + Scale = new Vector2(1, newValue ? -1 : 1); + Judgements.Scale = Scale; + } + + public void AddColumn(Column c) + { + c.VisibleTimeRange.BindTo(VisibleTimeRange); + + topLevelContainer.Add(c.TopLevelContainer.CreateProxy()); + columnFlow.Add(c); + AddNested(c); + } + + public override void Add(DrawableHitObject h) + { + var maniaObject = (ManiaHitObject)h.HitObject; + int columnIndex = maniaObject.Column - firstColumnIndex; + Columns.ElementAt(columnIndex).Add(h); + h.OnJudgement += OnJudgement; + } + + public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline)); + + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + { + judgements.Clear(); + judgements.Add(new DrawableManiaJudgement(judgement, judgedObject) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + normalColumnColours = new List + { + colours.RedDark, + colours.GreenDark + }; + + specialColumnColour = colours.BlueDark; + + // Set the special column + colour + key + foreach (var column in Columns) + { + if (!column.IsSpecial) + continue; + + column.AccentColour = specialColumnColour; + } + + var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList(); + + // We'll set the colours of the non-special columns in a separate loop, because the non-special + // column colours are mirrored across their centre and special styles mess with this + for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++) + { + Color4 colour = normalColumnColours[i % normalColumnColours.Count]; + nonSpecialColumns[i].AccentColour = colour; + nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour; + } + } + + protected override void Update() + { + // Due to masking differences, it is not possible to get the width of the columns container automatically + // While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually + content.Width = columnFlow.Width; + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 59c59dc0e3..6ac3c016a0 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -1,70 +1,70 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Osu.Beatmaps; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Tests.Beatmaps; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Tests -{ - public class OsuBeatmapConversionTest : BeatmapConversionTest - { - protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - - [TestCase("basic")] - [TestCase("colinear-perfect-curve")] - public new void Test(string name) - { - base.Test(name); - } - - protected override IEnumerable CreateConvertValue(HitObject hitObject) - { - var startPosition = (hitObject as IHasPosition)?.Position ?? new Vector2(256, 192); - var endPosition = (hitObject as Slider)?.EndPosition ?? startPosition; - - yield return new ConvertValue - { - StartTime = hitObject.StartTime, - EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, - StartX = startPosition.X, - StartY = startPosition.Y, - EndX = endPosition.X, - EndY = endPosition.Y - }; - } - - protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new OsuBeatmapConverter(); - } - - public struct ConvertValue : IEquatable - { - /// - /// A sane value to account for osu!stable using ints everwhere. - /// - private const double conversion_lenience = 2; - - public double StartTime; - public double EndTime; - public float StartX; - public float StartY; - public float EndX; - public float EndY; - - public bool Equals(ConvertValue other) - => Precision.AlmostEquals(StartTime, other.StartTime) - && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) - && Precision.AlmostEquals(StartX, other.StartX) - && Precision.AlmostEquals(StartY, other.StartY, conversion_lenience) - && Precision.AlmostEquals(EndX, other.EndX, conversion_lenience) - && Precision.AlmostEquals(EndY, other.EndY, conversion_lenience); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class OsuBeatmapConversionTest : BeatmapConversionTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; + + [TestCase("basic")] + [TestCase("colinear-perfect-curve")] + public new void Test(string name) + { + base.Test(name); + } + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + var startPosition = (hitObject as IHasPosition)?.Position ?? new Vector2(256, 192); + var endPosition = (hitObject as Slider)?.EndPosition ?? startPosition; + + yield return new ConvertValue + { + StartTime = hitObject.StartTime, + EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, + StartX = startPosition.X, + StartY = startPosition.Y, + EndX = endPosition.X, + EndY = endPosition.Y + }; + } + + protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new OsuBeatmapConverter(); + } + + public struct ConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everwhere. + /// + private const double conversion_lenience = 2; + + public double StartTime; + public double EndTime; + public float StartX; + public float StartY; + public float EndX; + public float EndY; + + public bool Equals(ConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime) + && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) + && Precision.AlmostEquals(StartX, other.StartX) + && Precision.AlmostEquals(StartY, other.StartY, conversion_lenience) + && Precision.AlmostEquals(EndX, other.EndX, conversion_lenience) + && Precision.AlmostEquals(EndY, other.EndY, conversion_lenience); + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseEditor.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseEditor.cs index a11f32935e..f2525ad555 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseEditor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseEditor.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCaseEditor : EditorTestCase - { - public TestCaseEditor() - : base(new OsuRuleset()) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseEditor : EditorTestCase + { + public TestCaseEditor() + : base(new OsuRuleset()) + { + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs index 273422f2e9..472da88e1f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Cursor; -using osu.Game.Graphics.Cursor; -using osu.Game.Rulesets.Osu.UI.Cursor; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCaseGameplayCursor : OsuTestCase, IProvideCursor - { - private GameplayCursor cursor; - - public override IReadOnlyList RequiredTypes => new [] { typeof(CursorTrail) }; - - public CursorContainer Cursor => cursor; - - public bool ProvidingUserCursor => true; - - [BackgroundDependencyLoader] - private void load() - { - Add(cursor = new GameplayCursor { RelativeSizeAxes = Axes.Both }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.Cursor; +using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseGameplayCursor : OsuTestCase, IProvideCursor + { + private GameplayCursor cursor; + + public override IReadOnlyList RequiredTypes => new [] { typeof(CursorTrail) }; + + public CursorContainer Cursor => cursor; + + public bool ProvidingUserCursor => true; + + [BackgroundDependencyLoader] + private void load() + { + Add(cursor = new GameplayCursor { RelativeSizeAxes = Axes.Both }); + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs index b0cfa43f15..7af7140fd8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs @@ -1,115 +1,115 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Visual; -using OpenTK; -using osu.Game.Rulesets.Osu.Judgements; -using System.Collections.Generic; -using System; -using osu.Game.Rulesets.Mods; -using System.Linq; -using NUnit.Framework; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCaseHitCircle : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableHitCircle) - }; - - private readonly Container content; - protected override Container Content => content; - - private int depthIndex; - protected readonly List Mods = new List(); - - public TestCaseHitCircle() - { - base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Big Single", () => testSingle(2)); - AddStep("Miss Medium Single", () => testSingle(5)); - AddStep("Miss Small Single", () => testSingle(7)); - AddStep("Hit Big Single", () => testSingle(2, true)); - AddStep("Hit Medium Single", () => testSingle(5, true)); - AddStep("Hit Small Single", () => testSingle(7, true)); - AddStep("Miss Big Stream", () => testStream(2)); - AddStep("Miss Medium Stream", () => testStream(5)); - AddStep("Miss Small Stream", () => testStream(7)); - AddStep("Hit Big Stream", () => testStream(2, true)); - AddStep("Hit Medium Stream", () => testStream(5, true)); - AddStep("Hit Small Stream", () => testStream(7, true)); - } - - private void testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null) - { - positionOffset = positionOffset ?? Vector2.Zero; - - var circle = new HitCircle - { - StartTime = Time.Current + 1000 + timeOffset, - Position = positionOffset.Value, - }; - - circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); - - var drawable = new TestDrawableHitCircle(circle, auto) - { - Anchor = Anchor.Centre, - Depth = depthIndex++ - }; - - foreach (var mod in Mods.OfType()) - mod.ApplyToDrawableHitObjects(new[] { drawable }); - - Add(drawable); - } - - private void testStream(float circleSize, bool auto = false) - { - Vector2 pos = new Vector2(-250, 0); - - for (int i = 0; i <= 1000; i += 100) - { - testSingle(circleSize, auto, i, pos); - pos.X += 50; - } - } - - private class TestDrawableHitCircle : DrawableHitCircle - { - private readonly bool auto; - - public TestDrawableHitCircle(HitCircle h, bool auto) : base(h) - { - this.auto = auto; - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (auto && !userTriggered && timeOffset > 0) - { - // force success - AddJudgement(new OsuJudgement - { - Result = HitResult.Great - }); - State.Value = ArmedState.Hit; - } - else - base.CheckForJudgements(userTriggered, timeOffset); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Visual; +using OpenTK; +using osu.Game.Rulesets.Osu.Judgements; +using System.Collections.Generic; +using System; +using osu.Game.Rulesets.Mods; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseHitCircle : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DrawableHitCircle) + }; + + private readonly Container content; + protected override Container Content => content; + + private int depthIndex; + protected readonly List Mods = new List(); + + public TestCaseHitCircle() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Miss Big Single", () => testSingle(2)); + AddStep("Miss Medium Single", () => testSingle(5)); + AddStep("Miss Small Single", () => testSingle(7)); + AddStep("Hit Big Single", () => testSingle(2, true)); + AddStep("Hit Medium Single", () => testSingle(5, true)); + AddStep("Hit Small Single", () => testSingle(7, true)); + AddStep("Miss Big Stream", () => testStream(2)); + AddStep("Miss Medium Stream", () => testStream(5)); + AddStep("Miss Small Stream", () => testStream(7)); + AddStep("Hit Big Stream", () => testStream(2, true)); + AddStep("Hit Medium Stream", () => testStream(5, true)); + AddStep("Hit Small Stream", () => testStream(7, true)); + } + + private void testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null) + { + positionOffset = positionOffset ?? Vector2.Zero; + + var circle = new HitCircle + { + StartTime = Time.Current + 1000 + timeOffset, + Position = positionOffset.Value, + }; + + circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); + + var drawable = new TestDrawableHitCircle(circle, auto) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + + foreach (var mod in Mods.OfType()) + mod.ApplyToDrawableHitObjects(new[] { drawable }); + + Add(drawable); + } + + private void testStream(float circleSize, bool auto = false) + { + Vector2 pos = new Vector2(-250, 0); + + for (int i = 0; i <= 1000; i += 100) + { + testSingle(circleSize, auto, i, pos); + pos.X += 50; + } + } + + private class TestDrawableHitCircle : DrawableHitCircle + { + private readonly bool auto; + + public TestDrawableHitCircle(HitCircle h, bool auto) : base(h) + { + this.auto = auto; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (auto && !userTriggered && timeOffset > 0) + { + // force success + AddJudgement(new OsuJudgement + { + Result = HitResult.Great + }); + State.Value = ArmedState.Hit; + } + else + base.CheckForJudgements(userTriggered, timeOffset); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs index f030c6db60..68c35faca4 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleHidden.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Game.Rulesets.Osu.Mods; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCaseHitCircleHidden : TestCaseHitCircle - { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - - public TestCaseHitCircleHidden() - { - Mods.Add(new OsuModHidden()); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseHitCircleHidden : TestCaseHitCircle + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); + + public TestCaseHitCircleHidden() + { + Mods.Add(new OsuModHidden()); + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Osu.Tests/TestCasePerformancePoints.cs index b6dca3f1cb..63026fe316 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCasePerformancePoints.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints - { - public TestCasePerformancePoints() - : base(new OsuRuleset()) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints + { + public TestCasePerformancePoints() + : base(new OsuRuleset()) + { + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs index e819d8fff5..f7f73f74a5 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs @@ -1,313 +1,313 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Visual; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Rulesets.Mods; -using System.Linq; -using NUnit.Framework; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCaseSlider : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SliderBall), - typeof(SliderBody), - typeof(SliderTick), - typeof(DrawableSlider), - typeof(DrawableSliderTick), - typeof(DrawableRepeatPoint), - typeof(DrawableOsuHitObject) - }; - - private readonly Container content; - protected override Container Content => content; - - private int depthIndex; - protected readonly List Mods = new List(); - - public TestCaseSlider() - { - base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Big Single", () => testSimpleBig()); - AddStep("Medium Single", () => testSimpleMedium()); - AddStep("Small Single", () => testSimpleSmall()); - AddStep("Big 1 Repeat", () => testSimpleBig(1)); - AddStep("Medium 1 Repeat", () => testSimpleMedium(1)); - AddStep("Small 1 Repeat", () => testSimpleSmall(1)); - AddStep("Big 2 Repeats", () => testSimpleBig(2)); - AddStep("Medium 2 Repeats", () => testSimpleMedium(2)); - AddStep("Small 2 Repeats", () => testSimpleSmall(2)); - - AddStep("Slow Slider", testSlowSpeed); // slow long sliders take ages already so no repeat steps - AddStep("Slow Short Slider", () => testShortSlowSpeed()); - AddStep("Slow Short Slider 1 Repeats", () => testShortSlowSpeed(1)); - AddStep("Slow Short Slider 2 Repeats", () => testShortSlowSpeed(2)); - - AddStep("Fast Slider", () => testHighSpeed()); - AddStep("Fast Slider 1 Repeat", () => testHighSpeed(1)); - AddStep("Fast Slider 2 Repeats", () => testHighSpeed(2)); - AddStep("Fast Short Slider", () => testShortHighSpeed()); - AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1)); - AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2)); - AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6)); - - AddStep("Perfect Curve", () => testPerfect()); - AddStep("Perfect Curve 1 Repeat", () => testPerfect(1)); - AddStep("Perfect Curve 2 Repeats", () => testPerfect(2)); - - AddStep("Linear Slider", () => testLinear()); - AddStep("Linear Slider 1 Repeat", () => testLinear(1)); - AddStep("Linear Slider 2 Repeats", () => testLinear(2)); - - AddStep("Bezier Slider", () => testBezier()); - AddStep("Bezier Slider 1 Repeat", () => testBezier(1)); - AddStep("Bezier Slider 2 Repeats", () => testBezier(2)); - - AddStep("Linear Overlapping", () => testLinearOverlapping()); - AddStep("Linear Overlapping 1 Repeat", () => testLinearOverlapping(1)); - AddStep("Linear Overlapping 2 Repeats", () => testLinearOverlapping(2)); - - AddStep("Catmull Slider", () => testCatmull()); - AddStep("Catmull Slider 1 Repeat", () => testCatmull(1)); - AddStep("Catmull Slider 2 Repeats", () => testCatmull(2)); - - AddStep("Big Single, Large StackOffset", () => testSimpleBigLargeStackOffset()); - AddStep("Big 1 Repeat, Large StackOffset", () => testSimpleBigLargeStackOffset(1)); - } - - private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); - - private void testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10); - - private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats); - - private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats); - - private void testSlowSpeed() => createSlider(speedMultiplier: 0.5); - - private void testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5); - - private void testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15); - - private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); - - private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) - { - var slider = new Slider - { - StartTime = Time.Current + 1000, - Position = new Vector2(-(distance / 2), 0), - ControlPoints = new List - { - Vector2.Zero, - new Vector2(distance, 0), - }, - Distance = distance, - RepeatCount = repeats, - RepeatSamples = createEmptySamples(repeats), - StackHeight = stackHeight - }; - - addSlider(slider, circleSize, speedMultiplier); - } - - private void testPerfect(int repeats = 0) - { - var slider = new Slider - { - StartTime = Time.Current + 1000, - Position = new Vector2(-200, 0), - ControlPoints = new List - { - Vector2.Zero, - new Vector2(200, 200), - new Vector2(400, 0) - }, - Distance = 600, - RepeatCount = repeats, - RepeatSamples = createEmptySamples(repeats) - }; - - addSlider(slider, 2, 3); - } - - private void testLinear(int repeats = 0) => createLinear(repeats); - - private void createLinear(int repeats) - { - var slider = new Slider - { - CurveType = CurveType.Linear, - StartTime = Time.Current + 1000, - Position = new Vector2(-200, 0), - ControlPoints = new List - { - Vector2.Zero, - new Vector2(150, 75), - new Vector2(200, 0), - new Vector2(300, -200), - new Vector2(400, 0), - new Vector2(430, 0) - }, - Distance = 793.4417, - RepeatCount = repeats, - RepeatSamples = createEmptySamples(repeats) - }; - - addSlider(slider, 2, 3); - } - - private void testBezier(int repeats = 0) => createBezier(repeats); - - private void createBezier(int repeats) - { - var slider = new Slider - { - CurveType = CurveType.Bezier, - StartTime = Time.Current + 1000, - Position = new Vector2(-200, 0), - ControlPoints = new List - { - Vector2.Zero, - new Vector2(150, 75), - new Vector2(200, 100), - new Vector2(300, -200), - new Vector2(430, 0) - }, - Distance = 480, - RepeatCount = repeats, - RepeatSamples = createEmptySamples(repeats) - }; - - addSlider(slider, 2, 3); - } - - private void testLinearOverlapping(int repeats = 0) => createOverlapping(repeats); - - private void createOverlapping(int repeats) - { - var slider = new Slider - { - CurveType = CurveType.Linear, - StartTime = Time.Current + 1000, - Position = new Vector2(0, 0), - ControlPoints = new List - { - Vector2.Zero, - new Vector2(-200, 0), - new Vector2(0, 0), - new Vector2(0, -200), - new Vector2(-200, -200), - new Vector2(0, -200) - }, - Distance = 1000, - RepeatCount = repeats, - RepeatSamples = createEmptySamples(repeats) - }; - - addSlider(slider, 2, 3); - } - - private void testCatmull(int repeats = 0) => createCatmull(repeats); - - private void createCatmull(int repeats = 0) - { - var repeatSamples = new List>(); - for (int i = 0; i < repeats; i++) - repeatSamples.Add(new List()); - - var slider = new Slider - { - StartTime = Time.Current + 1000, - Position = new Vector2(-100, 0), - CurveType = CurveType.Catmull, - ControlPoints = new List - { - Vector2.Zero, - new Vector2(50, -50), - new Vector2(150, 50), - new Vector2(200, 0) - }, - Distance = 300, - RepeatCount = repeats, - RepeatSamples = repeatSamples - }; - - addSlider(slider, 3, 1); - } - - private List> createEmptySamples(int repeats) - { - var repeatSamples = new List>(); - for (int i = 0; i < repeats; i++) - repeatSamples.Add(new List()); - return repeatSamples; - } - - private void addSlider(Slider slider, float circleSize, double speedMultiplier) - { - var cpi = new ControlPointInfo(); - cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier }); - - slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize, SliderTickRate = 3 }); - - var drawable = new DrawableSlider(slider) - { - Anchor = Anchor.Centre, - Depth = depthIndex++ - }; - - foreach (var mod in Mods.OfType()) - mod.ApplyToDrawableHitObjects(new[] { drawable }); - - drawable.OnJudgement += onJudgement; - - Add(drawable); - } - - private float judgementOffsetDirection = 1; - private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) - { - var osuObject = judgedObject as DrawableOsuHitObject; - if (osuObject == null) - return; - - OsuSpriteText text; - Add(text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = judgement.IsHit ? "Hit!" : "Miss!", - Colour = judgement.IsHit ? Color4.Green : Color4.Red, - TextSize = 30, - Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45) - }); - - text.Delay(150) - .Then().FadeOut(200) - .Then().Expire(); - - judgementOffsetDirection *= -1; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Visual; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Rulesets.Mods; +using System.Linq; +using NUnit.Framework; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseSlider : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(SliderBall), + typeof(SliderBody), + typeof(SliderTick), + typeof(DrawableSlider), + typeof(DrawableSliderTick), + typeof(DrawableRepeatPoint), + typeof(DrawableOsuHitObject) + }; + + private readonly Container content; + protected override Container Content => content; + + private int depthIndex; + protected readonly List Mods = new List(); + + public TestCaseSlider() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Big Single", () => testSimpleBig()); + AddStep("Medium Single", () => testSimpleMedium()); + AddStep("Small Single", () => testSimpleSmall()); + AddStep("Big 1 Repeat", () => testSimpleBig(1)); + AddStep("Medium 1 Repeat", () => testSimpleMedium(1)); + AddStep("Small 1 Repeat", () => testSimpleSmall(1)); + AddStep("Big 2 Repeats", () => testSimpleBig(2)); + AddStep("Medium 2 Repeats", () => testSimpleMedium(2)); + AddStep("Small 2 Repeats", () => testSimpleSmall(2)); + + AddStep("Slow Slider", testSlowSpeed); // slow long sliders take ages already so no repeat steps + AddStep("Slow Short Slider", () => testShortSlowSpeed()); + AddStep("Slow Short Slider 1 Repeats", () => testShortSlowSpeed(1)); + AddStep("Slow Short Slider 2 Repeats", () => testShortSlowSpeed(2)); + + AddStep("Fast Slider", () => testHighSpeed()); + AddStep("Fast Slider 1 Repeat", () => testHighSpeed(1)); + AddStep("Fast Slider 2 Repeats", () => testHighSpeed(2)); + AddStep("Fast Short Slider", () => testShortHighSpeed()); + AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1)); + AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2)); + AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6)); + + AddStep("Perfect Curve", () => testPerfect()); + AddStep("Perfect Curve 1 Repeat", () => testPerfect(1)); + AddStep("Perfect Curve 2 Repeats", () => testPerfect(2)); + + AddStep("Linear Slider", () => testLinear()); + AddStep("Linear Slider 1 Repeat", () => testLinear(1)); + AddStep("Linear Slider 2 Repeats", () => testLinear(2)); + + AddStep("Bezier Slider", () => testBezier()); + AddStep("Bezier Slider 1 Repeat", () => testBezier(1)); + AddStep("Bezier Slider 2 Repeats", () => testBezier(2)); + + AddStep("Linear Overlapping", () => testLinearOverlapping()); + AddStep("Linear Overlapping 1 Repeat", () => testLinearOverlapping(1)); + AddStep("Linear Overlapping 2 Repeats", () => testLinearOverlapping(2)); + + AddStep("Catmull Slider", () => testCatmull()); + AddStep("Catmull Slider 1 Repeat", () => testCatmull(1)); + AddStep("Catmull Slider 2 Repeats", () => testCatmull(2)); + + AddStep("Big Single, Large StackOffset", () => testSimpleBigLargeStackOffset()); + AddStep("Big 1 Repeat, Large StackOffset", () => testSimpleBigLargeStackOffset(1)); + } + + private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); + + private void testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10); + + private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats); + + private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats); + + private void testSlowSpeed() => createSlider(speedMultiplier: 0.5); + + private void testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5); + + private void testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15); + + private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); + + private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) + { + var slider = new Slider + { + StartTime = Time.Current + 1000, + Position = new Vector2(-(distance / 2), 0), + ControlPoints = new List + { + Vector2.Zero, + new Vector2(distance, 0), + }, + Distance = distance, + RepeatCount = repeats, + RepeatSamples = createEmptySamples(repeats), + StackHeight = stackHeight + }; + + addSlider(slider, circleSize, speedMultiplier); + } + + private void testPerfect(int repeats = 0) + { + var slider = new Slider + { + StartTime = Time.Current + 1000, + Position = new Vector2(-200, 0), + ControlPoints = new List + { + Vector2.Zero, + new Vector2(200, 200), + new Vector2(400, 0) + }, + Distance = 600, + RepeatCount = repeats, + RepeatSamples = createEmptySamples(repeats) + }; + + addSlider(slider, 2, 3); + } + + private void testLinear(int repeats = 0) => createLinear(repeats); + + private void createLinear(int repeats) + { + var slider = new Slider + { + CurveType = CurveType.Linear, + StartTime = Time.Current + 1000, + Position = new Vector2(-200, 0), + ControlPoints = new List + { + Vector2.Zero, + new Vector2(150, 75), + new Vector2(200, 0), + new Vector2(300, -200), + new Vector2(400, 0), + new Vector2(430, 0) + }, + Distance = 793.4417, + RepeatCount = repeats, + RepeatSamples = createEmptySamples(repeats) + }; + + addSlider(slider, 2, 3); + } + + private void testBezier(int repeats = 0) => createBezier(repeats); + + private void createBezier(int repeats) + { + var slider = new Slider + { + CurveType = CurveType.Bezier, + StartTime = Time.Current + 1000, + Position = new Vector2(-200, 0), + ControlPoints = new List + { + Vector2.Zero, + new Vector2(150, 75), + new Vector2(200, 100), + new Vector2(300, -200), + new Vector2(430, 0) + }, + Distance = 480, + RepeatCount = repeats, + RepeatSamples = createEmptySamples(repeats) + }; + + addSlider(slider, 2, 3); + } + + private void testLinearOverlapping(int repeats = 0) => createOverlapping(repeats); + + private void createOverlapping(int repeats) + { + var slider = new Slider + { + CurveType = CurveType.Linear, + StartTime = Time.Current + 1000, + Position = new Vector2(0, 0), + ControlPoints = new List + { + Vector2.Zero, + new Vector2(-200, 0), + new Vector2(0, 0), + new Vector2(0, -200), + new Vector2(-200, -200), + new Vector2(0, -200) + }, + Distance = 1000, + RepeatCount = repeats, + RepeatSamples = createEmptySamples(repeats) + }; + + addSlider(slider, 2, 3); + } + + private void testCatmull(int repeats = 0) => createCatmull(repeats); + + private void createCatmull(int repeats = 0) + { + var repeatSamples = new List>(); + for (int i = 0; i < repeats; i++) + repeatSamples.Add(new List()); + + var slider = new Slider + { + StartTime = Time.Current + 1000, + Position = new Vector2(-100, 0), + CurveType = CurveType.Catmull, + ControlPoints = new List + { + Vector2.Zero, + new Vector2(50, -50), + new Vector2(150, 50), + new Vector2(200, 0) + }, + Distance = 300, + RepeatCount = repeats, + RepeatSamples = repeatSamples + }; + + addSlider(slider, 3, 1); + } + + private List> createEmptySamples(int repeats) + { + var repeatSamples = new List>(); + for (int i = 0; i < repeats; i++) + repeatSamples.Add(new List()); + return repeatSamples; + } + + private void addSlider(Slider slider, float circleSize, double speedMultiplier) + { + var cpi = new ControlPointInfo(); + cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier }); + + slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize, SliderTickRate = 3 }); + + var drawable = new DrawableSlider(slider) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + + foreach (var mod in Mods.OfType()) + mod.ApplyToDrawableHitObjects(new[] { drawable }); + + drawable.OnJudgement += onJudgement; + + Add(drawable); + } + + private float judgementOffsetDirection = 1; + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) + { + var osuObject = judgedObject as DrawableOsuHitObject; + if (osuObject == null) + return; + + OsuSpriteText text; + Add(text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = judgement.IsHit ? "Hit!" : "Miss!", + Colour = judgement.IsHit ? Color4.Green : Color4.Red, + TextSize = 30, + Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45) + }); + + text.Delay(150) + .Then().FadeOut(200) + .Then().Expire(); + + judgementOffsetDirection *= -1; + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs index 57b719464f..1c207d1fca 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderHidden.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Game.Rulesets.Osu.Mods; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCaseSliderHidden : TestCaseSlider - { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - - public TestCaseSliderHidden() - { - Mods.Add(new OsuModHidden()); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseSliderHidden : TestCaseSlider + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); + + public TestCaseSliderHidden() + { + Mods.Add(new OsuModHidden()); + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs index d3620bcbda..b05a763e88 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs @@ -1,88 +1,88 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCaseSpinner : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] -{ - typeof(SpinnerDisc), - typeof(DrawableSpinner), - typeof(DrawableOsuHitObject) - }; - - private readonly Container content; - protected override Container Content => content; - - private int depthIndex; - protected readonly List Mods = new List(); - - public TestCaseSpinner() - { - base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Big", () => testSingle(2)); - AddStep("Miss Medium", () => testSingle(5)); - AddStep("Miss Small", () => testSingle(7)); - AddStep("Hit Big", () => testSingle(2, true)); - AddStep("Hit Medium", () => testSingle(5, true)); - AddStep("Hit Small", () => testSingle(7, true)); - } - - private void testSingle(float circleSize, bool auto = false) - { - var spinner = new Spinner { StartTime = Time.Current + 1000, EndTime = Time.Current + 4000 }; - - spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); - - var drawable = new TestDrawableSpinner(spinner, auto) - { - Anchor = Anchor.Centre, - Depth = depthIndex++ - }; - - foreach (var mod in Mods.OfType()) - mod.ApplyToDrawableHitObjects(new[] { drawable }); - - Add(drawable); - } - - private class TestDrawableSpinner : DrawableSpinner - { - private bool auto; - - public TestDrawableSpinner(Spinner s, bool auto) : base(s) - { - this.auto = auto; - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1) - { - // force completion only once to not break human interaction - Disc.RotationAbsolute = Spinner.SpinsRequired * 360; - auto = false; - } - - base.CheckForJudgements(userTriggered, timeOffset); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseSpinner : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] +{ + typeof(SpinnerDisc), + typeof(DrawableSpinner), + typeof(DrawableOsuHitObject) + }; + + private readonly Container content; + protected override Container Content => content; + + private int depthIndex; + protected readonly List Mods = new List(); + + public TestCaseSpinner() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Miss Big", () => testSingle(2)); + AddStep("Miss Medium", () => testSingle(5)); + AddStep("Miss Small", () => testSingle(7)); + AddStep("Hit Big", () => testSingle(2, true)); + AddStep("Hit Medium", () => testSingle(5, true)); + AddStep("Hit Small", () => testSingle(7, true)); + } + + private void testSingle(float circleSize, bool auto = false) + { + var spinner = new Spinner { StartTime = Time.Current + 1000, EndTime = Time.Current + 4000 }; + + spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); + + var drawable = new TestDrawableSpinner(spinner, auto) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + + foreach (var mod in Mods.OfType()) + mod.ApplyToDrawableHitObjects(new[] { drawable }); + + Add(drawable); + } + + private class TestDrawableSpinner : DrawableSpinner + { + private bool auto; + + public TestDrawableSpinner(Spinner s, bool auto) : base(s) + { + this.auto = auto; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1) + { + // force completion only once to not break human interaction + Disc.RotationAbsolute = Spinner.SpinsRequired * 360; + auto = false; + } + + base.CheckForJudgements(userTriggered, timeOffset); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs index 75b3b4c763..b93a6ed0f1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerHidden.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Game.Rulesets.Osu.Mods; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - public class TestCaseSpinnerHidden : TestCaseSpinner - { - public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - - public TestCaseSpinnerHidden() - { - Mods.Add(new OsuModHidden()); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseSpinnerHidden : TestCaseSpinner + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); + + public TestCaseSpinnerHidden() + { + Mods.Add(new OsuModHidden()); + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index b2b524a71c..49c5f15713 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -1,10 +1,10 @@ - - - - WinExe - netcoreapp2.0;net461 - - - - + + + + WinExe + netcoreapp2.0;net461 + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 37cb47a83e..1236076f48 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Objects; -using System.Collections.Generic; -using osu.Game.Rulesets.Objects.Types; -using System; -using osu.Game.Rulesets.Osu.UI; - -namespace osu.Game.Rulesets.Osu.Beatmaps -{ - internal class OsuBeatmapConverter : BeatmapConverter - { - protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasPosition) }; - - protected override IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap) - { - var curveData = original as IHasCurve; - var endTimeData = original as IHasEndTime; - var positionData = original as IHasPosition; - var comboData = original as IHasCombo; - - if (curveData != null) - { - yield return new Slider - { - StartTime = original.StartTime, - Samples = original.Samples, - ControlPoints = curveData.ControlPoints, - CurveType = curveData.CurveType, - Distance = curveData.Distance, - RepeatSamples = curveData.RepeatSamples, - RepeatCount = curveData.RepeatCount, - Position = positionData?.Position ?? Vector2.Zero, - NewCombo = comboData?.NewCombo ?? false - }; - } - else if (endTimeData != null) - { - yield return new Spinner - { - StartTime = original.StartTime, - Samples = original.Samples, - EndTime = endTimeData.EndTime, - - Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2, - }; - } - else - { - yield return new HitCircle - { - StartTime = original.StartTime, - Samples = original.Samples, - Position = positionData?.Position ?? Vector2.Zero, - NewCombo = comboData?.NewCombo ?? false - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using System.Collections.Generic; +using osu.Game.Rulesets.Objects.Types; +using System; +using osu.Game.Rulesets.Osu.UI; + +namespace osu.Game.Rulesets.Osu.Beatmaps +{ + internal class OsuBeatmapConverter : BeatmapConverter + { + protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasPosition) }; + + protected override IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap) + { + var curveData = original as IHasCurve; + var endTimeData = original as IHasEndTime; + var positionData = original as IHasPosition; + var comboData = original as IHasCombo; + + if (curveData != null) + { + yield return new Slider + { + StartTime = original.StartTime, + Samples = original.Samples, + ControlPoints = curveData.ControlPoints, + CurveType = curveData.CurveType, + Distance = curveData.Distance, + RepeatSamples = curveData.RepeatSamples, + RepeatCount = curveData.RepeatCount, + Position = positionData?.Position ?? Vector2.Zero, + NewCombo = comboData?.NewCombo ?? false + }; + } + else if (endTimeData != null) + { + yield return new Spinner + { + StartTime = original.StartTime, + Samples = original.Samples, + EndTime = endTimeData.EndTime, + + Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2, + }; + } + else + { + yield return new HitCircle + { + StartTime = original.StartTime, + Samples = original.Samples, + Position = positionData?.Position ?? Vector2.Zero, + NewCombo = comboData?.NewCombo ?? false + }; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index 42b22a71ec..afa2437bf6 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -1,166 +1,166 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.Beatmaps -{ - internal class OsuBeatmapProcessor : BeatmapProcessor - { - public override void PostProcess(Beatmap beatmap) - { - applyStacking(beatmap); - base.PostProcess(beatmap); - } - - private void applyStacking(Beatmap beatmap) - { - const int stack_distance = 3; - - // Reset stacking - for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++) - beatmap.HitObjects[i].StackHeight = 0; - - // Extend the end index to include objects they are stacked on - int extendedEndIndex = beatmap.HitObjects.Count - 1; - for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--) - { - int stackBaseIndex = i; - for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++) - { - OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex]; - if (stackBaseObject is Spinner) break; - - OsuHitObject objectN = beatmap.HitObjects[n]; - if (objectN is Spinner) - continue; - - double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime; - double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f; - - if (objectN.StartTime - endTime > stackThreshold) - //We are no longer within stacking range of the next object. - break; - - if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance || - stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance) - { - stackBaseIndex = n; - - // HitObjects after the specified update range haven't been reset yet - objectN.StackHeight = 0; - } - } - - if (stackBaseIndex > extendedEndIndex) - { - extendedEndIndex = stackBaseIndex; - if (extendedEndIndex == beatmap.HitObjects.Count - 1) - break; - } - } - - //Reverse pass for stack calculation. - int extendedStartIndex = 0; - for (int i = extendedEndIndex; i > 0; i--) - { - int n = i; - /* We should check every note which has not yet got a stack. - * Consider the case we have two interwound stacks and this will make sense. - * - * o <-1 o <-2 - * o <-3 o <-4 - * - * We first process starting from 4 and handle 2, - * then we come backwards on the i loop iteration until we reach 3 and handle 1. - * 2 and 1 will be ignored in the i loop because they already have a stack value. - */ - - OsuHitObject objectI = beatmap.HitObjects[i]; - if (objectI.StackHeight != 0 || objectI is Spinner) continue; - - double stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f; - - /* If this object is a hitcircle, then we enter this "special" case. - * It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider. - * Any other case is handled by the "is Slider" code below this. - */ - if (objectI is HitCircle) - { - while (--n >= 0) - { - OsuHitObject objectN = beatmap.HitObjects[n]; - if (objectN is Spinner) continue; - - double endTime = (objectN as IHasEndTime)?.EndTime ?? objectN.StartTime; - - if (objectI.StartTime - endTime > stackThreshold) - //We are no longer within stacking range of the previous object. - break; - - // HitObjects before the specified update range haven't been reset yet - if (n < extendedStartIndex) - { - objectN.StackHeight = 0; - extendedStartIndex = n; - } - - /* This is a special case where hticircles are moved DOWN and RIGHT (negative stacking) if they are under the *last* slider in a stacked pattern. - * o==o <- slider is at original location - * o <- hitCircle has stack of -1 - * o <- hitCircle has stack of -2 - */ - if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) - { - int offset = objectI.StackHeight - objectN.StackHeight + 1; - for (int j = n + 1; j <= i; j++) - { - //For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). - OsuHitObject objectJ = beatmap.HitObjects[j]; - if (Vector2Extensions.Distance(objectN.EndPosition, objectJ.Position) < stack_distance) - objectJ.StackHeight -= offset; - } - - //We have hit a slider. We should restart calculation using this as the new base. - //Breaking here will mean that the slider still has StackCount of 0, so will be handled in the i-outer-loop. - break; - } - - if (Vector2Extensions.Distance(objectN.Position, objectI.Position) < stack_distance) - { - //Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out. - //NOTE: Sliders with start positions stacking are a special case that is also handled here. - - objectN.StackHeight = objectI.StackHeight + 1; - objectI = objectN; - } - } - } - else if (objectI is Slider) - { - /* We have hit the first slider in a possible stack. - * From this point on, we ALWAYS stack positive regardless. - */ - while (--n >= 0) - { - OsuHitObject objectN = beatmap.HitObjects[n]; - if (objectN is Spinner) continue; - - if (objectI.StartTime - objectN.StartTime > stackThreshold) - //We are no longer within stacking range of the previous object. - break; - - if (Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) - { - objectN.StackHeight = objectI.StackHeight + 1; - objectI = objectN; - } - } - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Beatmaps +{ + internal class OsuBeatmapProcessor : BeatmapProcessor + { + public override void PostProcess(Beatmap beatmap) + { + applyStacking(beatmap); + base.PostProcess(beatmap); + } + + private void applyStacking(Beatmap beatmap) + { + const int stack_distance = 3; + + // Reset stacking + for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++) + beatmap.HitObjects[i].StackHeight = 0; + + // Extend the end index to include objects they are stacked on + int extendedEndIndex = beatmap.HitObjects.Count - 1; + for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--) + { + int stackBaseIndex = i; + for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++) + { + OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex]; + if (stackBaseObject is Spinner) break; + + OsuHitObject objectN = beatmap.HitObjects[n]; + if (objectN is Spinner) + continue; + + double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime; + double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f; + + if (objectN.StartTime - endTime > stackThreshold) + //We are no longer within stacking range of the next object. + break; + + if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance || + stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance) + { + stackBaseIndex = n; + + // HitObjects after the specified update range haven't been reset yet + objectN.StackHeight = 0; + } + } + + if (stackBaseIndex > extendedEndIndex) + { + extendedEndIndex = stackBaseIndex; + if (extendedEndIndex == beatmap.HitObjects.Count - 1) + break; + } + } + + //Reverse pass for stack calculation. + int extendedStartIndex = 0; + for (int i = extendedEndIndex; i > 0; i--) + { + int n = i; + /* We should check every note which has not yet got a stack. + * Consider the case we have two interwound stacks and this will make sense. + * + * o <-1 o <-2 + * o <-3 o <-4 + * + * We first process starting from 4 and handle 2, + * then we come backwards on the i loop iteration until we reach 3 and handle 1. + * 2 and 1 will be ignored in the i loop because they already have a stack value. + */ + + OsuHitObject objectI = beatmap.HitObjects[i]; + if (objectI.StackHeight != 0 || objectI is Spinner) continue; + + double stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f; + + /* If this object is a hitcircle, then we enter this "special" case. + * It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider. + * Any other case is handled by the "is Slider" code below this. + */ + if (objectI is HitCircle) + { + while (--n >= 0) + { + OsuHitObject objectN = beatmap.HitObjects[n]; + if (objectN is Spinner) continue; + + double endTime = (objectN as IHasEndTime)?.EndTime ?? objectN.StartTime; + + if (objectI.StartTime - endTime > stackThreshold) + //We are no longer within stacking range of the previous object. + break; + + // HitObjects before the specified update range haven't been reset yet + if (n < extendedStartIndex) + { + objectN.StackHeight = 0; + extendedStartIndex = n; + } + + /* This is a special case where hticircles are moved DOWN and RIGHT (negative stacking) if they are under the *last* slider in a stacked pattern. + * o==o <- slider is at original location + * o <- hitCircle has stack of -1 + * o <- hitCircle has stack of -2 + */ + if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) + { + int offset = objectI.StackHeight - objectN.StackHeight + 1; + for (int j = n + 1; j <= i; j++) + { + //For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). + OsuHitObject objectJ = beatmap.HitObjects[j]; + if (Vector2Extensions.Distance(objectN.EndPosition, objectJ.Position) < stack_distance) + objectJ.StackHeight -= offset; + } + + //We have hit a slider. We should restart calculation using this as the new base. + //Breaking here will mean that the slider still has StackCount of 0, so will be handled in the i-outer-loop. + break; + } + + if (Vector2Extensions.Distance(objectN.Position, objectI.Position) < stack_distance) + { + //Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out. + //NOTE: Sliders with start positions stacking are a special case that is also handled here. + + objectN.StackHeight = objectI.StackHeight + 1; + objectI = objectN; + } + } + } + else if (objectI is Slider) + { + /* We have hit the first slider in a possible stack. + * From this point on, we ALWAYS stack positive regardless. + */ + while (--n >= 0) + { + OsuHitObject objectN = beatmap.HitObjects[n]; + if (objectN is Spinner) continue; + + if (objectI.StartTime - objectN.StartTime > stackThreshold) + //We are no longer within stacking range of the previous object. + break; + + if (Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) + { + objectN.StackHeight = objectI.StackHeight + 1; + objectI = objectN; + } + } + } + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs index 89a7686581..a2aa639004 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs @@ -1,37 +1,37 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; - -namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays -{ - public class HitCircleMask : HitObjectMask - { - public HitCircleMask(DrawableHitCircle hitCircle) - : base(hitCircle) - { - Origin = Anchor.Centre; - - Position = hitCircle.Position; - Size = hitCircle.Size; - Scale = hitCircle.Scale; - - CornerRadius = Size.X / 2; - - AddInternal(new RingPiece()); - - hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = colours.Yellow; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays +{ + public class HitCircleMask : HitObjectMask + { + public HitCircleMask(DrawableHitCircle hitCircle) + : base(hitCircle) + { + Origin = Anchor.Centre; + + Position = hitCircle.Position; + Size = hitCircle.Size; + Scale = hitCircle.Scale; + + CornerRadius = Size.X / 2; + + AddInternal(new RingPiece()); + + hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Yellow; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs index 96ff14205e..adb28289cf 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs @@ -1,61 +1,61 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays -{ - public class SliderCircleMask : HitObjectMask - { - public SliderCircleMask(DrawableHitCircle sliderHead, DrawableSlider slider) - : this(sliderHead, Vector2.Zero, slider) - { - } - - public SliderCircleMask(DrawableSliderTail sliderTail, DrawableSlider slider) - : this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider) - { - } - - private readonly DrawableOsuHitObject hitObject; - - private SliderCircleMask(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider) - : base(hitObject) - { - this.hitObject = hitObject; - - Origin = Anchor.Centre; - - Position = position; - Size = slider.HeadCircle.Size; - Scale = slider.HeadCircle.Scale; - - AddInternal(new RingPiece()); - - Select(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = colours.Yellow; - } - - protected override void Update() - { - base.Update(); - - RelativeAnchorPosition = hitObject.RelativeAnchorPosition; - } - - // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input. - public override bool HandleMouseInput => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays +{ + public class SliderCircleMask : HitObjectMask + { + public SliderCircleMask(DrawableHitCircle sliderHead, DrawableSlider slider) + : this(sliderHead, Vector2.Zero, slider) + { + } + + public SliderCircleMask(DrawableSliderTail sliderTail, DrawableSlider slider) + : this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider) + { + } + + private readonly DrawableOsuHitObject hitObject; + + private SliderCircleMask(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider) + : base(hitObject) + { + this.hitObject = hitObject; + + Origin = Anchor.Centre; + + Position = position; + Size = slider.HeadCircle.Size; + Scale = slider.HeadCircle.Scale; + + AddInternal(new RingPiece()); + + Select(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Yellow; + } + + protected override void Update() + { + base.Update(); + + RelativeAnchorPosition = hitObject.RelativeAnchorPosition; + } + + // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input. + public override bool HandleMouseInput => false; + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs index 629bce1847..0f6143a83d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs @@ -1,67 +1,67 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Primitives; -using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays -{ - public class SliderMask : HitObjectMask - { - private readonly SliderBody body; - private readonly DrawableSlider slider; - - public SliderMask(DrawableSlider slider) - : base(slider) - { - this.slider = slider; - - Position = slider.Position; - - var sliderObject = (Slider)slider.HitObject; - - InternalChildren = new Drawable[] - { - body = new SliderBody(sliderObject) - { - AccentColour = Color4.Transparent, - PathWidth = sliderObject.Scale * 64 - }, - new SliderCircleMask(slider.HeadCircle, slider), - new SliderCircleMask(slider.TailCircle, slider), - }; - - sliderObject.PositionChanged += _ => Position = slider.Position; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - body.BorderColour = colours.Yellow; - } - - protected override void Update() - { - base.Update(); - - Size = slider.Size; - OriginPosition = slider.OriginPosition; - - // Need to cause one update - body.UpdateProgress(0); - } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos); - - public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition); - public override Quad SelectionQuad => body.PathDrawQuad; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; +using osu.Game.Graphics; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays +{ + public class SliderMask : HitObjectMask + { + private readonly SliderBody body; + private readonly DrawableSlider slider; + + public SliderMask(DrawableSlider slider) + : base(slider) + { + this.slider = slider; + + Position = slider.Position; + + var sliderObject = (Slider)slider.HitObject; + + InternalChildren = new Drawable[] + { + body = new SliderBody(sliderObject) + { + AccentColour = Color4.Transparent, + PathWidth = sliderObject.Scale * 64 + }, + new SliderCircleMask(slider.HeadCircle, slider), + new SliderCircleMask(slider.TailCircle, slider), + }; + + sliderObject.PositionChanged += _ => Position = slider.Position; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + body.BorderColour = colours.Yellow; + } + + protected override void Update() + { + base.Update(); + + Size = slider.Size; + OriginPosition = slider.OriginPosition; + + // Need to cause one update + body.UpdateProgress(0); + } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos); + + public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition); + public override Quad SelectionQuad => body.PathDrawQuad; + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs index 46a3d8575f..3518e030b6 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Osu.UI; - -namespace osu.Game.Rulesets.Osu.Edit -{ - public class OsuEditPlayfield : OsuPlayfield - { - protected override bool ProxyApproachCircles => false; - protected override bool DisplayJudgements => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Osu.UI; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuEditPlayfield : OsuPlayfield + { + protected override bool ProxyApproachCircles => false; + protected override bool DisplayJudgements => false; + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs index a8d895bc1d..8d4c342740 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Cursor; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.UI; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Edit -{ - public class OsuEditRulesetContainer : OsuRulesetContainer - { - public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) - { - } - - protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); - - protected override Vector2 PlayfieldArea => Vector2.One; - - protected override CursorContainer CreateCursor() => null; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Cursor; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuEditRulesetContainer : OsuRulesetContainer + { + public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + } + + protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); + + protected override Vector2 PlayfieldArea => Vector2.One; + + protected override CursorContainer CreateCursor() => null; + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 026c85d909..7bf0651443 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -1,49 +1,49 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Osu.Edit -{ - public class OsuHitObjectComposer : HitObjectComposer - { - public OsuHitObjectComposer(Ruleset ruleset) - : base(ruleset) - { - } - - protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap, true); - - protected override IReadOnlyList CompositionTools => new ICompositionTool[] - { - new HitObjectCompositionTool(), - new HitObjectCompositionTool(), - new HitObjectCompositionTool() - }; - - protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; - - public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject) - { - switch (hitObject) - { - case DrawableHitCircle circle: - return new HitCircleMask(circle); - case DrawableSlider slider: - return new SliderMask(slider); - } - - return base.CreateMaskFor(hitObject); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuHitObjectComposer : HitObjectComposer + { + public OsuHitObjectComposer(Ruleset ruleset) + : base(ruleset) + { + } + + protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap, true); + + protected override IReadOnlyList CompositionTools => new ICompositionTool[] + { + new HitObjectCompositionTool(), + new HitObjectCompositionTool(), + new HitObjectCompositionTool() + }; + + protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; + + public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject) + { + switch (hitObject) + { + case DrawableHitCircle circle: + return new HitCircleMask(circle); + case DrawableSlider slider: + return new SliderMask(slider); + } + + return base.CreateMaskFor(hitObject); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs index b2d9844613..b7c4470592 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs @@ -1,37 +1,37 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Judgements -{ - public class OsuJudgement : Judgement - { - public override HitResult MaxResult => HitResult.Great; - - /// - /// The positional hit offset. - /// - public Vector2 PositionOffset; - - protected override int NumericResultFor(HitResult result) - { - switch (result) - { - default: - return 0; - case HitResult.Meh: - return 50; - case HitResult.Good: - return 100; - case HitResult.Great: - return 300; - } - } - - public ComboResult Combo; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Judgements +{ + public class OsuJudgement : Judgement + { + public override HitResult MaxResult => HitResult.Great; + + /// + /// The positional hit offset. + /// + public Vector2 PositionOffset; + + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Meh: + return 50; + case HitResult.Good: + return 100; + case HitResult.Great: + return 300; + } + } + + public ComboResult Combo; + } +} diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs index a6e67ea979..c4e265aac9 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Judgements -{ - public class OsuSliderTailJudgement : OsuJudgement - { - public override bool AffectsCombo => false; - protected override int NumericResultFor(HitResult result) => 0; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Judgements +{ + public class OsuSliderTailJudgement : OsuJudgement + { + public override bool AffectsCombo => false; + protected override int NumericResultFor(HitResult result) => 0; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 0c842143e4..3573c133c2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModAutopilot : Mod - { - public override string Name => "Autopilot"; - public override string ShortenedName => "AP"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot; - public override string Description => @"Automatic cursor movement - just follow the rhythm."; - public override double ScoreMultiplier => 0; - public override bool Ranked => false; - public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModAutopilot : Mod + { + public override string Name => "Autopilot"; + public override string ShortenedName => "AP"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot; + public override string Description => @"Automatic cursor movement - just follow the rhythm."; + public override double ScoreMultiplier => 0; + public override bool Ranked => false; + public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) }; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs index a06390a4ea..1eb2f59010 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Replays; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModAutoplay : ModAutoplay - { - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray(); - - protected override Score CreateReplayScore(Beatmap beatmap) - { - return new Score - { - Replay = new OsuAutoGenerator(beatmap).Generate() - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModAutoplay : ModAutoplay + { + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray(); + + protected override Score CreateReplayScore(Beatmap beatmap) + { + return new Score + { + Replay = new OsuAutoGenerator(beatmap).Generate() + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs index 987bb28932..ff3f6ea7cf 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModDaycore : ModDaycore - { - public override double ScoreMultiplier => 0.3; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModDaycore : ModDaycore + { + public override double ScoreMultiplier => 0.3; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs index 5a835aac75..45b538d506 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModDoubleTime : ModDoubleTime - { - public override double ScoreMultiplier => 1.12; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1.12; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs b/osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs index d842b607c6..465775b175 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModEasy.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModEasy : ModEasy - { - public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModEasy : ModEasy + { + public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!"; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 342c53b41f..a337439593 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModFlashlight : ModFlashlight - { - public override double ScoreMultiplier => 1.12; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModFlashlight : ModFlashlight + { + public override double ScoreMultiplier => 1.12; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs index 1b9291bcf3..dc3ba592fb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModHalfTime : ModHalfTime - { - public override double ScoreMultiplier => 0.3; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModHalfTime : ModHalfTime + { + public override double ScoreMultiplier => 0.3; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 29bf3e248d..cf71116d47 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -1,40 +1,40 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.UI; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModHardRock : ModHardRock, IApplicableToHitObject - { - public override double ScoreMultiplier => 1.06; - public override bool Ranked => true; - - public void ApplyToHitObject(OsuHitObject hitObject) - { - hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y); - - var slider = hitObject as Slider; - if (slider == null) - return; - - slider.HeadCircle.Position = new Vector2(slider.HeadCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.HeadCircle.Position.Y); - slider.TailCircle.Position = new Vector2(slider.TailCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.TailCircle.Position.Y); - - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - - var newControlPoints = new List(); - slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y))); - - slider.ControlPoints = newControlPoints; - slider.Curve?.Calculate(); // Recalculate the slider curve - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModHardRock : ModHardRock, IApplicableToHitObject + { + public override double ScoreMultiplier => 1.06; + public override bool Ranked => true; + + public void ApplyToHitObject(OsuHitObject hitObject) + { + hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y); + + var slider = hitObject as Slider; + if (slider == null) + return; + + slider.HeadCircle.Position = new Vector2(slider.HeadCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.HeadCircle.Position.Y); + slider.TailCircle.Position = new Vector2(slider.TailCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.TailCircle.Position.Y); + + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); + + var newControlPoints = new List(); + slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y))); + + slider.ControlPoints = newControlPoints; + slider.Curve?.Calculate(); // Recalculate the slider curve + } + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 1117b5bbd5..e72d667c0b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -1,87 +1,87 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects - { - public override string Description => @"Play with no approach circles and fading circles/sliders."; - public override double ScoreMultiplier => 1.06; - - private const double fade_in_duration_multiplier = 0.4; - private const double fade_out_duration_multiplier = 0.3; - - public void ApplyToDrawableHitObjects(IEnumerable drawables) - { - foreach (var d in drawables.OfType()) - { - d.ApplyCustomUpdateState += ApplyHiddenState; - - d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier; - foreach (var h in d.HitObject.NestedHitObjects.OfType()) - h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier; - } - } - - protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) - { - if (!(drawable is DrawableOsuHitObject d)) - return; - - var h = d.HitObject; - - var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein; - var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier; - - // new duration from completed fade in to end (before fading out) - var longFadeDuration = ((h as IHasEndTime)?.EndTime ?? h.StartTime) - fadeOutStartTime; - - switch (drawable) - { - case DrawableHitCircle circle: - // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - circle.ApproachCircle.Hide(); - - // fade out immediately after fade in. - using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true)) - circle.FadeOut(fadeOutDuration); - - break; - case DrawableSlider slider: - using (slider.BeginAbsoluteSequence(fadeOutStartTime, true)) - slider.Body.FadeOut(longFadeDuration, Easing.Out); - - break; - case DrawableSliderTick sliderTick: - // slider ticks fade out over up to one second - var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); - - using (sliderTick.BeginAbsoluteSequence(sliderTick.HitObject.StartTime - tickFadeOutDuration, true)) - sliderTick.FadeOut(tickFadeOutDuration); - - break; - case DrawableSpinner spinner: - // hide elements we don't care about. - spinner.Disc.Hide(); - spinner.Ticks.Hide(); - spinner.Background.Hide(); - - using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true)) - spinner.FadeOut(fadeOutDuration); - - break; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects + { + public override string Description => @"Play with no approach circles and fading circles/sliders."; + public override double ScoreMultiplier => 1.06; + + private const double fade_in_duration_multiplier = 0.4; + private const double fade_out_duration_multiplier = 0.3; + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var d in drawables.OfType()) + { + d.ApplyCustomUpdateState += ApplyHiddenState; + + d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier; + foreach (var h in d.HitObject.NestedHitObjects.OfType()) + h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier; + } + } + + protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableOsuHitObject d)) + return; + + var h = d.HitObject; + + var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein; + var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier; + + // new duration from completed fade in to end (before fading out) + var longFadeDuration = ((h as IHasEndTime)?.EndTime ?? h.StartTime) - fadeOutStartTime; + + switch (drawable) + { + case DrawableHitCircle circle: + // we don't want to see the approach circle + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + circle.ApproachCircle.Hide(); + + // fade out immediately after fade in. + using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true)) + circle.FadeOut(fadeOutDuration); + + break; + case DrawableSlider slider: + using (slider.BeginAbsoluteSequence(fadeOutStartTime, true)) + slider.Body.FadeOut(longFadeDuration, Easing.Out); + + break; + case DrawableSliderTick sliderTick: + // slider ticks fade out over up to one second + var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); + + using (sliderTick.BeginAbsoluteSequence(sliderTick.HitObject.StartTime - tickFadeOutDuration, true)) + sliderTick.FadeOut(tickFadeOutDuration); + + break; + case DrawableSpinner spinner: + // hide elements we don't care about. + spinner.Disc.Hide(); + spinner.Ticks.Hide(); + spinner.Background.Hide(); + + using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true)) + spinner.FadeOut(fadeOutDuration); + + break; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs index aa0acff68d..9c1b2d8a35 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModNightcore : ModNightcore - { - public override double ScoreMultiplier => 1.12; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModNightcore : ModNightcore + { + public override double ScoreMultiplier => 1.12; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs index 07128cb8ff..a51cb9d888 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModNoFail : ModNoFail - { - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModNoFail : ModNoFail + { + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs b/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs index 886048cd30..5169c8186b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModPerfect : ModPerfect - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModPerfect : ModPerfect + { + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index ed774f0d0a..9f9c2a09b6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModRelax : ModRelax - { - public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModRelax : ModRelax + { + public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index 401e56a3c8..d14af57bab 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModSpunOut : Mod - { - public override string Name => "Spun Out"; - public override string ShortenedName => "SO"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout; - public override string Description => @"Spinners will be automatically completed."; - public override double ScoreMultiplier => 0.9; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModSpunOut : Mod + { + public override string Name => "Spun Out"; + public override string ShortenedName => "SO"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout; + public override string Description => @"Spinners will be automatically completed."; + public override double ScoreMultiplier => 0.9; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) }; + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs index 6c15095bfe..fdd5099ad6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModSuddenDeath : ModSuddenDeath - { - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModSuddenDeath : ModSuddenDeath + { + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 613fbc4e32..ce53857a09 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Osu.Mods -{ - public class OsuModTarget : Mod - { - public override string Name => "Target"; - public override string ShortenedName => "TP"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_target; - public override string Description => @"Practice keeping up with the beat of the song."; - public override double ScoreMultiplier => 1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModTarget : Mod + { + public override string Name => "Target"; + public override string ShortenedName => "TP"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_target; + 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/Objects/Drawables/Connections/ConnectionRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/ConnectionRenderer.cs index c7dd12cb0c..f15be94b8a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/ConnectionRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/ConnectionRenderer.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections -{ - /// - /// Connects hit objects visually, for example with follow points. - /// - public abstract class ConnectionRenderer : Container - where T : HitObject - { - /// - /// Hit objects to create connections for - /// - public abstract IEnumerable HitObjects { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections +{ + /// + /// Connects hit objects visually, for example with follow points. + /// + public abstract class ConnectionRenderer : Container + where T : HitObject + { + /// + /// Hit objects to create connections for + /// + public abstract IEnumerable HitObjects { get; set; } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs index 8a93f7151f..2c89ddc9cf 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections -{ - public class FollowPoint : Container - { - private const float width = 8; - - public override bool RemoveWhenNotAlive => false; - - public FollowPoint() - { - Origin = Anchor.Centre; - - Masking = true; - AutoSizeAxes = Axes.Both; - CornerRadius = width / 2; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Color4.White.Opacity(0.2f), - Radius = 4, - }; - - Children = new Drawable[] - { - new Box - { - Size = new Vector2(width), - Blending = BlendingMode.Additive, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Alpha = 0.5f, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections +{ + public class FollowPoint : Container + { + private const float width = 8; + + public override bool RemoveWhenNotAlive => false; + + public FollowPoint() + { + Origin = Anchor.Centre; + + Masking = true; + AutoSizeAxes = Axes.Both; + CornerRadius = width / 2; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Color4.White.Opacity(0.2f), + Radius = 4, + }; + + Children = new Drawable[] + { + new Box + { + Size = new Vector2(width), + Blending = BlendingMode.Additive, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Alpha = 0.5f, + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index d501df492d..4653f45149 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -1,114 +1,114 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using OpenTK; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections -{ - public class FollowPointRenderer : ConnectionRenderer - { - private int pointDistance = 32; - /// - /// Determines how much space there is between points. - /// - public int PointDistance - { - get { return pointDistance; } - set - { - if (pointDistance == value) return; - pointDistance = value; - update(); - } - } - - private int preEmpt = 800; - /// - /// Follow points to the next hitobject start appearing for this many milliseconds before an hitobject's end time. - /// - public int PreEmpt - { - get { return preEmpt; } - set - { - if (preEmpt == value) return; - preEmpt = value; - update(); - } - } - - private IEnumerable hitObjects; - public override IEnumerable HitObjects - { - get { return hitObjects; } - set - { - hitObjects = value; - update(); - } - } - - public override bool RemoveCompletedTransforms => false; - - private void update() - { - Clear(); - - if (hitObjects == null) - return; - - OsuHitObject prevHitObject = null; - foreach (var currHitObject in hitObjects) - { - if (prevHitObject != null && !currHitObject.NewCombo && !(prevHitObject is Spinner) && !(currHitObject is Spinner)) - { - Vector2 startPosition = prevHitObject.EndPosition; - Vector2 endPosition = currHitObject.Position; - double startTime = (prevHitObject as IHasEndTime)?.EndTime ?? prevHitObject.StartTime; - double endTime = currHitObject.StartTime; - - Vector2 distanceVector = endPosition - startPosition; - int distance = (int)distanceVector.Length; - float rotation = (float)Math.Atan2(distanceVector.Y, distanceVector.X); - double duration = endTime - startTime; - - for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance) - { - float fraction = (float)d / distance; - Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector; - Vector2 pointEndPosition = startPosition + fraction * distanceVector; - double fadeOutTime = startTime + fraction * duration; - double fadeInTime = fadeOutTime - PreEmpt; - - FollowPoint fp; - - Add(fp = new FollowPoint - { - Position = pointStartPosition, - Rotation = rotation, - Alpha = 0, - Scale = new Vector2(1.5f), - }); - - using (fp.BeginAbsoluteSequence(fadeInTime)) - { - fp.FadeIn(currHitObject.TimeFadein); - fp.ScaleTo(1, currHitObject.TimeFadein, Easing.Out); - - fp.MoveTo(pointEndPosition, currHitObject.TimeFadein, Easing.Out); - - fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadein); - } - - fp.Expire(true); - } - } - prevHitObject = currHitObject; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using OpenTK; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections +{ + public class FollowPointRenderer : ConnectionRenderer + { + private int pointDistance = 32; + /// + /// Determines how much space there is between points. + /// + public int PointDistance + { + get { return pointDistance; } + set + { + if (pointDistance == value) return; + pointDistance = value; + update(); + } + } + + private int preEmpt = 800; + /// + /// Follow points to the next hitobject start appearing for this many milliseconds before an hitobject's end time. + /// + public int PreEmpt + { + get { return preEmpt; } + set + { + if (preEmpt == value) return; + preEmpt = value; + update(); + } + } + + private IEnumerable hitObjects; + public override IEnumerable HitObjects + { + get { return hitObjects; } + set + { + hitObjects = value; + update(); + } + } + + public override bool RemoveCompletedTransforms => false; + + private void update() + { + Clear(); + + if (hitObjects == null) + return; + + OsuHitObject prevHitObject = null; + foreach (var currHitObject in hitObjects) + { + if (prevHitObject != null && !currHitObject.NewCombo && !(prevHitObject is Spinner) && !(currHitObject is Spinner)) + { + Vector2 startPosition = prevHitObject.EndPosition; + Vector2 endPosition = currHitObject.Position; + double startTime = (prevHitObject as IHasEndTime)?.EndTime ?? prevHitObject.StartTime; + double endTime = currHitObject.StartTime; + + Vector2 distanceVector = endPosition - startPosition; + int distance = (int)distanceVector.Length; + float rotation = (float)Math.Atan2(distanceVector.Y, distanceVector.X); + double duration = endTime - startTime; + + for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance) + { + float fraction = (float)d / distance; + Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector; + Vector2 pointEndPosition = startPosition + fraction * distanceVector; + double fadeOutTime = startTime + fraction * duration; + double fadeInTime = fadeOutTime - PreEmpt; + + FollowPoint fp; + + Add(fp = new FollowPoint + { + Position = pointStartPosition, + Rotation = rotation, + Alpha = 0, + Scale = new Vector2(1.5f), + }); + + using (fp.BeginAbsoluteSequence(fadeInTime)) + { + fp.FadeIn(currHitObject.TimeFadein); + fp.ScaleTo(1, currHitObject.TimeFadein, Easing.Out); + + fp.MoveTo(pointEndPosition, currHitObject.TimeFadein, Easing.Out); + + fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadein); + } + + fp.Expire(true); + } + } + prevHitObject = currHitObject; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 9066a9ef92..9fe6dcd46c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -1,155 +1,155 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using OpenTK; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Scoring; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach - { - public ApproachCircle ApproachCircle; - private readonly CirclePiece circle; - private readonly RingPiece ring; - private readonly FlashPiece flash; - private readonly ExplodePiece explode; - private readonly NumberPiece number; - private readonly GlowPiece glow; - - public DrawableHitCircle(HitCircle h) - : base(h) - { - Origin = Anchor.Centre; - - Position = HitObject.StackedPosition; - Scale = new Vector2(h.Scale); - - InternalChildren = new Drawable[] - { - glow = new GlowPiece(), - circle = new CirclePiece - { - Hit = () => - { - if (AllJudged) - return false; - - UpdateJudgement(true); - return true; - }, - }, - number = new NumberPiece - { - Text = (HitObject.IndexInCurrentCombo + 1).ToString(), - }, - ring = new RingPiece(), - flash = new FlashPiece(), - explode = new ExplodePiece(), - ApproachCircle = new ApproachCircle - { - Alpha = 0, - Scale = new Vector2(4), - } - }; - - //may not be so correct - Size = circle.DrawSize; - - HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; - } - - public override Color4 AccentColour - { - get { return base.AccentColour; } - set - { - base.AccentColour = value; - explode.Colour = AccentColour; - glow.Colour = AccentColour; - circle.Colour = AccentColour; - ApproachCircle.Colour = AccentColour; - } - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!userTriggered) - { - if (!HitObject.HitWindows.CanBeHit(timeOffset)) - AddJudgement(new OsuJudgement { Result = HitResult.Miss }); - return; - } - - var result = HitObject.HitWindows.ResultFor(timeOffset); - if (result == HitResult.None) - return; - - AddJudgement(new OsuJudgement - { - Result = result, - PositionOffset = Vector2.Zero //todo: set to correct value - }); - } - - protected override void UpdatePreemptState() - { - base.UpdatePreemptState(); - - ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadein * 2, HitObject.TimePreempt)); - ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt); - } - - protected override void UpdateCurrentState(ArmedState state) - { - glow.FadeOut(400); - - switch (state) - { - case ArmedState.Idle: - this.Delay(HitObject.TimePreempt).FadeOut(500); - - Expire(true); - - // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. - LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); - break; - case ArmedState.Miss: - ApproachCircle.FadeOut(50); - this.FadeOut(100); - Expire(); - break; - case ArmedState.Hit: - ApproachCircle.FadeOut(50); - - const double flash_in = 40; - flash.FadeTo(0.8f, flash_in) - .Then() - .FadeOut(100); - - explode.FadeIn(flash_in); - - using (BeginDelayedSequence(flash_in, true)) - { - //after the flash, we can hide some elements that were behind it - ring.FadeOut(); - circle.FadeOut(); - number.FadeOut(); - - this.FadeOut(800) - .ScaleTo(Scale * 1.5f, 400, Easing.OutQuad); - } - - Expire(); - break; - } - } - - public Drawable ProxiedLayer => ApproachCircle; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using OpenTK; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach + { + public ApproachCircle ApproachCircle; + private readonly CirclePiece circle; + private readonly RingPiece ring; + private readonly FlashPiece flash; + private readonly ExplodePiece explode; + private readonly NumberPiece number; + private readonly GlowPiece glow; + + public DrawableHitCircle(HitCircle h) + : base(h) + { + Origin = Anchor.Centre; + + Position = HitObject.StackedPosition; + Scale = new Vector2(h.Scale); + + InternalChildren = new Drawable[] + { + glow = new GlowPiece(), + circle = new CirclePiece + { + Hit = () => + { + if (AllJudged) + return false; + + UpdateJudgement(true); + return true; + }, + }, + number = new NumberPiece + { + Text = (HitObject.IndexInCurrentCombo + 1).ToString(), + }, + ring = new RingPiece(), + flash = new FlashPiece(), + explode = new ExplodePiece(), + ApproachCircle = new ApproachCircle + { + Alpha = 0, + Scale = new Vector2(4), + } + }; + + //may not be so correct + Size = circle.DrawSize; + + HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; + } + + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + explode.Colour = AccentColour; + glow.Colour = AccentColour; + circle.Colour = AccentColour; + ApproachCircle.Colour = AccentColour; + } + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!userTriggered) + { + if (!HitObject.HitWindows.CanBeHit(timeOffset)) + AddJudgement(new OsuJudgement { Result = HitResult.Miss }); + return; + } + + var result = HitObject.HitWindows.ResultFor(timeOffset); + if (result == HitResult.None) + return; + + AddJudgement(new OsuJudgement + { + Result = result, + PositionOffset = Vector2.Zero //todo: set to correct value + }); + } + + protected override void UpdatePreemptState() + { + base.UpdatePreemptState(); + + ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadein * 2, HitObject.TimePreempt)); + ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt); + } + + protected override void UpdateCurrentState(ArmedState state) + { + glow.FadeOut(400); + + switch (state) + { + case ArmedState.Idle: + this.Delay(HitObject.TimePreempt).FadeOut(500); + + Expire(true); + + // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. + LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); + break; + case ArmedState.Miss: + ApproachCircle.FadeOut(50); + this.FadeOut(100); + Expire(); + break; + case ArmedState.Hit: + ApproachCircle.FadeOut(50); + + const double flash_in = 40; + flash.FadeTo(0.8f, flash_in) + .Then() + .FadeOut(100); + + explode.FadeIn(flash_in); + + using (BeginDelayedSequence(flash_in, true)) + { + //after the flash, we can hide some elements that were behind it + ring.FadeOut(); + circle.FadeOut(); + number.FadeOut(); + + this.FadeOut(800) + .ScaleTo(Scale * 1.5f, 400, Easing.OutQuad); + } + + Expire(); + break; + } + } + + public Drawable ProxiedLayer => ApproachCircle; + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index d4d89c2aa3..7c9503dfe2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -1,72 +1,72 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Framework.Graphics; -using System.Linq; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Skinning; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableOsuHitObject : DrawableHitObject - { - public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - HitObject.TimePreempt; - - protected DrawableOsuHitObject(OsuHitObject hitObject) - : base(hitObject) - { - Alpha = 0; - } - - protected sealed override void UpdateState(ArmedState state) - { - double transformTime = HitObject.StartTime - HitObject.TimePreempt; - - base.ApplyTransformsAt(transformTime, true); - base.ClearTransformsAfter(transformTime, true); - - using (BeginAbsoluteSequence(transformTime, true)) - { - UpdatePreemptState(); - - using (BeginDelayedSequence(HitObject.TimePreempt + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true)) - UpdateCurrentState(state); - } - } - - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - - if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; - } - - protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein); - - protected virtual void UpdateCurrentState(ArmedState state) - { - } - - // Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply - // transforms in the same way and don't rely on them not being cleared - public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { } - public override void ApplyTransformsAt(double time, bool propagateChildren = false) { } - - private OsuInputManager osuActionInputManager; - internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); - } - - public enum ComboResult - { - [Description(@"")] - None, - [Description(@"Good")] - Good, - [Description(@"Amazing")] - Perfect - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Framework.Graphics; +using System.Linq; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableOsuHitObject : DrawableHitObject + { + public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - HitObject.TimePreempt; + + protected DrawableOsuHitObject(OsuHitObject hitObject) + : base(hitObject) + { + Alpha = 0; + } + + protected sealed override void UpdateState(ArmedState state) + { + double transformTime = HitObject.StartTime - HitObject.TimePreempt; + + base.ApplyTransformsAt(transformTime, true); + base.ClearTransformsAfter(transformTime, true); + + using (BeginAbsoluteSequence(transformTime, true)) + { + UpdatePreemptState(); + + using (BeginDelayedSequence(HitObject.TimePreempt + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true)) + UpdateCurrentState(state); + } + } + + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + if (HitObject is IHasComboInformation combo) + AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + } + + protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein); + + protected virtual void UpdateCurrentState(ArmedState state) + { + } + + // Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply + // transforms in the same way and don't rely on them not being cleared + public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { } + public override void ApplyTransformsAt(double time, bool propagateChildren = false) { } + + private OsuInputManager osuActionInputManager; + internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); + } + + public enum ComboResult + { + [Description(@"")] + None, + [Description(@"Good")] + Good, + [Description(@"Amazing")] + Perfect + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 1468c82b57..e8743281da 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using OpenTK; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableOsuJudgement : DrawableJudgement - { - public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject) - : base(judgement, judgedObject) - { - } - - protected override void LoadComplete() - { - if (Judgement.Result != HitResult.Miss) - JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); - - base.LoadComplete(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using OpenTK; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableOsuJudgement : DrawableJudgement + { + public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject) + : base(judgement, judgedObject) + { + } + + protected override void LoadComplete() + { + if (Judgement.Result != HitResult.Miss) + JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); + + base.LoadComplete(); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 94179f30d3..b3bc2930d8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -1,100 +1,100 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; -using osu.Game.Graphics; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableRepeatPoint : DrawableOsuHitObject, ITrackSnaking - { - private readonly RepeatPoint repeatPoint; - private readonly DrawableSlider drawableSlider; - - private double animDuration; - - public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) - : base(repeatPoint) - { - this.repeatPoint = repeatPoint; - this.drawableSlider = drawableSlider; - - Size = new Vector2(45 * repeatPoint.Scale); - - Blending = BlendingMode.Additive; - Origin = Anchor.Centre; - - InternalChildren = new Drawable[] - { - new SpriteIcon - { - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.fa_chevron_right - } - }; - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (repeatPoint.StartTime <= Time.Current) - AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss }); - } - - protected override void UpdatePreemptState() - { - animDuration = Math.Min(150, repeatPoint.SpanDuration / 2); - - this.Animate( - d => d.FadeIn(animDuration), - d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf) - ); - } - - protected override void UpdateCurrentState(ArmedState state) - { - switch (state) - { - case ArmedState.Idle: - this.Delay(HitObject.TimePreempt).FadeOut(); - break; - case ArmedState.Miss: - this.FadeOut(animDuration); - break; - case ArmedState.Hit: - this.FadeOut(animDuration, Easing.OutQuint) - .ScaleTo(Scale * 1.5f, animDuration, Easing.Out); - break; - } - } - - public void UpdateSnakingPosition(Vector2 start, Vector2 end) - { - bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0; - List curve = drawableSlider.Body.CurrentCurve; - - Position = isRepeatAtEnd ? end : start; - - if (curve.Count < 2) - return; - - int searchStart = isRepeatAtEnd ? curve.Count - 1 : 0; - int direction = isRepeatAtEnd ? -1 : 1; - - // find the next vector2 in the curve which is not equal to our current position to infer a rotation. - for (int i = searchStart; i >= 0 && i < curve.Count; i += direction) - { - if (curve[i] == Position) - continue; - - Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X)); - break; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; +using osu.Game.Graphics; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableRepeatPoint : DrawableOsuHitObject, ITrackSnaking + { + private readonly RepeatPoint repeatPoint; + private readonly DrawableSlider drawableSlider; + + private double animDuration; + + public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) + : base(repeatPoint) + { + this.repeatPoint = repeatPoint; + this.drawableSlider = drawableSlider; + + Size = new Vector2(45 * repeatPoint.Scale); + + Blending = BlendingMode.Additive; + Origin = Anchor.Centre; + + InternalChildren = new Drawable[] + { + new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_chevron_right + } + }; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (repeatPoint.StartTime <= Time.Current) + AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss }); + } + + protected override void UpdatePreemptState() + { + animDuration = Math.Min(150, repeatPoint.SpanDuration / 2); + + this.Animate( + d => d.FadeIn(animDuration), + d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf) + ); + } + + protected override void UpdateCurrentState(ArmedState state) + { + switch (state) + { + case ArmedState.Idle: + this.Delay(HitObject.TimePreempt).FadeOut(); + break; + case ArmedState.Miss: + this.FadeOut(animDuration); + break; + case ArmedState.Hit: + this.FadeOut(animDuration, Easing.OutQuint) + .ScaleTo(Scale * 1.5f, animDuration, Easing.Out); + break; + } + } + + public void UpdateSnakingPosition(Vector2 start, Vector2 end) + { + bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0; + List curve = drawableSlider.Body.CurrentCurve; + + Position = isRepeatAtEnd ? end : start; + + if (curve.Count < 2) + return; + + int searchStart = isRepeatAtEnd ? curve.Count - 1 : 0; + int direction = isRepeatAtEnd ? -1 : 1; + + // find the next vector2 in the curve which is not equal to our current position to infer a rotation. + for (int i = searchStart; i >= 0 && i < curve.Count; i += direction) + { + if (curve[i] == Position) + continue; + + Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X)); + break; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 5373926138..dfab123038 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -1,180 +1,180 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Configuration; -using osu.Game.Rulesets.Scoring; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach - { - private readonly Slider slider; - private readonly List components = new List(); - - public readonly DrawableHitCircle HeadCircle; - public readonly DrawableSliderTail TailCircle; - - public readonly SliderBody Body; - public readonly SliderBall Ball; - - public DrawableSlider(Slider s) - : base(s) - { - slider = s; - - Position = s.StackedPosition; - - Container ticks; - Container repeatPoints; - - InternalChildren = new Drawable[] - { - Body = new SliderBody(s) - { - PathWidth = s.Scale * 64, - }, - ticks = new Container { RelativeSizeAxes = Axes.Both }, - repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, - Ball = new SliderBall(s) - { - BypassAutoSizeAxes = Axes.Both, - Scale = new Vector2(s.Scale), - AlwaysPresent = true, - Alpha = 0 - }, - HeadCircle = new DrawableSliderHead(s, s.HeadCircle), - TailCircle = new DrawableSliderTail(s, s.TailCircle) - }; - - components.Add(Body); - components.Add(Ball); - - AddNested(HeadCircle); - - AddNested(TailCircle); - components.Add(TailCircle); - - foreach (var tick in s.NestedHitObjects.OfType()) - { - var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position }; - - ticks.Add(drawableTick); - components.Add(drawableTick); - AddNested(drawableTick); - } - - foreach (var repeatPoint in s.NestedHitObjects.OfType()) - { - var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position }; - - repeatPoints.Add(drawableRepeatPoint); - components.Add(drawableRepeatPoint); - AddNested(drawableRepeatPoint); - } - - HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; - } - - public override Color4 AccentColour - { - get { return base.AccentColour; } - set - { - base.AccentColour = value; - Body.AccentColour = AccentColour; - Ball.AccentColour = AccentColour; - } - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn); - config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut); - } - - public bool Tracking; - - protected override void Update() - { - base.Update(); - - Tracking = Ball.Tracking; - - double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); - - foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); - foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); - foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; - - Size = Body.Size; - OriginPosition = Body.PathOffset; - - if (DrawSize != Vector2.Zero) - { - var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize); - foreach (var obj in NestedHitObjects) - obj.RelativeAnchorPosition = childAnchorPosition; - Ball.RelativeAnchorPosition = childAnchorPosition; - } - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!userTriggered && Time.Current >= slider.EndTime) - { - var judgementsCount = NestedHitObjects.Count; - var judgementsHit = NestedHitObjects.Count(h => h.IsHit); - - var hitFraction = (double)judgementsHit / judgementsCount; - if (hitFraction == 1 && HeadCircle.Judgements.Any(j => j.Result == HitResult.Great)) - AddJudgement(new OsuJudgement { Result = HitResult.Great }); - else if (hitFraction >= 0.5 && HeadCircle.Judgements.Any(j => j.Result >= HitResult.Good)) - AddJudgement(new OsuJudgement { Result = HitResult.Good }); - else if (hitFraction > 0) - AddJudgement(new OsuJudgement { Result = HitResult.Meh }); - else - AddJudgement(new OsuJudgement { Result = HitResult.Miss }); - } - } - - protected override void UpdateCurrentState(ArmedState state) - { - Ball.FadeIn(); - Ball.ScaleTo(HitObject.Scale); - - using (BeginDelayedSequence(slider.Duration, true)) - { - const float fade_out_time = 450; - - // intentionally pile on an extra FadeOut to make it happen much faster. - Ball.FadeOut(fade_out_time / 4, Easing.Out); - - switch (state) - { - case ArmedState.Hit: - Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out); - break; - } - - this.FadeOut(fade_out_time, Easing.OutQuint).Expire(); - } - - Expire(true); - } - - public Drawable ProxiedLayer => HeadCircle.ApproachCircle; - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Configuration; +using osu.Game.Rulesets.Scoring; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach + { + private readonly Slider slider; + private readonly List components = new List(); + + public readonly DrawableHitCircle HeadCircle; + public readonly DrawableSliderTail TailCircle; + + public readonly SliderBody Body; + public readonly SliderBall Ball; + + public DrawableSlider(Slider s) + : base(s) + { + slider = s; + + Position = s.StackedPosition; + + Container ticks; + Container repeatPoints; + + InternalChildren = new Drawable[] + { + Body = new SliderBody(s) + { + PathWidth = s.Scale * 64, + }, + ticks = new Container { RelativeSizeAxes = Axes.Both }, + repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, + Ball = new SliderBall(s) + { + BypassAutoSizeAxes = Axes.Both, + Scale = new Vector2(s.Scale), + AlwaysPresent = true, + Alpha = 0 + }, + HeadCircle = new DrawableSliderHead(s, s.HeadCircle), + TailCircle = new DrawableSliderTail(s, s.TailCircle) + }; + + components.Add(Body); + components.Add(Ball); + + AddNested(HeadCircle); + + AddNested(TailCircle); + components.Add(TailCircle); + + foreach (var tick in s.NestedHitObjects.OfType()) + { + var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position }; + + ticks.Add(drawableTick); + components.Add(drawableTick); + AddNested(drawableTick); + } + + foreach (var repeatPoint in s.NestedHitObjects.OfType()) + { + var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position }; + + repeatPoints.Add(drawableRepeatPoint); + components.Add(drawableRepeatPoint); + AddNested(drawableRepeatPoint); + } + + HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; + } + + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + Body.AccentColour = AccentColour; + Ball.AccentColour = AccentColour; + } + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn); + config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut); + } + + public bool Tracking; + + protected override void Update() + { + base.Update(); + + Tracking = Ball.Tracking; + + double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); + + foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); + foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); + foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; + + Size = Body.Size; + OriginPosition = Body.PathOffset; + + if (DrawSize != Vector2.Zero) + { + var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize); + foreach (var obj in NestedHitObjects) + obj.RelativeAnchorPosition = childAnchorPosition; + Ball.RelativeAnchorPosition = childAnchorPosition; + } + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!userTriggered && Time.Current >= slider.EndTime) + { + var judgementsCount = NestedHitObjects.Count; + var judgementsHit = NestedHitObjects.Count(h => h.IsHit); + + var hitFraction = (double)judgementsHit / judgementsCount; + if (hitFraction == 1 && HeadCircle.Judgements.Any(j => j.Result == HitResult.Great)) + AddJudgement(new OsuJudgement { Result = HitResult.Great }); + else if (hitFraction >= 0.5 && HeadCircle.Judgements.Any(j => j.Result >= HitResult.Good)) + AddJudgement(new OsuJudgement { Result = HitResult.Good }); + else if (hitFraction > 0) + AddJudgement(new OsuJudgement { Result = HitResult.Meh }); + else + AddJudgement(new OsuJudgement { Result = HitResult.Miss }); + } + } + + protected override void UpdateCurrentState(ArmedState state) + { + Ball.FadeIn(); + Ball.ScaleTo(HitObject.Scale); + + using (BeginDelayedSequence(slider.Duration, true)) + { + const float fade_out_time = 450; + + // intentionally pile on an extra FadeOut to make it happen much faster. + Ball.FadeOut(fade_out_time / 4, Easing.Out); + + switch (state) + { + case ArmedState.Hit: + Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out); + break; + } + + this.FadeOut(fade_out_time, Easing.OutQuint).Expire(); + } + + Expire(true); + } + + public Drawable ProxiedLayer => HeadCircle.ApproachCircle; + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos); + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index cf36d5fc14..e823c870f9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -1,32 +1,32 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableSliderHead : DrawableHitCircle - { - private readonly Slider slider; - - public DrawableSliderHead(Slider slider, HitCircle h) - : base(h) - { - this.slider = slider; - - Position = HitObject.Position - slider.Position; - } - - protected override void Update() - { - base.Update(); - - double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); - - //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. - if (!IsHit) - Position = slider.CurvePositionAt(completionProgress); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableSliderHead : DrawableHitCircle + { + private readonly Slider slider; + + public DrawableSliderHead(Slider slider, HitCircle h) + : base(h) + { + this.slider = slider; + + Position = HitObject.Position - slider.Position; + } + + protected override void Update() + { + base.Update(); + + double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); + + //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. + if (!IsHit) + Position = slider.CurvePositionAt(completionProgress); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index b277e7df7a..fee663963e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -1,38 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking - { - /// - /// The judgement text is provided by the . - /// - public override bool DisplayJudgement => false; - - public bool Tracking { get; set; } - - public DrawableSliderTail(Slider slider, HitCircle hitCircle) - : base(hitCircle) - { - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - FillMode = FillMode.Fit; - - AlwaysPresent = true; - - Position = HitObject.Position - slider.Position; - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!userTriggered && timeOffset >= 0) - AddJudgement(new OsuSliderTailJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking + { + /// + /// The judgement text is provided by the . + /// + public override bool DisplayJudgement => false; + + public bool Tracking { get; set; } + + public DrawableSliderTail(Slider slider, HitCircle hitCircle) + : base(hitCircle) + { + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + + AlwaysPresent = true; + + Position = HitObject.Position - slider.Position; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!userTriggered && timeOffset >= 0) + AddJudgement(new OsuSliderTailJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss }); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 22bf63814c..db75321eb0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -1,75 +1,75 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableSliderTick : DrawableOsuHitObject, IRequireTracking - { - public const double ANIM_DURATION = 150; - - public bool Tracking { get; set; } - - public override bool DisplayJudgement => false; - - public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) - { - Size = new Vector2(16) * sliderTick.Scale; - - Masking = true; - CornerRadius = Size.X / 2; - - Origin = Anchor.Centre; - - BorderThickness = 2; - BorderColour = Color4.White; - - InternalChildren = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = AccentColour, - Alpha = 0.3f, - } - }; - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (timeOffset >= 0) - AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss }); - } - - protected override void UpdatePreemptState() - { - this.FadeOut().FadeIn(ANIM_DURATION); - this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf); - } - - protected override void UpdateCurrentState(ArmedState state) - { - switch (state) - { - case ArmedState.Idle: - this.Delay(HitObject.TimePreempt).FadeOut(); - break; - case ArmedState.Miss: - this.FadeOut(ANIM_DURATION); - this.FadeColour(Color4.Red, ANIM_DURATION / 2); - break; - case ArmedState.Hit: - this.FadeOut(ANIM_DURATION, Easing.OutQuint); - this.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out); - break; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableSliderTick : DrawableOsuHitObject, IRequireTracking + { + public const double ANIM_DURATION = 150; + + public bool Tracking { get; set; } + + public override bool DisplayJudgement => false; + + public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) + { + Size = new Vector2(16) * sliderTick.Scale; + + Masking = true; + CornerRadius = Size.X / 2; + + Origin = Anchor.Centre; + + BorderThickness = 2; + BorderColour = Color4.White; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = AccentColour, + Alpha = 0.3f, + } + }; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (timeOffset >= 0) + AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss }); + } + + protected override void UpdatePreemptState() + { + this.FadeOut().FadeIn(ANIM_DURATION); + this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf); + } + + protected override void UpdateCurrentState(ArmedState state) + { + switch (state) + { + case ArmedState.Idle: + this.Delay(HitObject.TimePreempt).FadeOut(); + break; + case ArmedState.Miss: + this.FadeOut(ANIM_DURATION); + this.FadeColour(Color4.Red, ANIM_DURATION / 2); + break; + case ArmedState.Hit: + this.FadeOut(ANIM_DURATION, Easing.OutQuint); + this.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out); + break; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 2705c213d9..f9d21ea5bb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -1,223 +1,223 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Allocation; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Screens.Ranking; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableSpinner : DrawableOsuHitObject - { - protected readonly Spinner Spinner; - - public readonly SpinnerDisc Disc; - public readonly SpinnerTicks Ticks; - private readonly SpinnerSpmCounter spmCounter; - - private readonly Container mainContainer; - - public readonly SpinnerBackground Background; - private readonly Container circleContainer; - private readonly CirclePiece circle; - private readonly GlowPiece glow; - - private readonly SpriteIcon symbol; - - private readonly Color4 baseColour = OsuColour.FromHex(@"002c3c"); - private readonly Color4 fillColour = OsuColour.FromHex(@"005b7c"); - - private Color4 normalColour; - private Color4 completeColour; - - public DrawableSpinner(Spinner s) : base(s) - { - Origin = Anchor.Centre; - Position = s.Position; - - RelativeSizeAxes = Axes.Both; - - // we are slightly bigger than our parent, to clip the top and bottom of the circle - Height = 1.3f; - - Spinner = s; - - InternalChildren = new Drawable[] - { - circleContainer = new Container - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new Drawable[] - { - glow = new GlowPiece(), - circle = new CirclePiece - { - Position = Vector2.Zero, - Anchor = Anchor.Centre, - }, - new RingPiece(), - symbol = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(48), - Icon = FontAwesome.fa_asterisk, - Shadow = false, - }, - } - }, - mainContainer = new AspectContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - Background = new SpinnerBackground - { - Alpha = 0.6f, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - Disc = new SpinnerDisc(Spinner) - { - Scale = Vector2.Zero, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - circleContainer.CreateProxy(), - Ticks = new SpinnerTicks - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - } - }, - spmCounter = new SpinnerSpmCounter - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = 120, - Alpha = 0 - } - }; - } - - public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1); - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (Time.Current < HitObject.StartTime) return; - - if (Progress >= 1 && !Disc.Complete) - { - Disc.Complete = true; - - const float duration = 200; - - Disc.FadeAccent(completeColour, duration); - - Background.FadeAccent(completeColour, duration); - Background.FadeOut(duration); - - circle.FadeColour(completeColour, duration); - glow.FadeColour(completeColour, duration); - } - - if (!userTriggered && Time.Current >= Spinner.EndTime) - { - if (Progress >= 1) - AddJudgement(new OsuJudgement { Result = HitResult.Great }); - else if (Progress > .9) - AddJudgement(new OsuJudgement { Result = HitResult.Good }); - else if (Progress > .75) - AddJudgement(new OsuJudgement { Result = HitResult.Meh }); - else if (Time.Current >= Spinner.EndTime) - AddJudgement(new OsuJudgement { Result = HitResult.Miss }); - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - normalColour = baseColour; - - Background.AccentColour = normalColour; - - completeColour = colours.YellowLight.Opacity(0.75f); - - Disc.AccentColour = fillColour; - circle.Colour = colours.BlueDark; - glow.Colour = colours.BlueDark; - } - - protected override void Update() - { - Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton); - if (!spmCounter.IsPresent && Disc.Tracking) - spmCounter.FadeIn(HitObject.TimeFadein); - - base.Update(); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - circle.Rotation = Disc.Rotation; - Ticks.Rotation = Disc.Rotation; - spmCounter.SetRotation(Disc.RotationAbsolute); - - float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; - Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint); - - symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint); - } - - protected override void UpdatePreemptState() - { - base.UpdatePreemptState(); - - circleContainer.ScaleTo(Spinner.Scale * 0.3f); - circleContainer.ScaleTo(Spinner.Scale, HitObject.TimePreempt / 1.4f, Easing.OutQuint); - - Disc.RotateTo(-720); - symbol.RotateTo(-720); - - mainContainer - .ScaleTo(0) - .ScaleTo(Spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, HitObject.TimePreempt - 150, Easing.OutQuint) - .Then() - .ScaleTo(1, 500, Easing.OutQuint); - } - - protected override void UpdateCurrentState(ArmedState state) - { - var sequence = this.Delay(Spinner.Duration).FadeOut(160); - - switch (state) - { - case ArmedState.Hit: - sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out); - break; - case ArmedState.Miss: - sequence.ScaleTo(Scale * 0.8f, 320, Easing.In); - break; - } - - Expire(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Allocation; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Screens.Ranking; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DrawableSpinner : DrawableOsuHitObject + { + protected readonly Spinner Spinner; + + public readonly SpinnerDisc Disc; + public readonly SpinnerTicks Ticks; + private readonly SpinnerSpmCounter spmCounter; + + private readonly Container mainContainer; + + public readonly SpinnerBackground Background; + private readonly Container circleContainer; + private readonly CirclePiece circle; + private readonly GlowPiece glow; + + private readonly SpriteIcon symbol; + + private readonly Color4 baseColour = OsuColour.FromHex(@"002c3c"); + private readonly Color4 fillColour = OsuColour.FromHex(@"005b7c"); + + private Color4 normalColour; + private Color4 completeColour; + + public DrawableSpinner(Spinner s) : base(s) + { + Origin = Anchor.Centre; + Position = s.Position; + + RelativeSizeAxes = Axes.Both; + + // we are slightly bigger than our parent, to clip the top and bottom of the circle + Height = 1.3f; + + Spinner = s; + + InternalChildren = new Drawable[] + { + circleContainer = new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + glow = new GlowPiece(), + circle = new CirclePiece + { + Position = Vector2.Zero, + Anchor = Anchor.Centre, + }, + new RingPiece(), + symbol = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(48), + Icon = FontAwesome.fa_asterisk, + Shadow = false, + }, + } + }, + mainContainer = new AspectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + Background = new SpinnerBackground + { + Alpha = 0.6f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + Disc = new SpinnerDisc(Spinner) + { + Scale = Vector2.Zero, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + circleContainer.CreateProxy(), + Ticks = new SpinnerTicks + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } + }, + spmCounter = new SpinnerSpmCounter + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 120, + Alpha = 0 + } + }; + } + + public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1); + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (Time.Current < HitObject.StartTime) return; + + if (Progress >= 1 && !Disc.Complete) + { + Disc.Complete = true; + + const float duration = 200; + + Disc.FadeAccent(completeColour, duration); + + Background.FadeAccent(completeColour, duration); + Background.FadeOut(duration); + + circle.FadeColour(completeColour, duration); + glow.FadeColour(completeColour, duration); + } + + if (!userTriggered && Time.Current >= Spinner.EndTime) + { + if (Progress >= 1) + AddJudgement(new OsuJudgement { Result = HitResult.Great }); + else if (Progress > .9) + AddJudgement(new OsuJudgement { Result = HitResult.Good }); + else if (Progress > .75) + AddJudgement(new OsuJudgement { Result = HitResult.Meh }); + else if (Time.Current >= Spinner.EndTime) + AddJudgement(new OsuJudgement { Result = HitResult.Miss }); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + normalColour = baseColour; + + Background.AccentColour = normalColour; + + completeColour = colours.YellowLight.Opacity(0.75f); + + Disc.AccentColour = fillColour; + circle.Colour = colours.BlueDark; + glow.Colour = colours.BlueDark; + } + + protected override void Update() + { + Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton); + if (!spmCounter.IsPresent && Disc.Tracking) + spmCounter.FadeIn(HitObject.TimeFadein); + + base.Update(); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + circle.Rotation = Disc.Rotation; + Ticks.Rotation = Disc.Rotation; + spmCounter.SetRotation(Disc.RotationAbsolute); + + float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; + Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint); + + symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint); + } + + protected override void UpdatePreemptState() + { + base.UpdatePreemptState(); + + circleContainer.ScaleTo(Spinner.Scale * 0.3f); + circleContainer.ScaleTo(Spinner.Scale, HitObject.TimePreempt / 1.4f, Easing.OutQuint); + + Disc.RotateTo(-720); + symbol.RotateTo(-720); + + mainContainer + .ScaleTo(0) + .ScaleTo(Spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, HitObject.TimePreempt - 150, Easing.OutQuint) + .Then() + .ScaleTo(1, 500, Easing.OutQuint); + } + + protected override void UpdateCurrentState(ArmedState state) + { + var sequence = this.Delay(Spinner.Duration).FadeOut(160); + + switch (state) + { + case ArmedState.Hit: + sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out); + break; + case ArmedState.Miss: + sequence.ScaleTo(Scale * 0.8f, 320, Easing.In); + break; + } + + Expire(); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs index 98fc686dd3..e89ec1775b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public interface IRequireTracking - { - /// - /// Whether the is currently being tracked by the user. - /// - bool Tracking { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public interface IRequireTracking + { + /// + /// Whether the is currently being tracked by the user. + /// + bool Tracking { get; set; } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs index b5fd87f60b..a05e51a460 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - /// - /// A component which tracks the current end snaking position of a slider. - /// - public interface ITrackSnaking - { - void UpdateSnakingPosition(Vector2 start, Vector2 end); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + /// + /// A component which tracks the current end snaking position of a slider. + /// + public interface ITrackSnaking + { + void UpdateSnakingPosition(Vector2 start, Vector2 end); + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs index 51f8b7026a..07d99bda42 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class ApproachCircle : Container - { - public ApproachCircle() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - Child = new SkinnableDrawable("Play/osu/approachcircle", name => new Sprite { Texture = textures.Get(name) }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class ApproachCircle : Container + { + public ApproachCircle() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Child = new SkinnableDrawable("Play/osu/approachcircle", name => new Sprite { Texture = textures.Get(name) }); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index e7b6598cf2..bd7a4ad3f6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Bindings; -using osu.Game.Skinning; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class CirclePiece : Container, IKeyBindingHandler - { - public Func Hit; - - public CirclePiece() - { - Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2); - Masking = true; - CornerRadius = Size.X / 2; - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - InternalChild = new SkinnableDrawable("Play/osu/hitcircle", _ => new DefaultCirclePiece()); - } - - public bool OnPressed(OsuAction action) - { - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - return IsHovered && (Hit?.Invoke() ?? false); - } - - return false; - } - - public bool OnReleased(OsuAction action) => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; +using osu.Game.Skinning; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class CirclePiece : Container, IKeyBindingHandler + { + public Func Hit; + + public CirclePiece() + { + Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2); + Masking = true; + CornerRadius = Size.X / 2; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + InternalChild = new SkinnableDrawable("Play/osu/hitcircle", _ => new DefaultCirclePiece()); + } + + public bool OnPressed(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + return IsHovered && (Hit?.Invoke() ?? false); + } + + return false; + } + + public bool OnReleased(OsuAction action) => false; + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs index 61f73b6d66..86b60c3443 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class DefaultCirclePiece : Container - { - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - RelativeSizeAxes = Axes.Both; - Children = new Drawable[] - { - new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = textures.Get(@"Play/osu/disc"), - }, - new TrianglesPiece - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Alpha = 0.5f, - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class DefaultCirclePiece : Container + { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + RelativeSizeAxes = Axes.Both; + Children = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = textures.Get(@"Play/osu/disc"), + }, + new TrianglesPiece + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Alpha = 0.5f, + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs index 28552e6c36..ed1b042939 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Skinning; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class ExplodePiece : Container - { - public ExplodePiece() - { - Size = new Vector2(128); - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - Blending = BlendingMode.Additive; - Alpha = 0; - - Child = new SkinnableDrawable("Play/osu/hitcircle-explode", _ => new TrianglesPiece - { - Blending = BlendingMode.Additive, - RelativeSizeAxes = Axes.Both, - Alpha = 0.2f, - }, s => s.GetTexture("Play/osu/hitcircle") == null); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Skinning; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class ExplodePiece : Container + { + public ExplodePiece() + { + Size = new Vector2(128); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Blending = BlendingMode.Additive; + Alpha = 0; + + Child = new SkinnableDrawable("Play/osu/hitcircle-explode", _ => new TrianglesPiece + { + Blending = BlendingMode.Additive, + RelativeSizeAxes = Axes.Both, + Alpha = 0.2f, + }, s => s.GetTexture("Play/osu/hitcircle") == null); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs index 50dc473750..0a1339a6ca 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using osu.Framework.Graphics.Shapes; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class FlashPiece : Container - { - public FlashPiece() - { - Size = new Vector2(128); - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - Blending = BlendingMode.Additive; - Alpha = 0; - - Child = new SkinnableDrawable("Play/osu/hitcircle-flash", name => new CircularContainer - { - Masking = true, - RelativeSizeAxes = Axes.Both, - Child = new Box - { - RelativeSizeAxes = Axes.Both - } - }, s => s.GetTexture("Play/osu/hitcircle") == null); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using osu.Framework.Graphics.Shapes; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class FlashPiece : Container + { + public FlashPiece() + { + Size = new Vector2(128); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Blending = BlendingMode.Additive; + Alpha = 0; + + Child = new SkinnableDrawable("Play/osu/hitcircle-flash", name => new CircularContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Child = new Box + { + RelativeSizeAxes = Axes.Both + } + }, s => s.GetTexture("Play/osu/hitcircle") == null); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs index 211e138b65..48b78bac40 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class GlowPiece : Container - { - public GlowPiece() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - RelativeSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - Child = new SkinnableDrawable("Play/osu/ring-glow", name => new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = textures.Get(name), - Blending = BlendingMode.Additive, - Alpha = 0.5f - }, s => s.GetTexture("Play/osu/hitcircle") == null); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class GlowPiece : Container + { + public GlowPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Child = new SkinnableDrawable("Play/osu/ring-glow", name => new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = textures.Get(name), + Blending = BlendingMode.Additive, + Alpha = 0.5f + }, s => s.GetTexture("Play/osu/hitcircle") == null); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs index 0c1fd4c364..30140484de 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -1,57 +1,57 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Sprites; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class NumberPiece : Container - { - private readonly SpriteText number; - - public string Text - { - get { return number.Text; } - set { number.Text = value; } - } - - public NumberPiece() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - Children = new Drawable[] - { - new SkinnableDrawable("Play/osu/number-glow", name => new CircularContainer - { - Masking = true, - Origin = Anchor.Centre, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Radius = 60, - Colour = Color4.White.Opacity(0.5f), - }, - Child = new Box() - }, s => s.GetTexture("Play/osu/hitcircle") == null), - number = new OsuSpriteText - { - Text = @"1", - Font = @"Venera", - UseFullGlyphHeight = false, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextSize = 40, - Alpha = 1 - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class NumberPiece : Container + { + private readonly SpriteText number; + + public string Text + { + get { return number.Text; } + set { number.Text = value; } + } + + public NumberPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Children = new Drawable[] + { + new SkinnableDrawable("Play/osu/number-glow", name => new CircularContainer + { + Masking = true, + Origin = Anchor.Centre, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 60, + Colour = Color4.White.Opacity(0.5f), + }, + Child = new Box() + }, s => s.GetTexture("Play/osu/hitcircle") == null), + number = new OsuSpriteText + { + Text = @"1", + Font = @"Venera", + UseFullGlyphHeight = false, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextSize = 40, + Alpha = 1 + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs index 12cc0dc5d9..f3e0a0ef43 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class RingPiece : Container - { - public RingPiece() - { - Size = new Vector2(128); - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - InternalChild = new SkinnableDrawable("Play/osu/hitcircleoverlay", _ => new Container - { - Masking = true, - CornerRadius = Size.X / 2, - BorderThickness = 10, - BorderColour = Color4.White, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - AlwaysPresent = true, - Alpha = 0, - RelativeSizeAxes = Axes.Both - } - } - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class RingPiece : Container + { + public RingPiece() + { + Size = new Vector2(128); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + InternalChild = new SkinnableDrawable("Play/osu/hitcircleoverlay", _ => new Container + { + Masking = true, + CornerRadius = Size.X / 2, + BorderThickness = 10, + BorderColour = Color4.White, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both + } + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 1921c51889..894d972e47 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -1,148 +1,148 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Rulesets.Objects.Types; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class SliderBall : CircularContainer, ISliderProgress - { - private const float width = 128; - - private Color4 accentColour = Color4.Black; - /// - /// The colour that is used for the slider ball. - /// - public Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - if (ball != null) - ball.Colour = value; - } - } - - private readonly Slider slider; - public readonly Box FollowCircle; - private readonly Box ball; - - public SliderBall(Slider slider) - { - this.slider = slider; - Masking = true; - AutoSizeAxes = Axes.Both; - Blending = BlendingMode.Additive; - Origin = Anchor.Centre; - BorderThickness = 10; - BorderColour = Color4.Orange; - - Children = new Drawable[] - { - FollowCircle = new Box - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Colour = Color4.Orange, - Width = width, - Height = width, - Alpha = 0, - }, - new CircularContainer - { - Masking = true, - AutoSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - BorderThickness = 10, - BorderColour = Color4.White, - Alpha = 1, - Children = new[] - { - ball = new Box - { - Colour = AccentColour, - Alpha = 0.4f, - Width = width, - Height = width, - }, - } - } - }; - } - - private InputState lastState; - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - lastState = state; - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - lastState = state; - return base.OnMouseUp(state, args); - } - - protected override bool OnMouseMove(InputState state) - { - lastState = state; - return base.OnMouseMove(state); - } - - // If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position. - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos); - - public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) - { - // Consider the case of rewinding - children's transforms are handled internally, so propagating down - // any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point. - base.ClearTransformsAfter(time, false, targetMember); - } - - private bool tracking; - public bool Tracking - { - get { return tracking; } - private set - { - if (value == tracking) - return; - tracking = value; - - FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint); - FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint); - } - } - - private bool canCurrentlyTrack => Time.Current >= slider.StartTime && Time.Current < slider.EndTime; - - protected override void Update() - { - base.Update(); - - if (Time.Current < slider.EndTime) - { - // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. - Tracking = canCurrentlyTrack - && lastState != null - && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) - && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); - } - } - - public void UpdateProgress(double completionProgress) - { - Position = slider.CurvePositionAt(completionProgress); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Rulesets.Objects.Types; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SliderBall : CircularContainer, ISliderProgress + { + private const float width = 128; + + private Color4 accentColour = Color4.Black; + /// + /// The colour that is used for the slider ball. + /// + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + if (ball != null) + ball.Colour = value; + } + } + + private readonly Slider slider; + public readonly Box FollowCircle; + private readonly Box ball; + + public SliderBall(Slider slider) + { + this.slider = slider; + Masking = true; + AutoSizeAxes = Axes.Both; + Blending = BlendingMode.Additive; + Origin = Anchor.Centre; + BorderThickness = 10; + BorderColour = Color4.Orange; + + Children = new Drawable[] + { + FollowCircle = new Box + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Colour = Color4.Orange, + Width = width, + Height = width, + Alpha = 0, + }, + new CircularContainer + { + Masking = true, + AutoSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + BorderThickness = 10, + BorderColour = Color4.White, + Alpha = 1, + Children = new[] + { + ball = new Box + { + Colour = AccentColour, + Alpha = 0.4f, + Width = width, + Height = width, + }, + } + } + }; + } + + private InputState lastState; + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + lastState = state; + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + lastState = state; + return base.OnMouseUp(state, args); + } + + protected override bool OnMouseMove(InputState state) + { + lastState = state; + return base.OnMouseMove(state); + } + + // If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position. + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos); + + public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) + { + // Consider the case of rewinding - children's transforms are handled internally, so propagating down + // any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point. + base.ClearTransformsAfter(time, false, targetMember); + } + + private bool tracking; + public bool Tracking + { + get { return tracking; } + private set + { + if (value == tracking) + return; + tracking = value; + + FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint); + FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint); + } + } + + private bool canCurrentlyTrack => Time.Current >= slider.StartTime && Time.Current < slider.EndTime; + + protected override void Update() + { + base.Update(); + + if (Time.Current < slider.EndTime) + { + // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. + Tracking = canCurrentlyTrack + && lastState != null + && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) + && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + } + } + + public void UpdateProgress(double completionProgress) + { + Position = slider.CurvePositionAt(completionProgress); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index c59c22c771..0a6b1b459a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -1,233 +1,233 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.OpenGL.Textures; -using osu.Framework.Graphics.Lines; -using osu.Framework.Graphics.Textures; -using OpenTK; -using OpenTK.Graphics.ES30; -using OpenTK.Graphics; -using osu.Framework.Graphics.Primitives; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class SliderBody : Container, ISliderProgress - { - private readonly Path path; - private readonly BufferedContainer container; - - public float PathWidth - { - get { return path.PathWidth; } - set { path.PathWidth = value; } - } - - /// - /// Offset in absolute coordinates from the start of the curve. - /// - public Vector2 PathOffset { get; private set; } - - public readonly List CurrentCurve = new List(); - - public readonly Bindable SnakingIn = new Bindable(); - public readonly Bindable SnakingOut = new Bindable(); - - public double? SnakedStart { get; private set; } - public double? SnakedEnd { get; private set; } - - private Color4 accentColour = Color4.White; - /// - /// Used to colour the path. - /// - public Color4 AccentColour - { - get { return accentColour; } - set - { - if (accentColour == value) - return; - accentColour = value; - - if (LoadState >= LoadState.Ready) - reloadTexture(); - } - } - - private Color4 borderColour = Color4.White; - /// - /// Used to colour the path border. - /// - public new Color4 BorderColour - { - get { return borderColour; } - set - { - if (borderColour == value) - return; - borderColour = value; - - if (LoadState >= LoadState.Ready) - reloadTexture(); - } - } - - public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; - - private int textureWidth => (int)PathWidth * 2; - - private Vector2 topLeftOffset; - - private readonly Slider slider; - public SliderBody(Slider s) - { - slider = s; - - Children = new Drawable[] - { - container = new BufferedContainer - { - RelativeSizeAxes = Axes.Both, - CacheDrawnFrameBuffer = true, - Children = new Drawable[] - { - path = new Path - { - Blending = BlendingMode.None, - }, - } - }, - }; - - container.Attach(RenderbufferInternalFormat.DepthComponent16); - } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => path.ReceiveMouseInputAt(screenSpacePos); - - public void SetRange(double p0, double p1) - { - if (p0 > p1) - MathHelper.Swap(ref p0, ref p1); - - if (updateSnaking(p0, p1)) - { - // The path is generated such that its size encloses it. This change of size causes the path - // to move around while snaking, so we need to offset it to make sure it maintains the - // same position as when it is fully snaked. - var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); - path.Position = topLeftOffset - newTopLeftOffset; - - container.ForceRedraw(); - } - } - - [BackgroundDependencyLoader] - private void load() - { - reloadTexture(); - computeSize(); - } - - private void reloadTexture() - { - var texture = new Texture(textureWidth, 1); - - //initialise background - var upload = new TextureUpload(textureWidth * 4); - var bytes = upload.Data; - - const float aa_portion = 0.02f; - const float border_portion = 0.128f; - const float gradient_portion = 1 - border_portion; - - const float opacity_at_centre = 0.3f; - const float opacity_at_edge = 0.8f; - - for (int i = 0; i < textureWidth; i++) - { - float progress = (float)i / (textureWidth - 1); - - if (progress <= border_portion) - { - bytes[i * 4] = (byte)(BorderColour.R * 255); - bytes[i * 4 + 1] = (byte)(BorderColour.G * 255); - bytes[i * 4 + 2] = (byte)(BorderColour.B * 255); - bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255)); - } - else - { - progress -= border_portion; - - bytes[i * 4] = (byte)(AccentColour.R * 255); - bytes[i * 4 + 1] = (byte)(AccentColour.G * 255); - bytes[i * 4 + 2] = (byte)(AccentColour.B * 255); - bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255)); - } - } - - texture.SetData(upload); - path.Texture = texture; - - container.ForceRedraw(); - } - - private void computeSize() - { - // Generate the entire curve - slider.Curve.GetPathToProgress(CurrentCurve, 0, 1); - foreach (Vector2 p in CurrentCurve) - path.AddVertex(p); - - Size = path.Size; - - topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); - PathOffset = path.PositionInBoundingBox(CurrentCurve[0]); - } - - private bool updateSnaking(double p0, double p1) - { - if (SnakedStart == p0 && SnakedEnd == p1) return false; - - SnakedStart = p0; - SnakedEnd = p1; - - slider.Curve.GetPathToProgress(CurrentCurve, p0, p1); - - path.ClearVertices(); - foreach (Vector2 p in CurrentCurve) - path.AddVertex(p); - - return true; - } - - public void UpdateProgress(double completionProgress) - { - var span = slider.SpanAt(completionProgress); - var spanProgress = slider.ProgressAt(completionProgress); - - double start = 0; - double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1; - - if (span >= slider.SpanCount() - 1) - { - if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) - { - start = 0; - end = SnakingOut ? spanProgress : 1; - } - else - { - start = SnakingOut ? spanProgress : 0; - } - } - - SetRange(start, end); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.Textures; +using OpenTK; +using OpenTK.Graphics.ES30; +using OpenTK.Graphics; +using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SliderBody : Container, ISliderProgress + { + private readonly Path path; + private readonly BufferedContainer container; + + public float PathWidth + { + get { return path.PathWidth; } + set { path.PathWidth = value; } + } + + /// + /// Offset in absolute coordinates from the start of the curve. + /// + public Vector2 PathOffset { get; private set; } + + public readonly List CurrentCurve = new List(); + + public readonly Bindable SnakingIn = new Bindable(); + public readonly Bindable SnakingOut = new Bindable(); + + public double? SnakedStart { get; private set; } + public double? SnakedEnd { get; private set; } + + private Color4 accentColour = Color4.White; + /// + /// Used to colour the path. + /// + public Color4 AccentColour + { + get { return accentColour; } + set + { + if (accentColour == value) + return; + accentColour = value; + + if (LoadState >= LoadState.Ready) + reloadTexture(); + } + } + + private Color4 borderColour = Color4.White; + /// + /// Used to colour the path border. + /// + public new Color4 BorderColour + { + get { return borderColour; } + set + { + if (borderColour == value) + return; + borderColour = value; + + if (LoadState >= LoadState.Ready) + reloadTexture(); + } + } + + public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; + + private int textureWidth => (int)PathWidth * 2; + + private Vector2 topLeftOffset; + + private readonly Slider slider; + public SliderBody(Slider s) + { + slider = s; + + Children = new Drawable[] + { + container = new BufferedContainer + { + RelativeSizeAxes = Axes.Both, + CacheDrawnFrameBuffer = true, + Children = new Drawable[] + { + path = new Path + { + Blending = BlendingMode.None, + }, + } + }, + }; + + container.Attach(RenderbufferInternalFormat.DepthComponent16); + } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => path.ReceiveMouseInputAt(screenSpacePos); + + public void SetRange(double p0, double p1) + { + if (p0 > p1) + MathHelper.Swap(ref p0, ref p1); + + if (updateSnaking(p0, p1)) + { + // The path is generated such that its size encloses it. This change of size causes the path + // to move around while snaking, so we need to offset it to make sure it maintains the + // same position as when it is fully snaked. + var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + path.Position = topLeftOffset - newTopLeftOffset; + + container.ForceRedraw(); + } + } + + [BackgroundDependencyLoader] + private void load() + { + reloadTexture(); + computeSize(); + } + + private void reloadTexture() + { + var texture = new Texture(textureWidth, 1); + + //initialise background + var upload = new TextureUpload(textureWidth * 4); + var bytes = upload.Data; + + const float aa_portion = 0.02f; + const float border_portion = 0.128f; + const float gradient_portion = 1 - border_portion; + + const float opacity_at_centre = 0.3f; + const float opacity_at_edge = 0.8f; + + for (int i = 0; i < textureWidth; i++) + { + float progress = (float)i / (textureWidth - 1); + + if (progress <= border_portion) + { + bytes[i * 4] = (byte)(BorderColour.R * 255); + bytes[i * 4 + 1] = (byte)(BorderColour.G * 255); + bytes[i * 4 + 2] = (byte)(BorderColour.B * 255); + bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255)); + } + else + { + progress -= border_portion; + + bytes[i * 4] = (byte)(AccentColour.R * 255); + bytes[i * 4 + 1] = (byte)(AccentColour.G * 255); + bytes[i * 4 + 2] = (byte)(AccentColour.B * 255); + bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255)); + } + } + + texture.SetData(upload); + path.Texture = texture; + + container.ForceRedraw(); + } + + private void computeSize() + { + // Generate the entire curve + slider.Curve.GetPathToProgress(CurrentCurve, 0, 1); + foreach (Vector2 p in CurrentCurve) + path.AddVertex(p); + + Size = path.Size; + + topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + PathOffset = path.PositionInBoundingBox(CurrentCurve[0]); + } + + private bool updateSnaking(double p0, double p1) + { + if (SnakedStart == p0 && SnakedEnd == p1) return false; + + SnakedStart = p0; + SnakedEnd = p1; + + slider.Curve.GetPathToProgress(CurrentCurve, p0, p1); + + path.ClearVertices(); + foreach (Vector2 p in CurrentCurve) + path.AddVertex(p); + + return true; + } + + public void UpdateProgress(double completionProgress) + { + var span = slider.SpanAt(completionProgress); + var spanProgress = slider.ProgressAt(completionProgress); + + double start = 0; + double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1; + + if (span >= slider.SpanCount() - 1) + { + if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) + { + start = 0; + end = SnakingOut ? spanProgress : 1; + } + else + { + start = SnakingOut ? spanProgress : 0; + } + } + + SetRange(start, end); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs index c44d7594ad..1a7455838f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs @@ -1,56 +1,56 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class SpinnerBackground : CircularContainer, IHasAccentColour - { - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - - protected Box Disc; - - public Color4 AccentColour - { - get - { - return Disc.Colour; - } - set - { - Disc.Colour = value; - - EdgeEffect = new EdgeEffectParameters - { - Hollow = true, - Type = EdgeEffectType.Glow, - Radius = 40, - Colour = value, - }; - } - } - - public SpinnerBackground() - { - RelativeSizeAxes = Axes.Both; - Masking = true; - - Children = new Drawable[] - { - Disc = new Box - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Alpha = 1, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SpinnerBackground : CircularContainer, IHasAccentColour + { + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; + + protected Box Disc; + + public Color4 AccentColour + { + get + { + return Disc.Colour; + } + set + { + Disc.Colour = value; + + EdgeEffect = new EdgeEffectParameters + { + Hollow = true, + Type = EdgeEffectType.Glow, + Radius = 40, + Colour = value, + }; + } + } + + public SpinnerBackground() + { + RelativeSizeAxes = Axes.Both; + Masking = true; + + Children = new Drawable[] + { + Disc = new Box + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 1, + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index 971914ca13..bb5fa1b575 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -1,127 +1,127 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Graphics; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class SpinnerDisc : CircularContainer, IHasAccentColour - { - private readonly Spinner spinner; - - public Color4 AccentColour - { - get { return background.AccentColour; } - set { background.AccentColour = value; } - } - - private readonly SpinnerBackground background; - - private const float idle_alpha = 0.2f; - private const float tracking_alpha = 0.4f; - - public override bool IsPresent => true; // handle input when hidden - - public SpinnerDisc(Spinner s) - { - spinner = s; - - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - background = new SpinnerBackground { Alpha = idle_alpha }, - }; - } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - - private bool tracking; - public bool Tracking - { - get { return tracking; } - set - { - if (value == tracking) return; - tracking = value; - - background.FadeTo(tracking ? tracking_alpha : idle_alpha, 100); - } - } - - private bool complete; - public bool Complete - { - get { return complete; } - set - { - if (value == complete) return; - complete = value; - - updateCompleteTick(); - } - } - - protected override bool OnMouseMove(InputState state) - { - mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position); - return base.OnMouseMove(state); - } - - private Vector2 mousePosition; - - private float lastAngle; - private float currentRotation; - public float RotationAbsolute; - private int completeTick; - - private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360)); - - private bool rotationTransferred; - - protected override void Update() - { - base.Update(); - - var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); - - bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current; - - if (validAndTracking) - { - if (!rotationTransferred) - { - currentRotation = Rotation * 2; - rotationTransferred = true; - } - - if (thisAngle - lastAngle > 180) - lastAngle += 360; - else if (lastAngle - thisAngle > 180) - lastAngle -= 360; - - currentRotation += thisAngle - lastAngle; - RotationAbsolute += Math.Abs(thisAngle - lastAngle); - } - - lastAngle = thisAngle; - - if (Complete && updateCompleteTick()) - { - background.FinishTransforms(false, nameof(Alpha)); - background - .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) - .Then() - .FadeTo(tracking_alpha, 250, Easing.OutQuint); - } - - this.RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, Easing.OutExpo); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SpinnerDisc : CircularContainer, IHasAccentColour + { + private readonly Spinner spinner; + + public Color4 AccentColour + { + get { return background.AccentColour; } + set { background.AccentColour = value; } + } + + private readonly SpinnerBackground background; + + private const float idle_alpha = 0.2f; + private const float tracking_alpha = 0.4f; + + public override bool IsPresent => true; // handle input when hidden + + public SpinnerDisc(Spinner s) + { + spinner = s; + + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + background = new SpinnerBackground { Alpha = idle_alpha }, + }; + } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + + private bool tracking; + public bool Tracking + { + get { return tracking; } + set + { + if (value == tracking) return; + tracking = value; + + background.FadeTo(tracking ? tracking_alpha : idle_alpha, 100); + } + } + + private bool complete; + public bool Complete + { + get { return complete; } + set + { + if (value == complete) return; + complete = value; + + updateCompleteTick(); + } + } + + protected override bool OnMouseMove(InputState state) + { + mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position); + return base.OnMouseMove(state); + } + + private Vector2 mousePosition; + + private float lastAngle; + private float currentRotation; + public float RotationAbsolute; + private int completeTick; + + private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360)); + + private bool rotationTransferred; + + protected override void Update() + { + base.Update(); + + var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); + + bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current; + + if (validAndTracking) + { + if (!rotationTransferred) + { + currentRotation = Rotation * 2; + rotationTransferred = true; + } + + if (thisAngle - lastAngle > 180) + lastAngle += 360; + else if (lastAngle - thisAngle > 180) + lastAngle -= 360; + + currentRotation += thisAngle - lastAngle; + RotationAbsolute += Math.Abs(thisAngle - lastAngle); + } + + lastAngle = thisAngle; + + if (Complete && updateCompleteTick()) + { + background.FinishTransforms(false, nameof(Alpha)); + background + .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) + .Then() + .FadeTo(tracking_alpha, 250, Easing.OutQuint); + } + + this.RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, Easing.OutExpo); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs index 182cc9d56a..0bdd4296fb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs @@ -1,80 +1,80 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class SpinnerSpmCounter : Container - { - private readonly OsuSpriteText spmText; - - public SpinnerSpmCounter() - { - Children = new Drawable[] - { - spmText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = @"0", - Font = @"Venera", - TextSize = 24 - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = @"SPINS PER MINUTE", - Font = @"Venera", - TextSize = 12, - Y = 30 - } - }; - } - - private double spm; - - public double SpinsPerMinute - { - get { return spm; } - private set - { - if (value == spm) return; - spm = value; - spmText.Text = Math.Truncate(value).ToString(@"#0"); - } - } - - private struct RotationRecord - { - public float Rotation; - public double Time; - } - - private readonly Queue records = new Queue(); - private const double spm_count_duration = 595; // not using hundreds to avoid frame rounding issues - - public void SetRotation(float currentRotation) - { - // If we've gone back in time, it's fine to work with a fresh set of records for now - if (records.Count > 0 && Time.Current < records.Last().Time) - records.Clear(); - - if (records.Count > 0) - { - var record = records.Peek(); - while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration) - record = records.Dequeue(); - SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360; - } - - records.Enqueue(new RotationRecord { Rotation = currentRotation, Time = Time.Current }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SpinnerSpmCounter : Container + { + private readonly OsuSpriteText spmText; + + public SpinnerSpmCounter() + { + Children = new Drawable[] + { + spmText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = @"0", + Font = @"Venera", + TextSize = 24 + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = @"SPINS PER MINUTE", + Font = @"Venera", + TextSize = 12, + Y = 30 + } + }; + } + + private double spm; + + public double SpinsPerMinute + { + get { return spm; } + private set + { + if (value == spm) return; + spm = value; + spmText.Text = Math.Truncate(value).ToString(@"#0"); + } + } + + private struct RotationRecord + { + public float Rotation; + public double Time; + } + + private readonly Queue records = new Queue(); + private const double spm_count_duration = 595; // not using hundreds to avoid frame rounding issues + + public void SetRotation(float currentRotation) + { + // If we've gone back in time, it's fine to work with a fresh set of records for now + if (records.Count > 0 && Time.Current < records.Last().Time) + records.Clear(); + + if (records.Count > 0) + { + var record = records.Peek(); + while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration) + record = records.Dequeue(); + SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360; + } + + records.Enqueue(new RotationRecord { Rotation = currentRotation, Time = Time.Current }); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs index 135fbbd8db..61387d796e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs @@ -1,57 +1,57 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class SpinnerTicks : Container - { - public SpinnerTicks() - { - Origin = Anchor.Centre; - Anchor = Anchor.Centre; - RelativeSizeAxes = Axes.Both; - - const int count = 18; - - for (int i = 0; i < count; i++) - { - Add(new Container - { - Colour = Color4.Black, - Alpha = 0.4f, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Radius = 10, - Colour = Color4.Gray.Opacity(0.2f), - }, - RelativePositionAxes = Axes.Both, - Masking = true, - CornerRadius = 5, - Size = new Vector2(60, 10), - Origin = Anchor.Centre, - Position = new Vector2( - 0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f, - 0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f - ), - Rotation = -(float)i / count * 360 + 90, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - } - } - }); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SpinnerTicks : Container + { + public SpinnerTicks() + { + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + + const int count = 18; + + for (int i = 0; i < count; i++) + { + Add(new Container + { + Colour = Color4.Black, + Alpha = 0.4f, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 10, + Colour = Color4.Gray.Opacity(0.2f), + }, + RelativePositionAxes = Axes.Both, + Masking = true, + CornerRadius = 5, + Size = new Vector2(60, 10), + Origin = Anchor.Centre, + Position = new Vector2( + 0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f, + 0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f + ), + Rotation = -(float)i / count * 360 + 90, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + } + } + }); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/TrianglesPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/TrianglesPiece.cs index 4d3397de3e..59b204bdaf 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/TrianglesPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/TrianglesPiece.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics.Backgrounds; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class TrianglesPiece : Triangles - { - protected override bool ExpireOffScreenTriangles => false; - protected override bool CreateNewTriangles => false; - protected override float SpawnRatio => 0.5f; - - public TrianglesPiece() - { - TriangleScale = 1.2f; - HideAlphaDiscrepancies = false; - } - - protected override void Update() - { - if (IsPresent) - base.Update(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics.Backgrounds; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class TrianglesPiece : Triangles + { + protected override bool ExpireOffScreenTriangles => false; + protected override bool CreateNewTriangles => false; + protected override float SpawnRatio => 0.5f; + + public TrianglesPiece() + { + TriangleScale = 1.2f; + HideAlphaDiscrepancies = false; + } + + protected override void Update() + { + if (IsPresent) + base.Update(); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/HitCircle.cs b/osu.Game.Rulesets.Osu/Objects/HitCircle.cs index 9f6041cb70..9e309a376d 100644 --- a/osu.Game.Rulesets.Osu/Objects/HitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/HitCircle.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Osu.Objects -{ - public class HitCircle : OsuHitObject - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Objects +{ + public class HitCircle : OsuHitObject + { + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs index a0566eaf17..43ec7ae006 100644 --- a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs +++ b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Osu.Objects -{ - public interface ISliderProgress - { - /// - /// Updates the progress of this element along the slider. - /// - /// Amount of the slider completed. - void UpdateProgress(double completionProgress); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Objects +{ + public interface ISliderProgress + { + /// + /// Updates the progress of this element along the slider. + /// + /// Amount of the slider completed. + void UpdateProgress(double completionProgress); + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index f64db6ba9e..2b7b7783e2 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -1,75 +1,75 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using OpenTK; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Edit.Types; - -namespace osu.Game.Rulesets.Osu.Objects -{ - public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasEditablePosition - { - public const double OBJECT_RADIUS = 64; - - public event Action PositionChanged; - - public double TimePreempt = 600; - public double TimeFadein = 400; - - private Vector2 position; - - public Vector2 Position - { - get => position; - set - { - if (position == value) - return; - position = value; - - PositionChanged?.Invoke(value); - } - } - - public float X => Position.X; - public float Y => Position.Y; - - public Vector2 StackedPosition => Position + StackOffset; - - public virtual Vector2 EndPosition => Position; - - public Vector2 StackedEndPosition => EndPosition + StackOffset; - - public virtual int StackHeight { get; set; } - - public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f); - - public double Radius => OBJECT_RADIUS * Scale; - - public float Scale { get; set; } = 1; - - public virtual bool NewCombo { get; set; } - - public int IndexInCurrentCombo { get; set; } - - public int ComboIndex { get; set; } - - public bool LastInCombo { get; set; } - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); - TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300); - - Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; - } - - public virtual void OffsetPosition(Vector2 offset) => Position += offset; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using OpenTK; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit.Types; + +namespace osu.Game.Rulesets.Osu.Objects +{ + public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasEditablePosition + { + public const double OBJECT_RADIUS = 64; + + public event Action PositionChanged; + + public double TimePreempt = 600; + public double TimeFadein = 400; + + private Vector2 position; + + public Vector2 Position + { + get => position; + set + { + if (position == value) + return; + position = value; + + PositionChanged?.Invoke(value); + } + } + + public float X => Position.X; + public float Y => Position.Y; + + public Vector2 StackedPosition => Position + StackOffset; + + public virtual Vector2 EndPosition => Position; + + public Vector2 StackedEndPosition => EndPosition + StackOffset; + + public virtual int StackHeight { get; set; } + + public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f); + + public double Radius => OBJECT_RADIUS * Scale; + + public float Scale { get; set; } = 1; + + public virtual bool NewCombo { get; set; } + + public int IndexInCurrentCombo { get; set; } + + public int ComboIndex { get; set; } + + public bool LastInCombo { get; set; } + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); + TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300); + + Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; + } + + public virtual void OffsetPosition(Vector2 offset) => Position += offset; + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs index eaaa8d7a7e..0b729f0956 100644 --- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Osu.Objects -{ - public class RepeatPoint : OsuHitObject - { - public int RepeatIndex { get; set; } - public double SpanDuration { get; set; } - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - // We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders - // we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time. - if (RepeatIndex > 0) - TimePreempt = Math.Min(SpanDuration * 2, TimePreempt); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Osu.Objects +{ + public class RepeatPoint : OsuHitObject + { + public int RepeatIndex { get; set; } + public double SpanDuration { get; set; } + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + // We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders + // we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time. + if (RepeatIndex > 0) + TimePreempt = Math.Min(SpanDuration * 2, TimePreempt); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 469c4ddcb4..7f4407370f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -1,186 +1,186 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Rulesets.Objects.Types; -using System.Collections.Generic; -using osu.Game.Rulesets.Objects; -using System.Linq; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Osu.Objects -{ - public class Slider : OsuHitObject, IHasCurve - { - /// - /// Scoring distance with a speed-adjusted beat length of 1 second. - /// - private const float base_scoring_distance = 100; - - public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; - public double Duration => EndTime - StartTime; - - public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); - public override Vector2 EndPosition => Position + this.CurvePositionAt(1); - - public SliderCurve Curve { get; } = new SliderCurve(); - - public List ControlPoints - { - get { return Curve.ControlPoints; } - set { Curve.ControlPoints = value; } - } - - public CurveType CurveType - { - get { return Curve.CurveType; } - set { Curve.CurveType = value; } - } - - public double Distance - { - get { return Curve.Distance; } - set { Curve.Distance = value; } - } - - /// - /// The position of the cursor at the point of completion of this if it was hit - /// with as few movements as possible. This is set and used by difficulty calculation. - /// - internal Vector2? LazyEndPosition; - - /// - /// The distance travelled by the cursor upon completion of this if it was hit - /// with as few movements as possible. This is set and used by difficulty calculation. - /// - internal float LazyTravelDistance; - - public List> RepeatSamples { get; set; } = new List>(); - public int RepeatCount { get; set; } - - /// - /// The length of one span of this . - /// - public double SpanDuration => Duration / this.SpanCount(); - - public double Velocity; - public double TickDistance; - - public HitCircle HeadCircle; - public HitCircle TailCircle; - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); - - double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; - - Velocity = scoringDistance / timingPoint.BeatLength; - TickDistance = scoringDistance / difficulty.SliderTickRate; - } - - protected override void CreateNestedHitObjects() - { - base.CreateNestedHitObjects(); - - createSliderEnds(); - createTicks(); - createRepeatPoints(); - } - - private void createSliderEnds() - { - HeadCircle = new SliderCircle(this) - { - StartTime = StartTime, - Position = Position, - Samples = Samples, - SampleControlPoint = SampleControlPoint, - IndexInCurrentCombo = IndexInCurrentCombo, - ComboIndex = ComboIndex, - }; - - TailCircle = new SliderCircle(this) - { - StartTime = EndTime, - Position = EndPosition, - IndexInCurrentCombo = IndexInCurrentCombo, - ComboIndex = ComboIndex, - }; - - AddNested(HeadCircle); - AddNested(TailCircle); - } - - private void createTicks() - { - var length = Curve.Distance; - var tickDistance = MathHelper.Clamp(TickDistance, 0, length); - - if (tickDistance == 0) return; - - var minDistanceFromEnd = Velocity * 0.01; - - var spanCount = this.SpanCount(); - - for (var span = 0; span < spanCount; span++) - { - var spanStartTime = StartTime + span * SpanDuration; - var reversed = span % 2 == 1; - - for (var d = tickDistance; d <= length; d += tickDistance) - { - if (d > length - minDistanceFromEnd) - break; - - var distanceProgress = d / length; - var timeProgress = reversed ? 1 - distanceProgress : distanceProgress; - - var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) - var sampleList = new List(); - - if (firstSample != null) - sampleList.Add(new SampleInfo - { - Bank = firstSample.Bank, - Volume = firstSample.Volume, - Name = @"slidertick", - }); - - AddNested(new SliderTick - { - SpanIndex = span, - SpanStartTime = spanStartTime, - StartTime = spanStartTime + timeProgress * SpanDuration, - Position = Position + Curve.PositionAt(distanceProgress), - StackHeight = StackHeight, - Scale = Scale, - Samples = sampleList - }); - } - } - } - - private void createRepeatPoints() - { - for (int repeatIndex = 0, repeat = 1; repeatIndex < RepeatCount; repeatIndex++, repeat++) - { - AddNested(new RepeatPoint - { - RepeatIndex = repeatIndex, - SpanDuration = SpanDuration, - StartTime = StartTime + repeat * SpanDuration, - Position = Position + Curve.PositionAt(repeat % 2), - StackHeight = StackHeight, - Scale = Scale, - Samples = new List(RepeatSamples[repeatIndex]) - }); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Rulesets.Objects.Types; +using System.Collections.Generic; +using osu.Game.Rulesets.Objects; +using System.Linq; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Osu.Objects +{ + public class Slider : OsuHitObject, IHasCurve + { + /// + /// Scoring distance with a speed-adjusted beat length of 1 second. + /// + private const float base_scoring_distance = 100; + + public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; + public double Duration => EndTime - StartTime; + + public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); + public override Vector2 EndPosition => Position + this.CurvePositionAt(1); + + public SliderCurve Curve { get; } = new SliderCurve(); + + public List ControlPoints + { + get { return Curve.ControlPoints; } + set { Curve.ControlPoints = value; } + } + + public CurveType CurveType + { + get { return Curve.CurveType; } + set { Curve.CurveType = value; } + } + + public double Distance + { + get { return Curve.Distance; } + set { Curve.Distance = value; } + } + + /// + /// The position of the cursor at the point of completion of this if it was hit + /// with as few movements as possible. This is set and used by difficulty calculation. + /// + internal Vector2? LazyEndPosition; + + /// + /// The distance travelled by the cursor upon completion of this if it was hit + /// with as few movements as possible. This is set and used by difficulty calculation. + /// + internal float LazyTravelDistance; + + public List> RepeatSamples { get; set; } = new List>(); + public int RepeatCount { get; set; } + + /// + /// The length of one span of this . + /// + public double SpanDuration => Duration / this.SpanCount(); + + public double Velocity; + public double TickDistance; + + public HitCircle HeadCircle; + public HitCircle TailCircle; + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); + + double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; + + Velocity = scoringDistance / timingPoint.BeatLength; + TickDistance = scoringDistance / difficulty.SliderTickRate; + } + + protected override void CreateNestedHitObjects() + { + base.CreateNestedHitObjects(); + + createSliderEnds(); + createTicks(); + createRepeatPoints(); + } + + private void createSliderEnds() + { + HeadCircle = new SliderCircle(this) + { + StartTime = StartTime, + Position = Position, + Samples = Samples, + SampleControlPoint = SampleControlPoint, + IndexInCurrentCombo = IndexInCurrentCombo, + ComboIndex = ComboIndex, + }; + + TailCircle = new SliderCircle(this) + { + StartTime = EndTime, + Position = EndPosition, + IndexInCurrentCombo = IndexInCurrentCombo, + ComboIndex = ComboIndex, + }; + + AddNested(HeadCircle); + AddNested(TailCircle); + } + + private void createTicks() + { + var length = Curve.Distance; + var tickDistance = MathHelper.Clamp(TickDistance, 0, length); + + if (tickDistance == 0) return; + + var minDistanceFromEnd = Velocity * 0.01; + + var spanCount = this.SpanCount(); + + for (var span = 0; span < spanCount; span++) + { + var spanStartTime = StartTime + span * SpanDuration; + var reversed = span % 2 == 1; + + for (var d = tickDistance; d <= length; d += tickDistance) + { + if (d > length - minDistanceFromEnd) + break; + + var distanceProgress = d / length; + var timeProgress = reversed ? 1 - distanceProgress : distanceProgress; + + var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) + var sampleList = new List(); + + if (firstSample != null) + sampleList.Add(new SampleInfo + { + Bank = firstSample.Bank, + Volume = firstSample.Volume, + Name = @"slidertick", + }); + + AddNested(new SliderTick + { + SpanIndex = span, + SpanStartTime = spanStartTime, + StartTime = spanStartTime + timeProgress * SpanDuration, + Position = Position + Curve.PositionAt(distanceProgress), + StackHeight = StackHeight, + Scale = Scale, + Samples = sampleList + }); + } + } + } + + private void createRepeatPoints() + { + for (int repeatIndex = 0, repeat = 1; repeatIndex < RepeatCount; repeatIndex++, repeat++) + { + AddNested(new RepeatPoint + { + RepeatIndex = repeatIndex, + SpanDuration = SpanDuration, + StartTime = StartTime + repeat * SpanDuration, + Position = Position + Curve.PositionAt(repeat % 2), + StackHeight = StackHeight, + Scale = Scale, + Samples = new List(RepeatSamples[repeatIndex]) + }); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs index 1e83d02735..1bdd16c9df 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Objects -{ - public class SliderCircle : HitCircle - { - private readonly Slider slider; - - public SliderCircle(Slider slider) - { - this.slider = slider; - } - - public override void OffsetPosition(Vector2 offset) => slider.OffsetPosition(offset); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Objects +{ + public class SliderCircle : HitCircle + { + private readonly Slider slider; + + public SliderCircle(Slider slider) + { + this.slider = slider; + } + + public override void OffsetPosition(Vector2 offset) => slider.OffsetPosition(offset); + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs index 966db73eb9..4db6eb9883 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs @@ -1,30 +1,30 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Osu.Objects -{ - public class SliderTick : OsuHitObject - { - public int SpanIndex { get; set; } - public double SpanStartTime { get; set; } - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - double offset; - - if (SpanIndex > 0) - // Adding 200 to include the offset stable used. - // This is so on repeats ticks don't appear too late to be visually processed by the player. - offset = 200; - else - offset = TimeFadein * 0.66f; - - TimePreempt = (StartTime - SpanStartTime) / 2 + offset; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Osu.Objects +{ + public class SliderTick : OsuHitObject + { + public int SpanIndex { get; set; } + public double SpanStartTime { get; set; } + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + double offset; + + if (SpanIndex > 0) + // Adding 200 to include the offset stable used. + // This is so on repeats ticks don't appear too late to be visually processed by the player. + offset = 200; + else + offset = TimeFadein * 0.66f; + + TimePreempt = (StartTime - SpanStartTime) / 2 + offset; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index b30e4cb932..503ad85674 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Osu.Objects -{ - public class Spinner : OsuHitObject, IHasEndTime - { - public double EndTime { get; set; } - public double Duration => EndTime - StartTime; - - /// - /// Number of spins required to finish the spinner without miss. - /// - public int SpinsRequired { get; protected set; } = 1; - - public override bool NewCombo => true; - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5)); - - // spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being. - SpinsRequired = (int)Math.Max(1, SpinsRequired * 0.6); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Osu.Objects +{ + public class Spinner : OsuHitObject, IHasEndTime + { + public double EndTime { get; set; } + public double Duration => EndTime - StartTime; + + /// + /// Number of spins required to finish the spinner without miss. + /// + public int SpinsRequired { get; protected set; } = 1; + + public override bool NewCombo => true; + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5)); + + // spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being. + SpinsRequired = (int)Math.Max(1, SpinsRequired * 0.6); + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs index fb5acbb643..926a7975f3 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs @@ -1,78 +1,78 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Beatmaps; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; -using osu.Game.Rulesets.Osu.OsuDifficulty.Skills; - -namespace osu.Game.Rulesets.Osu.OsuDifficulty -{ - public class OsuDifficultyCalculator : DifficultyCalculator - { - private const int section_length = 400; - private const double difficulty_multiplier = 0.0675; - - public OsuDifficultyCalculator(Beatmap beatmap) - : base(beatmap) - { - } - - public OsuDifficultyCalculator(Beatmap beatmap, Mod[] mods) - : base(beatmap, mods) - { - } - - protected override void PreprocessHitObjects() - { - new OsuBeatmapProcessor().PostProcess(Beatmap); - } - - public override double Calculate(Dictionary categoryDifficulty = null) - { - OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate); - Skill[] skills = - { - new Aim(), - new Speed() - }; - - double sectionEnd = section_length / TimeRate; - foreach (OsuDifficultyHitObject h in beatmap) - { - while (h.BaseObject.StartTime > sectionEnd) - { - foreach (Skill s in skills) - { - s.SaveCurrentPeak(); - s.StartNewSectionFrom(sectionEnd); - } - - sectionEnd += section_length; - } - - foreach (Skill s in skills) - s.Process(h); - } - - double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; - - double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; - - if (categoryDifficulty != null) - { - categoryDifficulty.Add("Aim", aimRating); - categoryDifficulty.Add("Speed", speedRating); - } - - return starRating; - } - - protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new OsuBeatmapConverter(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; +using osu.Game.Rulesets.Osu.OsuDifficulty.Skills; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty +{ + public class OsuDifficultyCalculator : DifficultyCalculator + { + private const int section_length = 400; + private const double difficulty_multiplier = 0.0675; + + public OsuDifficultyCalculator(Beatmap beatmap) + : base(beatmap) + { + } + + public OsuDifficultyCalculator(Beatmap beatmap, Mod[] mods) + : base(beatmap, mods) + { + } + + protected override void PreprocessHitObjects() + { + new OsuBeatmapProcessor().PostProcess(Beatmap); + } + + public override double Calculate(Dictionary categoryDifficulty = null) + { + OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate); + Skill[] skills = + { + new Aim(), + new Speed() + }; + + double sectionEnd = section_length / TimeRate; + foreach (OsuDifficultyHitObject h in beatmap) + { + while (h.BaseObject.StartTime > sectionEnd) + { + foreach (Skill s in skills) + { + s.SaveCurrentPeak(); + s.StartNewSectionFrom(sectionEnd); + } + + sectionEnd += section_length; + } + + foreach (Skill s in skills) + s.Process(h); + } + + double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + + double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; + + if (categoryDifficulty != null) + { + categoryDifficulty.Add("Aim", aimRating); + categoryDifficulty.Add("Speed", speedRating); + } + + return starRating; + } + + protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new OsuBeatmapConverter(); + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs index a1a2687cea..5c8ab0f3d4 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs @@ -1,94 +1,94 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections; -using System.Collections.Generic; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing -{ - /// - /// An enumerable container wrapping input as - /// which contains extra data required for difficulty calculation. - /// - public class OsuDifficultyBeatmap : IEnumerable - { - private readonly IEnumerator difficultyObjects; - private readonly Queue onScreen = new Queue(); - - /// - /// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as - /// which contains extra data required for difficulty calculation. - /// - public OsuDifficultyBeatmap(List objects, double timeRate) - { - // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases. - // This should probably happen before the objects reach the difficulty calculator. - objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime)); - difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate); - } - - /// - /// Returns an enumerator that enumerates all s in the . - /// The inner loop adds objects that appear on screen into a queue until we need to hit the next object. - /// The outer loop returns objects from this queue one at a time, only after they had to be hit, and should no longer be on screen. - /// This means that we can loop through every object that is on screen at the time when a new one appears, - /// allowing us to determine a reading strain for the object that just appeared. - /// - public IEnumerator GetEnumerator() - { - while (true) - { - // Add upcoming objects to the queue until we have at least one object that had been hit and can be dequeued. - // This means there is always at least one object in the queue unless we reached the end of the map. - do - { - if (!difficultyObjects.MoveNext()) - break; // New objects can't be added anymore, but we still need to dequeue and return the ones already on screen. - - OsuDifficultyHitObject latest = difficultyObjects.Current; - // Calculate flow values here - - foreach (OsuDifficultyHitObject h in onScreen) - { - // ReSharper disable once PossibleNullReferenceException (resharper not smart enough to understand IEnumerator.MoveNext()) - h.TimeUntilHit -= latest.DeltaTime; - // Calculate reading strain here - } - - onScreen.Enqueue(latest); - } - while (onScreen.Peek().TimeUntilHit > 0); // Keep adding new objects on screen while there is still time before we have to hit the next one. - - if (onScreen.Count == 0) break; // We have reached the end of the map and enumerated all the objects. - yield return onScreen.Dequeue(); // Remove and return objects one by one that had to be hit before the latest one appeared. - } - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - private IEnumerator createDifficultyObjectEnumerator(List objects, double timeRate) - { - // We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object. - OsuHitObject[] triangle = new OsuHitObject[3]; - - // OsuDifficultyHitObject construction requires three components, an extra copy of the first OsuHitObject is used at the beginning. - if (objects.Count > 1) - { - triangle[1] = objects[0]; // This copy will get shifted to the last spot in the triangle. - triangle[0] = objects[0]; // This component corresponds to the real first OsuHitOject. - } - - // The final component of the first triangle will be the second OsuHitOject of the map, which forms the first jump. - // If the map has less than two OsuHitObjects, the enumerator will not return anything. - for (int i = 1; i < objects.Count; ++i) - { - triangle[2] = triangle[1]; - triangle[1] = triangle[0]; - triangle[0] = objects[i]; - - yield return new OsuDifficultyHitObject(triangle, timeRate); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections; +using System.Collections.Generic; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing +{ + /// + /// An enumerable container wrapping input as + /// which contains extra data required for difficulty calculation. + /// + public class OsuDifficultyBeatmap : IEnumerable + { + private readonly IEnumerator difficultyObjects; + private readonly Queue onScreen = new Queue(); + + /// + /// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as + /// which contains extra data required for difficulty calculation. + /// + public OsuDifficultyBeatmap(List objects, double timeRate) + { + // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases. + // This should probably happen before the objects reach the difficulty calculator. + objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime)); + difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate); + } + + /// + /// Returns an enumerator that enumerates all s in the . + /// The inner loop adds objects that appear on screen into a queue until we need to hit the next object. + /// The outer loop returns objects from this queue one at a time, only after they had to be hit, and should no longer be on screen. + /// This means that we can loop through every object that is on screen at the time when a new one appears, + /// allowing us to determine a reading strain for the object that just appeared. + /// + public IEnumerator GetEnumerator() + { + while (true) + { + // Add upcoming objects to the queue until we have at least one object that had been hit and can be dequeued. + // This means there is always at least one object in the queue unless we reached the end of the map. + do + { + if (!difficultyObjects.MoveNext()) + break; // New objects can't be added anymore, but we still need to dequeue and return the ones already on screen. + + OsuDifficultyHitObject latest = difficultyObjects.Current; + // Calculate flow values here + + foreach (OsuDifficultyHitObject h in onScreen) + { + // ReSharper disable once PossibleNullReferenceException (resharper not smart enough to understand IEnumerator.MoveNext()) + h.TimeUntilHit -= latest.DeltaTime; + // Calculate reading strain here + } + + onScreen.Enqueue(latest); + } + while (onScreen.Peek().TimeUntilHit > 0); // Keep adding new objects on screen while there is still time before we have to hit the next one. + + if (onScreen.Count == 0) break; // We have reached the end of the map and enumerated all the objects. + yield return onScreen.Dequeue(); // Remove and return objects one by one that had to be hit before the latest one appeared. + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private IEnumerator createDifficultyObjectEnumerator(List objects, double timeRate) + { + // We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object. + OsuHitObject[] triangle = new OsuHitObject[3]; + + // OsuDifficultyHitObject construction requires three components, an extra copy of the first OsuHitObject is used at the beginning. + if (objects.Count > 1) + { + triangle[1] = objects[0]; // This copy will get shifted to the last spot in the triangle. + triangle[0] = objects[0]; // This component corresponds to the real first OsuHitOject. + } + + // The final component of the first triangle will be the second OsuHitOject of the map, which forms the first jump. + // If the map has less than two OsuHitObjects, the enumerator will not return anything. + for (int i = 1; i < objects.Count; ++i) + { + triangle[2] = triangle[1]; + triangle[1] = triangle[0]; + triangle[0] = objects[i]; + + yield return new OsuDifficultyHitObject(triangle, timeRate); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs index c817cd0ff3..415f76ced8 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -1,116 +1,116 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using OpenTK; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing -{ - /// - /// A wrapper around extending it with additional data required for difficulty calculation. - /// - public class OsuDifficultyHitObject - { - /// - /// The this refers to. - /// - public OsuHitObject BaseObject { get; } - - /// - /// Normalized distance from the of the previous . - /// - public double Distance { get; private set; } - - /// - /// Milliseconds elapsed since the StartTime of the previous . - /// - public double DeltaTime { get; private set; } - - /// - /// Number of milliseconds until the has to be hit. - /// - public double TimeUntilHit { get; set; } - - private const int normalized_radius = 52; - - private readonly double timeRate; - - private readonly OsuHitObject[] t; - - /// - /// Initializes the object calculating extra data required for difficulty calculation. - /// - public OsuDifficultyHitObject(OsuHitObject[] triangle, double timeRate) - { - this.timeRate = timeRate; - - t = triangle; - BaseObject = t[0]; - setDistances(); - setTimingValues(); - // Calculate angle here - } - - private void setDistances() - { - // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. - double scalingFactor = normalized_radius / BaseObject.Radius; - if (BaseObject.Radius < 30) - { - double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50; - scalingFactor *= 1 + smallCircleBonus; - } - - Vector2 lastCursorPosition = t[1].StackedPosition; - float lastTravelDistance = 0; - - var lastSlider = t[1] as Slider; - if (lastSlider != null) - { - computeSliderCursorPosition(lastSlider); - lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition; - lastTravelDistance = lastSlider.LazyTravelDistance; - } - - Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor; - } - - private void setTimingValues() - { - // Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure. - DeltaTime = Math.Max(40, (t[0].StartTime - t[1].StartTime) / timeRate); - TimeUntilHit = 450; // BaseObject.PreEmpt; - } - - private void computeSliderCursorPosition(Slider slider) - { - if (slider.LazyEndPosition != null) - return; - slider.LazyEndPosition = slider.StackedPosition; - - float approxFollowCircleRadius = (float)(slider.Radius * 3); - var computeVertex = new Action(t => - { - // ReSharper disable once PossibleInvalidOperationException (bugged in current r# version) - var diff = slider.StackedPositionAt(t) - slider.LazyEndPosition.Value; - float dist = diff.Length; - - if (dist > approxFollowCircleRadius) - { - // The cursor would be outside the follow circle, we need to move it - diff.Normalize(); // Obtain direction of diff - dist -= approxFollowCircleRadius; - slider.LazyEndPosition += diff * dist; - slider.LazyTravelDistance += dist; - } - }); - - var scoringTimes = slider.NestedHitObjects.Select(t => t.StartTime); - foreach (var time in scoringTimes) - computeVertex(time); - computeVertex(slider.EndTime); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using OpenTK; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing +{ + /// + /// A wrapper around extending it with additional data required for difficulty calculation. + /// + public class OsuDifficultyHitObject + { + /// + /// The this refers to. + /// + public OsuHitObject BaseObject { get; } + + /// + /// Normalized distance from the of the previous . + /// + public double Distance { get; private set; } + + /// + /// Milliseconds elapsed since the StartTime of the previous . + /// + public double DeltaTime { get; private set; } + + /// + /// Number of milliseconds until the has to be hit. + /// + public double TimeUntilHit { get; set; } + + private const int normalized_radius = 52; + + private readonly double timeRate; + + private readonly OsuHitObject[] t; + + /// + /// Initializes the object calculating extra data required for difficulty calculation. + /// + public OsuDifficultyHitObject(OsuHitObject[] triangle, double timeRate) + { + this.timeRate = timeRate; + + t = triangle; + BaseObject = t[0]; + setDistances(); + setTimingValues(); + // Calculate angle here + } + + private void setDistances() + { + // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. + double scalingFactor = normalized_radius / BaseObject.Radius; + if (BaseObject.Radius < 30) + { + double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50; + scalingFactor *= 1 + smallCircleBonus; + } + + Vector2 lastCursorPosition = t[1].StackedPosition; + float lastTravelDistance = 0; + + var lastSlider = t[1] as Slider; + if (lastSlider != null) + { + computeSliderCursorPosition(lastSlider); + lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition; + lastTravelDistance = lastSlider.LazyTravelDistance; + } + + Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor; + } + + private void setTimingValues() + { + // Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure. + DeltaTime = Math.Max(40, (t[0].StartTime - t[1].StartTime) / timeRate); + TimeUntilHit = 450; // BaseObject.PreEmpt; + } + + private void computeSliderCursorPosition(Slider slider) + { + if (slider.LazyEndPosition != null) + return; + slider.LazyEndPosition = slider.StackedPosition; + + float approxFollowCircleRadius = (float)(slider.Radius * 3); + var computeVertex = new Action(t => + { + // ReSharper disable once PossibleInvalidOperationException (bugged in current r# version) + var diff = slider.StackedPositionAt(t) - slider.LazyEndPosition.Value; + float dist = diff.Length; + + if (dist > approxFollowCircleRadius) + { + // The cursor would be outside the follow circle, we need to move it + diff.Normalize(); // Obtain direction of diff + dist -= approxFollowCircleRadius; + slider.LazyEndPosition += diff * dist; + slider.LazyTravelDistance += dist; + } + }); + + var scoringTimes = slider.NestedHitObjects.Select(t => t.StartTime); + foreach (var time in scoringTimes) + computeVertex(time); + computeVertex(slider.EndTime); + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs index 361b31c7bd..5c736d7bb5 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; - -namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills -{ - /// - /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. - /// - public class Aim : Skill - { - protected override double SkillMultiplier => 26.25; - protected override double StrainDecayBase => 0.15; - - protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills +{ + /// + /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. + /// + public class Aim : Skill + { + protected override double SkillMultiplier => 26.25; + protected override double StrainDecayBase => 0.15; + + protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime; + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs index db9658087b..983599432f 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs @@ -1,100 +1,100 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; -using osu.Game.Rulesets.Osu.OsuDifficulty.Utils; - -namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills -{ - /// - /// Used to processes strain values of s, keep track of strain levels caused by the processed objects - /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects. - /// - public abstract class Skill - { - /// - /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. - /// - protected abstract double SkillMultiplier { get; } - - /// - /// Determines how quickly strain decays for the given skill. - /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second. - /// - protected abstract double StrainDecayBase { get; } - - /// - /// s that were processed previously. They can affect the strain values of the following objects. - /// - protected readonly History Previous = new History(2); // Contained objects not used yet - - private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap. - private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. - private readonly List strainPeaks = new List(); - - /// - /// Process an and update current strain values accordingly. - /// - public void Process(OsuDifficultyHitObject current) - { - currentStrain *= strainDecay(current.DeltaTime); - if (!(current.BaseObject is Spinner)) - currentStrain += StrainValueOf(current) * SkillMultiplier; - - currentSectionPeak = Math.Max(currentStrain, currentSectionPeak); - - Previous.Push(current); - } - - /// - /// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty. - /// - public void SaveCurrentPeak() - { - if (Previous.Count > 0) - strainPeaks.Add(currentSectionPeak); - } - - /// - /// Sets the initial strain level for a new section. - /// - /// The beginning of the new section in milliseconds - public void StartNewSectionFrom(double offset) - { - // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries. - // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level. - if (Previous.Count > 0) - currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime); - } - - /// - /// Returns the calculated difficulty value representing all processed s. - /// - public double DifficultyValue() - { - strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - double difficulty = 0; - double weight = 1; - - // Difficulty is the weighted sum of the highest strains from every section. - foreach (double strain in strainPeaks) - { - difficulty += strain * weight; - weight *= 0.9; - } - - return difficulty; - } - - /// - /// Calculates the strain value of an . This value is affected by previously processed objects. - /// - protected abstract double StrainValueOf(OsuDifficultyHitObject current); - - private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; +using osu.Game.Rulesets.Osu.OsuDifficulty.Utils; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills +{ + /// + /// Used to processes strain values of s, keep track of strain levels caused by the processed objects + /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects. + /// + public abstract class Skill + { + /// + /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. + /// + protected abstract double SkillMultiplier { get; } + + /// + /// Determines how quickly strain decays for the given skill. + /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second. + /// + protected abstract double StrainDecayBase { get; } + + /// + /// s that were processed previously. They can affect the strain values of the following objects. + /// + protected readonly History Previous = new History(2); // Contained objects not used yet + + private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap. + private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. + private readonly List strainPeaks = new List(); + + /// + /// Process an and update current strain values accordingly. + /// + public void Process(OsuDifficultyHitObject current) + { + currentStrain *= strainDecay(current.DeltaTime); + if (!(current.BaseObject is Spinner)) + currentStrain += StrainValueOf(current) * SkillMultiplier; + + currentSectionPeak = Math.Max(currentStrain, currentSectionPeak); + + Previous.Push(current); + } + + /// + /// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty. + /// + public void SaveCurrentPeak() + { + if (Previous.Count > 0) + strainPeaks.Add(currentSectionPeak); + } + + /// + /// Sets the initial strain level for a new section. + /// + /// The beginning of the new section in milliseconds + public void StartNewSectionFrom(double offset) + { + // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries. + // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level. + if (Previous.Count > 0) + currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime); + } + + /// + /// Returns the calculated difficulty value representing all processed s. + /// + public double DifficultyValue() + { + strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + double difficulty = 0; + double weight = 1; + + // Difficulty is the weighted sum of the highest strains from every section. + foreach (double strain in strainPeaks) + { + difficulty += strain * weight; + weight *= 0.9; + } + + return difficulty; + } + + /// + /// Calculates the strain value of an . This value is affected by previously processed objects. + /// + protected abstract double StrainValueOf(OsuDifficultyHitObject current); + + private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000); + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs index b85904acf9..ae3caa1e66 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs @@ -1,39 +1,39 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; - -namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills -{ - /// - /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. - /// - public class Speed : Skill - { - protected override double SkillMultiplier => 1400; - protected override double StrainDecayBase => 0.3; - - private const double single_spacing_threshold = 125; - private const double stream_spacing_threshold = 110; - private const double almost_diameter = 90; - - protected override double StrainValueOf(OsuDifficultyHitObject current) - { - double distance = current.Distance; - - double speedValue; - if (distance > single_spacing_threshold) - speedValue = 2.5; - else if (distance > stream_spacing_threshold) - speedValue = 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold); - else if (distance > almost_diameter) - speedValue = 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter); - else if (distance > almost_diameter / 2) - speedValue = 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2); - else - speedValue = 0.95; - - return speedValue / current.DeltaTime; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills +{ + /// + /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. + /// + public class Speed : Skill + { + protected override double SkillMultiplier => 1400; + protected override double StrainDecayBase => 0.3; + + private const double single_spacing_threshold = 125; + private const double stream_spacing_threshold = 110; + private const double almost_diameter = 90; + + protected override double StrainValueOf(OsuDifficultyHitObject current) + { + double distance = current.Distance; + + double speedValue; + if (distance > single_spacing_threshold) + speedValue = 2.5; + else if (distance > stream_spacing_threshold) + speedValue = 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold); + else if (distance > almost_diameter) + speedValue = 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter); + else if (distance > almost_diameter / 2) + speedValue = 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2); + else + speedValue = 0.95; + + return speedValue / current.DeltaTime; + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs index 9f07248abf..f6933a3e5d 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs @@ -1,86 +1,86 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Osu.OsuDifficulty.Utils -{ - /// - /// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full. - /// Indexing starts at the top of the stack. - /// - public class History : IEnumerable - { - public int Count { get; private set; } - - private readonly T[] array; - private readonly int capacity; - private int marker; // Marks the position of the most recently added item. - - /// - /// Initializes a new instance of the History class that is empty and has the specified capacity. - /// - /// The number of items the History can hold. - public History(int capacity) - { - if (capacity < 0) - throw new ArgumentOutOfRangeException(); - - this.capacity = capacity; - array = new T[capacity]; - marker = capacity; // Set marker to the end of the array, outside of the indexed range by one. - } - - /// - /// The most recently added item is returned at index 0. - /// - public T this[int i] - { - get - { - if (i < 0 || i > Count - 1) - throw new IndexOutOfRangeException(); - - i += marker; - if (i > capacity - 1) - i -= capacity; - - return array[i]; - } - } - - /// - /// Adds the item as the most recent one in the history. - /// The oldest item is disposed if the history is full. - /// - public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition. - { - if (marker == 0) - marker = capacity - 1; - else - --marker; - - array[marker] = item; - - if (Count < capacity) - ++Count; - } - - /// - /// Returns an enumerator which enumerates items in the history starting from the most recently added one. - /// - public IEnumerator GetEnumerator() - { - for (int i = marker; i < capacity; ++i) - yield return array[i]; - - if (Count == capacity) - for (int i = 0; i < marker; ++i) - yield return array[i]; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Osu.OsuDifficulty.Utils +{ + /// + /// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full. + /// Indexing starts at the top of the stack. + /// + public class History : IEnumerable + { + public int Count { get; private set; } + + private readonly T[] array; + private readonly int capacity; + private int marker; // Marks the position of the most recently added item. + + /// + /// Initializes a new instance of the History class that is empty and has the specified capacity. + /// + /// The number of items the History can hold. + public History(int capacity) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException(); + + this.capacity = capacity; + array = new T[capacity]; + marker = capacity; // Set marker to the end of the array, outside of the indexed range by one. + } + + /// + /// The most recently added item is returned at index 0. + /// + public T this[int i] + { + get + { + if (i < 0 || i > Count - 1) + throw new IndexOutOfRangeException(); + + i += marker; + if (i > capacity - 1) + i -= capacity; + + return array[i]; + } + } + + /// + /// Adds the item as the most recent one in the history. + /// The oldest item is disposed if the history is full. + /// + public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition. + { + if (marker == 0) + marker = capacity - 1; + else + --marker; + + array[marker] = item; + + if (Count < capacity) + ++Count; + } + + /// + /// Returns an enumerator which enumerates items in the history starting from the most recently added one. + /// + public IEnumerator GetEnumerator() + { + for (int i = marker; i < capacity; ++i) + yield return array[i]; + + if (Count == capacity) + for (int i = 0; i < marker; ++i) + yield return array[i]; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 1a7f00ea51..d9ae836e0a 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.ComponentModel; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Osu -{ - public class OsuInputManager : RulesetInputManager - { - public IEnumerable PressedActions => KeyBindingContainer.PressedActions; - - public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique) - { - } - } - - public enum OsuAction - { - [Description("Left Button")] - LeftButton, - [Description("Right Button")] - RightButton - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.ComponentModel; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu +{ + public class OsuInputManager : RulesetInputManager + { + public IEnumerable PressedActions => KeyBindingContainer.PressedActions; + + public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique) + { + } + } + + public enum OsuAction + { + [Description("Left Button")] + LeftButton, + [Description("Right Button")] + RightButton + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index d407835a96..37e6ec3817 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -1,157 +1,157 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.OsuDifficulty; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.UI; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Game.Overlays.Settings; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Osu.Scoring; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Replays; -using osu.Game.Rulesets.Replays.Types; - -namespace osu.Game.Rulesets.Osu -{ - public class OsuRuleset : Ruleset - { - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuRulesetContainer(this, beatmap, isForCurrentRuleset); - - public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] - { - new KeyBinding(InputKey.Z, OsuAction.LeftButton), - new KeyBinding(InputKey.X, OsuAction.RightButton), - new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton), - new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), - }; - - public override IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) - { - IEnumerable hitObjects = beatmap.Beatmap.HitObjects; - IEnumerable circles = hitObjects.Where(c => !(c is IHasEndTime)); - IEnumerable sliders = hitObjects.Where(s => s is IHasCurve); - IEnumerable spinners = hitObjects.Where(s => s is IHasEndTime && !(s is IHasCurve)); - - return new[] - { - new BeatmapStatistic - { - Name = @"Circle Count", - Content = circles.Count().ToString(), - Icon = FontAwesome.fa_circle_o - }, - new BeatmapStatistic - { - Name = @"Slider Count", - Content = sliders.Count().ToString(), - Icon = FontAwesome.fa_circle - }, - new BeatmapStatistic - { - Name = @"Spinner Count", - Content = spinners.Count().ToString(), - Icon = FontAwesome.fa_circle - } - }; - } - - public override IEnumerable GetModsFor(ModType type) - { - switch (type) - { - case ModType.DifficultyReduction: - return new Mod[] - { - new OsuModEasy(), - new OsuModNoFail(), - new MultiMod - { - Mods = new Mod[] - { - new OsuModHalfTime(), - new OsuModDaycore(), - }, - }, - }; - - case ModType.DifficultyIncrease: - return new Mod[] - { - new OsuModHardRock(), - new MultiMod - { - Mods = new Mod[] - { - new OsuModSuddenDeath(), - new OsuModPerfect(), - }, - }, - new MultiMod - { - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModNightcore(), - }, - }, - new OsuModHidden(), - new OsuModFlashlight(), - }; - - case ModType.Special: - return new Mod[] - { - new OsuModRelax(), - new OsuModAutopilot(), - new OsuModSpunOut(), - new MultiMod - { - Mods = new Mod[] - { - new OsuModAutoplay(), - new ModCinema(), - }, - }, - new OsuModTarget(), - }; - - default: - return new Mod[] { }; - } - } - - public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; - - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods); - - public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score); - - public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); - - public override string Description => "osu!"; - - public override string ShortName => "osu"; - - public override SettingsSubsection CreateSettings() => new OsuSettings(); - - public override int? LegacyID => 0; - - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); - - public OsuRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.OsuDifficulty; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Overlays.Settings; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays.Types; + +namespace osu.Game.Rulesets.Osu +{ + public class OsuRuleset : Ruleset + { + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuRulesetContainer(this, beatmap, isForCurrentRuleset); + + public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] + { + new KeyBinding(InputKey.Z, OsuAction.LeftButton), + new KeyBinding(InputKey.X, OsuAction.RightButton), + new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton), + new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), + }; + + public override IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) + { + IEnumerable hitObjects = beatmap.Beatmap.HitObjects; + IEnumerable circles = hitObjects.Where(c => !(c is IHasEndTime)); + IEnumerable sliders = hitObjects.Where(s => s is IHasCurve); + IEnumerable spinners = hitObjects.Where(s => s is IHasEndTime && !(s is IHasCurve)); + + return new[] + { + new BeatmapStatistic + { + Name = @"Circle Count", + Content = circles.Count().ToString(), + Icon = FontAwesome.fa_circle_o + }, + new BeatmapStatistic + { + Name = @"Slider Count", + Content = sliders.Count().ToString(), + Icon = FontAwesome.fa_circle + }, + new BeatmapStatistic + { + Name = @"Spinner Count", + Content = spinners.Count().ToString(), + Icon = FontAwesome.fa_circle + } + }; + } + + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new OsuModEasy(), + new OsuModNoFail(), + new MultiMod + { + Mods = new Mod[] + { + new OsuModHalfTime(), + new OsuModDaycore(), + }, + }, + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new OsuModHardRock(), + new MultiMod + { + Mods = new Mod[] + { + new OsuModSuddenDeath(), + new OsuModPerfect(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModNightcore(), + }, + }, + new OsuModHidden(), + new OsuModFlashlight(), + }; + + case ModType.Special: + return new Mod[] + { + new OsuModRelax(), + new OsuModAutopilot(), + new OsuModSpunOut(), + new MultiMod + { + Mods = new Mod[] + { + new OsuModAutoplay(), + new ModCinema(), + }, + }, + new OsuModTarget(), + }; + + default: + return new Mod[] { }; + } + } + + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; + + public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods); + + public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score); + + public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); + + public override string Description => "osu!"; + + public override string ShortName => "osu"; + + public override SettingsSubsection CreateSettings() => new OsuSettings(); + + public override int? LegacyID => 0; + + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); + + public OsuRuleset(RulesetInfo rulesetInfo = null) + : base(rulesetInfo) + { + } + } +} diff --git a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs index ea2c2c6729..b61c53be6a 100644 --- a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Runtime.CompilerServices; - -// We publish our internal attributes to other sub-projects of the framework. -// Note, that we omit visual tests as they are meant to test the framework -// behavior "in the wild". - -[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")] -[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Dynamic")] +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Runtime.CompilerServices; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 699cdf75a2..ad4ea343d2 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -1,335 +1,335 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Osu.Objects; -using System; -using System.Linq; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Replays -{ - public class OsuAutoGenerator : OsuAutoGeneratorBase - { - #region Parameters - - /// - /// If delayed movements should be used, causing the cursor to stay on each hitobject for as long as possible. - /// Mainly for Autopilot. - /// - public bool DelayedMovements; // ModManager.CheckActive(Mods.Relax2); - - #endregion - - #region Constants - - /// - /// The "reaction time" in ms between "seeing" a new hit object and moving to "react" to it. - /// - private readonly double reactionTime; - - /// - /// What easing to use when moving between hitobjects - /// - private Easing preferredEasing => DelayedMovements ? Easing.InOutCubic : Easing.Out; - - #endregion - - #region Construction / Initialisation - - public OsuAutoGenerator(Beatmap beatmap) - : base(beatmap) - { - // Already superhuman, but still somewhat realistic - reactionTime = ApplyModsToRate(100); - } - - #endregion - - #region Generator - - /// - /// Which button (left or right) to use for the current hitobject. - /// Even means LMB will be used to click, odd means RMB will be used. - /// This keeps track of the button previously used for alt/singletap logic. - /// - private int buttonIndex; - - public override Replay Generate() - { - buttonIndex = 0; - - AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500))); - AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500))); - AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500))); - - for (int i = 0; i < Beatmap.HitObjects.Count; i++) - { - OsuHitObject h = Beatmap.HitObjects[i]; - - if (DelayedMovements && i > 0) - { - OsuHitObject prev = Beatmap.HitObjects[i - 1]; - addDelayedMovements(h, prev); - } - - addHitObjectReplay(h); - } - - return Replay; - } - - private void addDelayedMovements(OsuHitObject h, OsuHitObject prev) - { - double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime; - - // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). - if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) - { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); - } - else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) - { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); - } - else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Good) + 50) - { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); - if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); - } - } - - private void addHitObjectReplay(OsuHitObject h) - { - // Default values for circles/sliders - Vector2 startPosition = h.StackedPosition; - Easing easing = preferredEasing; - float spinnerDirection = -1; - - // The startPosition for the slider should not be its .Position, but the point on the circle whose tangent crosses the current cursor position - // We also modify spinnerDirection so it spins in the direction it enters the spin circle, to make a smooth transition. - // TODO: Shouldn't the spinner always spin in the same direction? - if (h is Spinner) - { - calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[Frames.Count - 1]).Position, out startPosition, out spinnerDirection); - - Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[Frames.Count - 1]).Position; - - if (spinCentreOffset.Length > SPIN_RADIUS) - { - // If moving in from the outside, don't ease out (default eases out). This means auto will "start" spinning immediately after moving into position. - easing = Easing.In; - } - } - - // Do some nice easing for cursor movements - if (Frames.Count > 0) - { - moveToHitObject(h, startPosition, easing); - } - - // Add frames to click the hitobject - addHitObjectClickFrames(h, startPosition, spinnerDirection); - } - - #endregion - - #region Helper subroutines - - private static void calcSpinnerStartPosAndDirection(Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) - { - Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos; - float distFromCentre = spinCentreOffset.Length; - float distToTangentPoint = (float)Math.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS); - - if (distFromCentre > SPIN_RADIUS) - { - // Previous cursor position was outside spin circle, set startPosition to the tangent point. - - // Angle between centre offset and tangent point offset. - float angle = (float)Math.Asin(SPIN_RADIUS / distFromCentre); - - if (angle > 0) - { - spinnerDirection = -1; - } - else - { - spinnerDirection = 1; - } - - // Rotate by angle so it's parallel to tangent line - spinCentreOffset.X = spinCentreOffset.X * (float)Math.Cos(angle) - spinCentreOffset.Y * (float)Math.Sin(angle); - spinCentreOffset.Y = spinCentreOffset.X * (float)Math.Sin(angle) + spinCentreOffset.Y * (float)Math.Cos(angle); - - // Set length to distToTangentPoint - spinCentreOffset.Normalize(); - spinCentreOffset *= distToTangentPoint; - - // Move along the tangent line, now startPosition is at the tangent point. - startPosition = prevPos + spinCentreOffset; - } - else if (spinCentreOffset.Length > 0) - { - // Previous cursor position was inside spin circle, set startPosition to the nearest point on spin circle. - startPosition = SPINNER_CENTRE - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); - spinnerDirection = 1; - } - else - { - // Degenerate case where cursor position is exactly at the centre of the spin circle. - startPosition = SPINNER_CENTRE + new Vector2(0, -SPIN_RADIUS); - spinnerDirection = 1; - } - } - - private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing) - { - OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1]; - - // Wait until Auto could "see and react" to the next note. - double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime); - if (waitTime > lastFrame.Time) - { - lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions }; - AddFrameToReplay(lastFrame); - } - - Vector2 lastPosition = lastFrame.Position; - - double timeDifference = ApplyModsToTime(h.StartTime - lastFrame.Time); - - // Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up. - if (timeDifference > 0 && // Sanity checks - ((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough - timeDifference >= 266)) // ... or the beats are slow enough to tap anyway. - { - // Perform eased movement - for (double time = lastFrame.Time + FrameDelay; time < h.StartTime; time += FrameDelay) - { - Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); - AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions }); - } - - buttonIndex = 0; - } - else - { - buttonIndex++; - } - } - - // Add frames to click the hitobject - private void addHitObjectClickFrames(OsuHitObject h, Vector2 startPosition, float spinnerDirection) - { - // Time to insert the first frame which clicks the object - // Here we mainly need to determine which button to use - var action = buttonIndex % 2 == 0 ? OsuAction.LeftButton : OsuAction.RightButton; - - var startFrame = new OsuReplayFrame(h.StartTime, new Vector2(startPosition.X, startPosition.Y), action); - - // TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime. - double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY; - int endDelay = h is Spinner ? 1 : 0; - var endFrame = new OsuReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y)); - - // Decrement because we want the previous frame, not the next one - int index = FindInsertionIndex(startFrame) - 1; - - // If the previous frame has a button pressed, force alternation. - // If there are frames ahead, modify those to use the new button press. - // Do we have a previous frame? No need to check for < replay.Count since we decremented! - if (index >= 0) - { - var previousFrame = (OsuReplayFrame)Frames[index]; - var previousActions = previousFrame.Actions; - - // If a button is already held, then we simply alternate - if (previousActions.Any()) - { - // Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button. - if (previousActions.Contains(action)) - { - action = action == OsuAction.LeftButton ? OsuAction.RightButton : OsuAction.LeftButton; - startFrame.Actions.Clear(); - startFrame.Actions.Add(action); - } - - // We always follow the most recent slider / spinner, so remove any other frames that occur while it exists. - int endIndex = FindInsertionIndex(endFrame); - - if (index < Frames.Count - 1) - Frames.RemoveRange(index + 1, Math.Max(0, endIndex - (index + 1))); - - // After alternating we need to keep holding the other button in the future rather than the previous one. - for (int j = index + 1; j < Frames.Count; ++j) - { - var frame = (OsuReplayFrame)Frames[j]; - - // Don't affect frames which stop pressing a button! - if (j < Frames.Count - 1 || frame.Actions.SequenceEqual(previousActions)) - { - frame.Actions.Clear(); - frame.Actions.Add(action); - } - } - } - } - - AddFrameToReplay(startFrame); - - // We add intermediate frames for spinning / following a slider here. - if (h is Spinner) - { - Spinner s = h as Spinner; - - Vector2 difference = startPosition - SPINNER_CENTRE; - - float radius = difference.Length; - float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X); - - double t; - - for (double j = h.StartTime + FrameDelay; j < s.EndTime; j += FrameDelay) - { - t = ApplyModsToTime(j - h.StartTime) * spinnerDirection; - - Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS); - AddFrameToReplay(new OsuReplayFrame((int)j, new Vector2(pos.X, pos.Y), action)); - } - - t = ApplyModsToTime(s.EndTime - h.StartTime) * spinnerDirection; - Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS); - - AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(endPosition.X, endPosition.Y), action)); - - endFrame.Position = endPosition; - } - else if (h is Slider) - { - Slider s = h as Slider; - - for (double j = FrameDelay; j < s.Duration; j += FrameDelay) - { - Vector2 pos = s.StackedPositionAt(j / s.Duration); - AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action)); - } - - AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(s.StackedEndPosition.X, s.StackedEndPosition.Y), action)); - } - - // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! - if (Frames[Frames.Count - 1].Time <= endFrame.Time) - AddFrameToReplay(endFrame); - } - - #endregion - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using System; +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Replays +{ + public class OsuAutoGenerator : OsuAutoGeneratorBase + { + #region Parameters + + /// + /// If delayed movements should be used, causing the cursor to stay on each hitobject for as long as possible. + /// Mainly for Autopilot. + /// + public bool DelayedMovements; // ModManager.CheckActive(Mods.Relax2); + + #endregion + + #region Constants + + /// + /// The "reaction time" in ms between "seeing" a new hit object and moving to "react" to it. + /// + private readonly double reactionTime; + + /// + /// What easing to use when moving between hitobjects + /// + private Easing preferredEasing => DelayedMovements ? Easing.InOutCubic : Easing.Out; + + #endregion + + #region Construction / Initialisation + + public OsuAutoGenerator(Beatmap beatmap) + : base(beatmap) + { + // Already superhuman, but still somewhat realistic + reactionTime = ApplyModsToRate(100); + } + + #endregion + + #region Generator + + /// + /// Which button (left or right) to use for the current hitobject. + /// Even means LMB will be used to click, odd means RMB will be used. + /// This keeps track of the button previously used for alt/singletap logic. + /// + private int buttonIndex; + + public override Replay Generate() + { + buttonIndex = 0; + + AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500))); + AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500))); + AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500))); + + for (int i = 0; i < Beatmap.HitObjects.Count; i++) + { + OsuHitObject h = Beatmap.HitObjects[i]; + + if (DelayedMovements && i > 0) + { + OsuHitObject prev = Beatmap.HitObjects[i - 1]; + addDelayedMovements(h, prev); + } + + addHitObjectReplay(h); + } + + return Replay; + } + + private void addDelayedMovements(OsuHitObject h, OsuHitObject prev) + { + double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime; + + // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). + if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) + { + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + } + else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) + { + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + } + else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Good) + 50) + { + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + } + } + + private void addHitObjectReplay(OsuHitObject h) + { + // Default values for circles/sliders + Vector2 startPosition = h.StackedPosition; + Easing easing = preferredEasing; + float spinnerDirection = -1; + + // The startPosition for the slider should not be its .Position, but the point on the circle whose tangent crosses the current cursor position + // We also modify spinnerDirection so it spins in the direction it enters the spin circle, to make a smooth transition. + // TODO: Shouldn't the spinner always spin in the same direction? + if (h is Spinner) + { + calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[Frames.Count - 1]).Position, out startPosition, out spinnerDirection); + + Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[Frames.Count - 1]).Position; + + if (spinCentreOffset.Length > SPIN_RADIUS) + { + // If moving in from the outside, don't ease out (default eases out). This means auto will "start" spinning immediately after moving into position. + easing = Easing.In; + } + } + + // Do some nice easing for cursor movements + if (Frames.Count > 0) + { + moveToHitObject(h, startPosition, easing); + } + + // Add frames to click the hitobject + addHitObjectClickFrames(h, startPosition, spinnerDirection); + } + + #endregion + + #region Helper subroutines + + private static void calcSpinnerStartPosAndDirection(Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) + { + Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos; + float distFromCentre = spinCentreOffset.Length; + float distToTangentPoint = (float)Math.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS); + + if (distFromCentre > SPIN_RADIUS) + { + // Previous cursor position was outside spin circle, set startPosition to the tangent point. + + // Angle between centre offset and tangent point offset. + float angle = (float)Math.Asin(SPIN_RADIUS / distFromCentre); + + if (angle > 0) + { + spinnerDirection = -1; + } + else + { + spinnerDirection = 1; + } + + // Rotate by angle so it's parallel to tangent line + spinCentreOffset.X = spinCentreOffset.X * (float)Math.Cos(angle) - spinCentreOffset.Y * (float)Math.Sin(angle); + spinCentreOffset.Y = spinCentreOffset.X * (float)Math.Sin(angle) + spinCentreOffset.Y * (float)Math.Cos(angle); + + // Set length to distToTangentPoint + spinCentreOffset.Normalize(); + spinCentreOffset *= distToTangentPoint; + + // Move along the tangent line, now startPosition is at the tangent point. + startPosition = prevPos + spinCentreOffset; + } + else if (spinCentreOffset.Length > 0) + { + // Previous cursor position was inside spin circle, set startPosition to the nearest point on spin circle. + startPosition = SPINNER_CENTRE - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); + spinnerDirection = 1; + } + else + { + // Degenerate case where cursor position is exactly at the centre of the spin circle. + startPosition = SPINNER_CENTRE + new Vector2(0, -SPIN_RADIUS); + spinnerDirection = 1; + } + } + + private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing) + { + OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1]; + + // Wait until Auto could "see and react" to the next note. + double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime); + if (waitTime > lastFrame.Time) + { + lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions }; + AddFrameToReplay(lastFrame); + } + + Vector2 lastPosition = lastFrame.Position; + + double timeDifference = ApplyModsToTime(h.StartTime - lastFrame.Time); + + // Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up. + if (timeDifference > 0 && // Sanity checks + ((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough + timeDifference >= 266)) // ... or the beats are slow enough to tap anyway. + { + // Perform eased movement + for (double time = lastFrame.Time + FrameDelay; time < h.StartTime; time += FrameDelay) + { + Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); + AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions }); + } + + buttonIndex = 0; + } + else + { + buttonIndex++; + } + } + + // Add frames to click the hitobject + private void addHitObjectClickFrames(OsuHitObject h, Vector2 startPosition, float spinnerDirection) + { + // Time to insert the first frame which clicks the object + // Here we mainly need to determine which button to use + var action = buttonIndex % 2 == 0 ? OsuAction.LeftButton : OsuAction.RightButton; + + var startFrame = new OsuReplayFrame(h.StartTime, new Vector2(startPosition.X, startPosition.Y), action); + + // TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime. + double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY; + int endDelay = h is Spinner ? 1 : 0; + var endFrame = new OsuReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y)); + + // Decrement because we want the previous frame, not the next one + int index = FindInsertionIndex(startFrame) - 1; + + // If the previous frame has a button pressed, force alternation. + // If there are frames ahead, modify those to use the new button press. + // Do we have a previous frame? No need to check for < replay.Count since we decremented! + if (index >= 0) + { + var previousFrame = (OsuReplayFrame)Frames[index]; + var previousActions = previousFrame.Actions; + + // If a button is already held, then we simply alternate + if (previousActions.Any()) + { + // Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button. + if (previousActions.Contains(action)) + { + action = action == OsuAction.LeftButton ? OsuAction.RightButton : OsuAction.LeftButton; + startFrame.Actions.Clear(); + startFrame.Actions.Add(action); + } + + // We always follow the most recent slider / spinner, so remove any other frames that occur while it exists. + int endIndex = FindInsertionIndex(endFrame); + + if (index < Frames.Count - 1) + Frames.RemoveRange(index + 1, Math.Max(0, endIndex - (index + 1))); + + // After alternating we need to keep holding the other button in the future rather than the previous one. + for (int j = index + 1; j < Frames.Count; ++j) + { + var frame = (OsuReplayFrame)Frames[j]; + + // Don't affect frames which stop pressing a button! + if (j < Frames.Count - 1 || frame.Actions.SequenceEqual(previousActions)) + { + frame.Actions.Clear(); + frame.Actions.Add(action); + } + } + } + } + + AddFrameToReplay(startFrame); + + // We add intermediate frames for spinning / following a slider here. + if (h is Spinner) + { + Spinner s = h as Spinner; + + Vector2 difference = startPosition - SPINNER_CENTRE; + + float radius = difference.Length; + float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X); + + double t; + + for (double j = h.StartTime + FrameDelay; j < s.EndTime; j += FrameDelay) + { + t = ApplyModsToTime(j - h.StartTime) * spinnerDirection; + + Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS); + AddFrameToReplay(new OsuReplayFrame((int)j, new Vector2(pos.X, pos.Y), action)); + } + + t = ApplyModsToTime(s.EndTime - h.StartTime) * spinnerDirection; + Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS); + + AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(endPosition.X, endPosition.Y), action)); + + endFrame.Position = endPosition; + } + else if (h is Slider) + { + Slider s = h as Slider; + + for (double j = FrameDelay; j < s.Duration; j += FrameDelay) + { + Vector2 pos = s.StackedPositionAt(j / s.Duration); + AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action)); + } + + AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(s.StackedEndPosition.X, s.StackedEndPosition.Y), action)); + } + + // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! + if (Frames[Frames.Count - 1].Time <= endFrame.Time) + AddFrameToReplay(endFrame); + } + + #endregion + } +} diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index b2adeda73b..324d0502c1 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -1,96 +1,96 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Osu.Objects; -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Replays; -using osu.Game.Users; - -namespace osu.Game.Rulesets.Osu.Replays -{ - public abstract class OsuAutoGeneratorBase : AutoGenerator - { - #region Constants - - /// - /// Constants (for spinners). - /// - protected static readonly Vector2 SPINNER_CENTRE = new Vector2(256, 192); - protected const float SPIN_RADIUS = 50; - - /// - /// The time in ms between each ReplayFrame. - /// - protected readonly double FrameDelay; - - #endregion - - #region Construction / Initialisation - - protected Replay Replay; - protected List Frames => Replay.Frames; - - protected OsuAutoGeneratorBase(Beatmap beatmap) - : base(beatmap) - { - Replay = new Replay - { - User = new User - { - Username = @"Autoplay", - } - }; - - // We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps. - FrameDelay = ApplyModsToRate(1000.0 / 60.0); - } - - #endregion - - #region Utilities - protected double ApplyModsToTime(double v) => v; - protected double ApplyModsToRate(double v) => v; - - private class ReplayFrameComparer : IComparer - { - public int Compare(ReplayFrame f1, ReplayFrame f2) - { - if (f1 == null) throw new ArgumentNullException(nameof(f1)); - if (f2 == null) throw new ArgumentNullException(nameof(f2)); - - return f1.Time.CompareTo(f2.Time); - } - } - - private static readonly IComparer replay_frame_comparer = new ReplayFrameComparer(); - - protected int FindInsertionIndex(ReplayFrame frame) - { - int index = Frames.BinarySearch(frame, replay_frame_comparer); - - if (index < 0) - { - index = ~index; - } - else - { - // Go to the first index which is actually bigger - while (index < Frames.Count && frame.Time == Frames[index].Time) - { - ++index; - } - } - - return index; - } - - protected void AddFrameToReplay(ReplayFrame frame) => Frames.Insert(FindInsertionIndex(frame), frame); - - protected static Vector2 CirclePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius)); - - #endregion - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Replays; +using osu.Game.Users; + +namespace osu.Game.Rulesets.Osu.Replays +{ + public abstract class OsuAutoGeneratorBase : AutoGenerator + { + #region Constants + + /// + /// Constants (for spinners). + /// + protected static readonly Vector2 SPINNER_CENTRE = new Vector2(256, 192); + protected const float SPIN_RADIUS = 50; + + /// + /// The time in ms between each ReplayFrame. + /// + protected readonly double FrameDelay; + + #endregion + + #region Construction / Initialisation + + protected Replay Replay; + protected List Frames => Replay.Frames; + + protected OsuAutoGeneratorBase(Beatmap beatmap) + : base(beatmap) + { + Replay = new Replay + { + User = new User + { + Username = @"Autoplay", + } + }; + + // We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps. + FrameDelay = ApplyModsToRate(1000.0 / 60.0); + } + + #endregion + + #region Utilities + protected double ApplyModsToTime(double v) => v; + protected double ApplyModsToRate(double v) => v; + + private class ReplayFrameComparer : IComparer + { + public int Compare(ReplayFrame f1, ReplayFrame f2) + { + if (f1 == null) throw new ArgumentNullException(nameof(f1)); + if (f2 == null) throw new ArgumentNullException(nameof(f2)); + + return f1.Time.CompareTo(f2.Time); + } + } + + private static readonly IComparer replay_frame_comparer = new ReplayFrameComparer(); + + protected int FindInsertionIndex(ReplayFrame frame) + { + int index = Frames.BinarySearch(frame, replay_frame_comparer); + + if (index < 0) + { + index = ~index; + } + else + { + // Go to the first index which is actually bigger + while (index < Frames.Count && frame.Time == Frames[index].Time) + { + ++index; + } + } + + return index; + } + + protected void AddFrameToReplay(ReplayFrame frame) => Frames.Insert(FindInsertionIndex(frame), frame); + + protected static Vector2 CirclePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius)); + + #endregion + } +} diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs index bcdfe07417..6f2512cc33 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs @@ -1,36 +1,36 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Replays.Legacy; -using osu.Game.Rulesets.Replays.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Replays -{ - public class OsuReplayFrame : ReplayFrame, IConvertibleReplayFrame - { - public Vector2 Position; - public List Actions = new List(); - - public OsuReplayFrame() - { - } - - public OsuReplayFrame(double time, Vector2 position, params OsuAction[] actions) - : base(time) - { - Position = position; - Actions.AddRange(actions); - } - - public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) - { - Position = legacyFrame.Position; - if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); - if (legacyFrame.MouseRight) Actions.Add(OsuAction.RightButton); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Replays +{ + public class OsuReplayFrame : ReplayFrame, IConvertibleReplayFrame + { + public Vector2 Position; + public List Actions = new List(); + + public OsuReplayFrame() + { + } + + public OsuReplayFrame(double time, Vector2 position, params OsuAction[] actions) + : base(time) + { + Position = position; + Actions.AddRange(actions); + } + + public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) + { + Position = legacyFrame.Position; + if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); + if (legacyFrame.MouseRight) Actions.Add(OsuAction.RightButton); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs index 69154a1d0c..f9e5bfa89b 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs @@ -1,45 +1,45 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Input; -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Replays; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Replays -{ - public class OsuReplayInputHandler : FramedReplayInputHandler - { - public OsuReplayInputHandler(Replay replay) - : base(replay) - { - } - - protected override bool IsImportant(OsuReplayFrame frame) => frame.Actions.Any(); - - protected Vector2? Position - { - get - { - if (!HasFrames) - return null; - - return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); - } - } - - public override List GetPendingStates() - { - return new List - { - new ReplayState - { - Mouse = new ReplayMouseState(GamefieldToScreenSpace(Position ?? Vector2.Zero)), - PressedActions = CurrentFrame.Actions - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Replays; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Replays +{ + public class OsuReplayInputHandler : FramedReplayInputHandler + { + public OsuReplayInputHandler(Replay replay) + : base(replay) + { + } + + protected override bool IsImportant(OsuReplayFrame frame) => frame.Actions.Any(); + + protected Vector2? Position + { + get + { + if (!HasFrames) + return null; + + return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); + } + } + + public override List GetPendingStates() + { + return new List + { + new ReplayState + { + Mouse = new ReplayMouseState(GamefieldToScreenSpace(Position ?? Vector2.Zero)), + PressedActions = CurrentFrame.Actions + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs index 25d88a38f3..8f0feca207 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs @@ -1,199 +1,199 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Beatmaps; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Scoring -{ - public class OsuPerformanceCalculator : PerformanceCalculator - { - private readonly int countHitCircles; - private readonly int beatmapMaxCombo; - - private Mod[] mods; - private double realApproachRate; - private double accuracy; - private int scoreMaxCombo; - private int count300; - private int count100; - private int count50; - private int countMiss; - - public OsuPerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) - : base(ruleset, beatmap, score) - { - countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle); - - beatmapMaxCombo = Beatmap.HitObjects.Count; - beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count) + 1; - } - - public override double Calculate(Dictionary categoryRatings = null) - { - mods = Score.Mods; - accuracy = Score.Accuracy; - scoreMaxCombo = Score.MaxCombo; - count300 = Convert.ToInt32(Score.Statistics[HitResult.Great]); - count100 = Convert.ToInt32(Score.Statistics[HitResult.Good]); - count50 = Convert.ToInt32(Score.Statistics[HitResult.Meh]); - countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]); - - // Don't count scores made with supposedly unranked mods - if (mods.Any(m => !m.Ranked)) - return 0; - - // Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done - // locally for now as doing so would modify animations and other things unexpectedly - // DO NOT MODIFY THIS - double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; - if (mods.Any(m => m is OsuModHardRock)) - ar = Math.Min(10, ar * 1.4); - if (mods.Any(m => m is OsuModEasy)) - ar = Math.Max(0, ar / 2); - double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450); - realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5; - - // Custom multipliers for NoFail and SpunOut. - double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things - - if (mods.Any(m => m is OsuModNoFail)) - multiplier *= 0.90f; - - if (mods.Any(m => m is OsuModSpunOut)) - multiplier *= 0.95f; - - double aimValue = computeAimValue(); - double speedValue = computeSpeedValue(); - double accuracyValue = computeAccuracyValue(); - double totalValue = - Math.Pow( - Math.Pow(aimValue, 1.1f) + - Math.Pow(speedValue, 1.1f) + - Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f - ) * multiplier; - - if (categoryRatings != null) - { - categoryRatings.Add("Aim", aimValue); - categoryRatings.Add("Speed", speedValue); - categoryRatings.Add("Accuracy", accuracyValue); - } - - return totalValue; - } - - private double computeAimValue() - { - double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Aim"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f; - - // Longer maps are worth more - double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + - (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f); - - aimValue *= lengthBonus; - - // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available - aimValue *= Math.Pow(0.97f, countMiss); - - // Combo scaling - if (beatmapMaxCombo > 0) - aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); - - double approachRateFactor = 1.0f; - if (realApproachRate > 10.33f) - approachRateFactor += 0.45f * (realApproachRate - 10.33f); - else if (realApproachRate < 8.0f) - { - // HD is worth more with lower ar! - if (mods.Any(h => h is OsuModHidden)) - approachRateFactor += 0.02f * (8.0f - realApproachRate); - else - approachRateFactor += 0.01f * (8.0f - realApproachRate); - } - - aimValue *= approachRateFactor; - - if (mods.Any(h => h is OsuModHidden)) - aimValue *= 1.18f; - - if (mods.Any(h => h is OsuModFlashlight)) - { - // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps. - aimValue *= 1.45f * lengthBonus; - } - - // Scale the aim value with accuracy _slightly_ - aimValue *= 0.5f + accuracy / 2.0f; - // It is important to also consider accuracy difficulty when doing that - aimValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500; - - return aimValue; - } - - private double computeSpeedValue() - { - double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Speed"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f; - - // Longer maps are worth more - speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + - (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f); - - // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available - speedValue *= Math.Pow(0.97f, countMiss); - - // Combo scaling - if (beatmapMaxCombo > 0) - speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); - - // Scale the speed value with accuracy _slightly_ - speedValue *= 0.5f + accuracy / 2.0f; - // It is important to also consider accuracy difficulty when doing that - speedValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500; - - return speedValue; - } - - private double computeAccuracyValue() - { - // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window - double betterAccuracyPercentage; - int amountHitObjectsWithAccuracy = countHitCircles; - - if (amountHitObjectsWithAccuracy > 0) - betterAccuracyPercentage = ((count300 - (totalHits - amountHitObjectsWithAccuracy)) * 6 + count100 * 2 + count50) / (amountHitObjectsWithAccuracy * 6); - else - betterAccuracyPercentage = 0; - - // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points - if (betterAccuracyPercentage < 0) - betterAccuracyPercentage = 0; - - // Lots of arbitrary values from testing. - // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution - double accuracyValue = Math.Pow(1.52163f, Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f; - - // Bonus for many hitcircles - it's harder to keep good accuracy up for longer - accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f)); - - if (mods.Any(m => m is OsuModHidden)) - accuracyValue *= 1.02f; - if (mods.Any(m => m is OsuModFlashlight)) - accuracyValue *= 1.02f; - - return accuracyValue; - } - - private double totalHits => count300 + count100 + count50 + countMiss; - private double totalSuccessfulHits => count300 + count100 + count50; - - protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + public class OsuPerformanceCalculator : PerformanceCalculator + { + private readonly int countHitCircles; + private readonly int beatmapMaxCombo; + + private Mod[] mods; + private double realApproachRate; + private double accuracy; + private int scoreMaxCombo; + private int count300; + private int count100; + private int count50; + private int countMiss; + + public OsuPerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) + : base(ruleset, beatmap, score) + { + countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle); + + beatmapMaxCombo = Beatmap.HitObjects.Count; + beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count) + 1; + } + + public override double Calculate(Dictionary categoryRatings = null) + { + mods = Score.Mods; + accuracy = Score.Accuracy; + scoreMaxCombo = Score.MaxCombo; + count300 = Convert.ToInt32(Score.Statistics[HitResult.Great]); + count100 = Convert.ToInt32(Score.Statistics[HitResult.Good]); + count50 = Convert.ToInt32(Score.Statistics[HitResult.Meh]); + countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]); + + // Don't count scores made with supposedly unranked mods + if (mods.Any(m => !m.Ranked)) + return 0; + + // Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done + // locally for now as doing so would modify animations and other things unexpectedly + // DO NOT MODIFY THIS + double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; + if (mods.Any(m => m is OsuModHardRock)) + ar = Math.Min(10, ar * 1.4); + if (mods.Any(m => m is OsuModEasy)) + ar = Math.Max(0, ar / 2); + double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450); + realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5; + + // Custom multipliers for NoFail and SpunOut. + double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things + + if (mods.Any(m => m is OsuModNoFail)) + multiplier *= 0.90f; + + if (mods.Any(m => m is OsuModSpunOut)) + multiplier *= 0.95f; + + double aimValue = computeAimValue(); + double speedValue = computeSpeedValue(); + double accuracyValue = computeAccuracyValue(); + double totalValue = + Math.Pow( + Math.Pow(aimValue, 1.1f) + + Math.Pow(speedValue, 1.1f) + + Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f + ) * multiplier; + + if (categoryRatings != null) + { + categoryRatings.Add("Aim", aimValue); + categoryRatings.Add("Speed", speedValue); + categoryRatings.Add("Accuracy", accuracyValue); + } + + return totalValue; + } + + private double computeAimValue() + { + double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Aim"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f; + + // Longer maps are worth more + double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f); + + aimValue *= lengthBonus; + + // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available + aimValue *= Math.Pow(0.97f, countMiss); + + // Combo scaling + if (beatmapMaxCombo > 0) + aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); + + double approachRateFactor = 1.0f; + if (realApproachRate > 10.33f) + approachRateFactor += 0.45f * (realApproachRate - 10.33f); + else if (realApproachRate < 8.0f) + { + // HD is worth more with lower ar! + if (mods.Any(h => h is OsuModHidden)) + approachRateFactor += 0.02f * (8.0f - realApproachRate); + else + approachRateFactor += 0.01f * (8.0f - realApproachRate); + } + + aimValue *= approachRateFactor; + + if (mods.Any(h => h is OsuModHidden)) + aimValue *= 1.18f; + + if (mods.Any(h => h is OsuModFlashlight)) + { + // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps. + aimValue *= 1.45f * lengthBonus; + } + + // Scale the aim value with accuracy _slightly_ + aimValue *= 0.5f + accuracy / 2.0f; + // It is important to also consider accuracy difficulty when doing that + aimValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500; + + return aimValue; + } + + private double computeSpeedValue() + { + double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Speed"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f; + + // Longer maps are worth more + speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f); + + // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available + speedValue *= Math.Pow(0.97f, countMiss); + + // Combo scaling + if (beatmapMaxCombo > 0) + speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); + + // Scale the speed value with accuracy _slightly_ + speedValue *= 0.5f + accuracy / 2.0f; + // It is important to also consider accuracy difficulty when doing that + speedValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500; + + return speedValue; + } + + private double computeAccuracyValue() + { + // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window + double betterAccuracyPercentage; + int amountHitObjectsWithAccuracy = countHitCircles; + + if (amountHitObjectsWithAccuracy > 0) + betterAccuracyPercentage = ((count300 - (totalHits - amountHitObjectsWithAccuracy)) * 6 + count100 * 2 + count50) / (amountHitObjectsWithAccuracy * 6); + else + betterAccuracyPercentage = 0; + + // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points + if (betterAccuracyPercentage < 0) + betterAccuracyPercentage = 0; + + // Lots of arbitrary values from testing. + // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution + double accuracyValue = Math.Pow(1.52163f, Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f; + + // Bonus for many hitcircles - it's harder to keep good accuracy up for longer + accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f)); + + if (mods.Any(m => m is OsuModHidden)) + accuracyValue *= 1.02f; + if (mods.Any(m => m is OsuModFlashlight)) + accuracyValue *= 1.02f; + + return accuracyValue; + } + + private double totalHits => count300 + count100 + count50 + countMiss; + private double totalSuccessfulHits => count300 + count100 + count50; + + protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter(); + } +} diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index d41331e3bd..01b92255ae 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -1,109 +1,109 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Osu.Scoring -{ - internal class OsuScoreProcessor : ScoreProcessor - { - public OsuScoreProcessor(RulesetContainer rulesetContainer) - : base(rulesetContainer) - { - } - - private float hpDrainRate; - - private readonly Dictionary scoreResultCounts = new Dictionary(); - private readonly Dictionary comboResultCounts = new Dictionary(); - - protected override void SimulateAutoplay(Beatmap beatmap) - { - hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; - - foreach (var obj in beatmap.HitObjects) - { - if (obj is Slider slider) - { - // Head - AddJudgement(new OsuJudgement { Result = HitResult.Great }); - - // Ticks - foreach (var unused in slider.NestedHitObjects.OfType()) - AddJudgement(new OsuJudgement { Result = HitResult.Great }); - - //Repeats - foreach (var unused in slider.NestedHitObjects.OfType()) - AddJudgement(new OsuJudgement { Result = HitResult.Great }); - } - - AddJudgement(new OsuJudgement { Result = HitResult.Great }); - } - } - - protected override void Reset(bool storeResults) - { - base.Reset(storeResults); - - scoreResultCounts.Clear(); - comboResultCounts.Clear(); - } - - public override void PopulateScore(Score score) - { - base.PopulateScore(score); - - score.Statistics[HitResult.Great] = scoreResultCounts.GetOrDefault(HitResult.Great); - score.Statistics[HitResult.Good] = scoreResultCounts.GetOrDefault(HitResult.Good); - score.Statistics[HitResult.Meh] = scoreResultCounts.GetOrDefault(HitResult.Meh); - score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss); - } - - private const double harshness = 0.01; - - protected override void OnNewJudgement(Judgement judgement) - { - base.OnNewJudgement(judgement); - - var osuJudgement = (OsuJudgement)judgement; - - if (judgement.Result != HitResult.None) - { - scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1; - comboResultCounts[osuJudgement.Combo] = comboResultCounts.GetOrDefault(osuJudgement.Combo) + 1; - } - - switch (judgement.Result) - { - case HitResult.Great: - Health.Value += (10.2 - hpDrainRate) * harshness; - break; - - case HitResult.Good: - Health.Value += (8 - hpDrainRate) * harshness; - break; - - case HitResult.Meh: - Health.Value += (4 - hpDrainRate) * harshness; - break; - - /*case HitResult.SliderTick: - Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01; - break;*/ - - case HitResult.Miss: - Health.Value -= hpDrainRate * (harshness * 2); - break; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + internal class OsuScoreProcessor : ScoreProcessor + { + public OsuScoreProcessor(RulesetContainer rulesetContainer) + : base(rulesetContainer) + { + } + + private float hpDrainRate; + + private readonly Dictionary scoreResultCounts = new Dictionary(); + private readonly Dictionary comboResultCounts = new Dictionary(); + + protected override void SimulateAutoplay(Beatmap beatmap) + { + hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; + + foreach (var obj in beatmap.HitObjects) + { + if (obj is Slider slider) + { + // Head + AddJudgement(new OsuJudgement { Result = HitResult.Great }); + + // Ticks + foreach (var unused in slider.NestedHitObjects.OfType()) + AddJudgement(new OsuJudgement { Result = HitResult.Great }); + + //Repeats + foreach (var unused in slider.NestedHitObjects.OfType()) + AddJudgement(new OsuJudgement { Result = HitResult.Great }); + } + + AddJudgement(new OsuJudgement { Result = HitResult.Great }); + } + } + + protected override void Reset(bool storeResults) + { + base.Reset(storeResults); + + scoreResultCounts.Clear(); + comboResultCounts.Clear(); + } + + public override void PopulateScore(Score score) + { + base.PopulateScore(score); + + score.Statistics[HitResult.Great] = scoreResultCounts.GetOrDefault(HitResult.Great); + score.Statistics[HitResult.Good] = scoreResultCounts.GetOrDefault(HitResult.Good); + score.Statistics[HitResult.Meh] = scoreResultCounts.GetOrDefault(HitResult.Meh); + score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss); + } + + private const double harshness = 0.01; + + protected override void OnNewJudgement(Judgement judgement) + { + base.OnNewJudgement(judgement); + + var osuJudgement = (OsuJudgement)judgement; + + if (judgement.Result != HitResult.None) + { + scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1; + comboResultCounts[osuJudgement.Combo] = comboResultCounts.GetOrDefault(osuJudgement.Combo) + 1; + } + + switch (judgement.Result) + { + case HitResult.Great: + Health.Value += (10.2 - hpDrainRate) * harshness; + break; + + case HitResult.Good: + Health.Value += (8 - hpDrainRate) * harshness; + break; + + case HitResult.Meh: + Health.Value += (4 - hpDrainRate) * harshness; + break; + + /*case HitResult.SliderTick: + Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01; + break;*/ + + case HitResult.Miss: + Health.Value -= hpDrainRate * (harshness * 2); + break; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 3203df16e2..7bd80b5718 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -1,273 +1,273 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.OpenGL.Buffers; -using osu.Framework.Graphics.OpenGL.Vertices; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shaders; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input; -using osu.Framework.Timing; -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Graphics.ES30; - -namespace osu.Game.Rulesets.Osu.UI.Cursor -{ - internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition - { - private int currentIndex; - - private Shader shader; - private Texture texture; - - private Vector2 size => texture.Size * Scale; - - private double timeOffset; - - private float time; - - public override bool IsPresent => true; - - private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); - private const int max_sprites = 2048; - - private readonly TrailPart[] parts = new TrailPart[max_sprites]; - - private Vector2? lastPosition; - - private readonly InputResampler resampler = new InputResampler(); - - protected override DrawNode CreateDrawNode() => new TrailDrawNode(); - - protected override void ApplyDrawNode(DrawNode node) - { - base.ApplyDrawNode(node); - - TrailDrawNode tNode = (TrailDrawNode)node; - tNode.Shader = shader; - tNode.Texture = texture; - tNode.Size = size; - tNode.Time = time; - tNode.Shared = trailDrawNodeSharedData; - - for (int i = 0; i < parts.Length; ++i) - if (parts[i].InvalidationID > tNode.Parts[i].InvalidationID) - tNode.Parts[i] = parts[i]; - } - - public CursorTrail() - { - // as we are currently very dependent on having a running clock, let's make our own clock for the time being. - Clock = new FramedClock(); - - RelativeSizeAxes = Axes.Both; - - for (int i = 0; i < max_sprites; i++) - { - parts[i].InvalidationID = 0; - parts[i].WasUpdated = true; - } - } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - - [BackgroundDependencyLoader] - private void load(ShaderManager shaders, TextureStore textures) - { - shader = shaders?.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE); - texture = textures.Get(@"Cursor/cursortrail"); - Scale = new Vector2(1 / texture.ScaleAdjust); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - resetTime(); - } - - protected override void Update() - { - base.Update(); - - Invalidate(Invalidation.DrawNode, shallPropagate: false); - - const int fade_clock_reset_threshold = 1000000; - - time = (float)(Time.Current - timeOffset) / 300f; - if (time > fade_clock_reset_threshold) - resetTime(); - } - - private void resetTime() - { - for (int i = 0; i < parts.Length; ++i) - { - parts[i].Time -= time; - ++parts[i].InvalidationID; - } - - time = 0; - timeOffset = Time.Current; - } - - protected override bool OnMouseMove(InputState state) - { - Vector2 pos = state.Mouse.NativeState.Position; - - if (lastPosition == null) - { - lastPosition = pos; - resampler.AddPosition(lastPosition.Value); - return base.OnMouseMove(state); - } - - foreach (Vector2 pos2 in resampler.AddPosition(pos)) - { - Trace.Assert(lastPosition.HasValue); - - // ReSharper disable once PossibleInvalidOperationException - Vector2 pos1 = lastPosition.Value; - Vector2 diff = pos2 - pos1; - float distance = diff.Length; - Vector2 direction = diff / distance; - - float interval = size.X / 2 * 0.9f; - - for (float d = interval; d < distance; d += interval) - { - lastPosition = pos1 + direction * d; - addPosition(lastPosition.Value); - } - } - - return base.OnMouseMove(state); - } - - private void addPosition(Vector2 pos) - { - parts[currentIndex].Position = pos; - parts[currentIndex].Time = time; - ++parts[currentIndex].InvalidationID; - - currentIndex = (currentIndex + 1) % max_sprites; - } - - private struct TrailPart - { - public Vector2 Position; - public float Time; - public long InvalidationID; - public bool WasUpdated; - } - - private class TrailDrawNodeSharedData - { - public VertexBuffer VertexBuffer; - } - - private class TrailDrawNode : DrawNode - { - public Shader Shader; - public Texture Texture; - - public float Time; - public TrailDrawNodeSharedData Shared; - - public readonly TrailPart[] Parts = new TrailPart[max_sprites]; - public Vector2 Size; - - public TrailDrawNode() - { - for (int i = 0; i < max_sprites; i++) - { - Parts[i].InvalidationID = 0; - Parts[i].WasUpdated = false; - } - } - - public override void Draw(Action vertexAction) - { - if (Shared.VertexBuffer == null) - Shared.VertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw); - - Shader.GetUniform("g_FadeClock").Value = Time; - - int updateStart = -1, updateEnd = 0; - for (int i = 0; i < Parts.Length; ++i) - { - if (Parts[i].WasUpdated) - { - if (updateStart == -1) - updateStart = i; - updateEnd = i + 1; - - int start = i * 4; - int end = start; - - Vector2 pos = Parts[i].Position; - float time = Parts[i].Time; - - Texture.DrawQuad( - new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y), - DrawInfo.Colour, - null, - v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex - { - Position = v.Position, - TexturePosition = v.TexturePosition, - Time = time + 1, - Colour = v.Colour, - }); - - Parts[i].WasUpdated = false; - } - else if (updateStart != -1) - { - Shared.VertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4); - updateStart = -1; - } - } - - // Update all remaining vertices that have been changed. - if (updateStart != -1) - Shared.VertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4); - - base.Draw(vertexAction); - - Shader.Bind(); - - Texture.TextureGL.Bind(); - Shared.VertexBuffer.Draw(); - - Shader.Unbind(); - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct TexturedTrailVertex : IEquatable, IVertex - { - [VertexMember(2, VertexAttribPointerType.Float)] - public Vector2 Position; - [VertexMember(4, VertexAttribPointerType.Float)] - public Color4 Colour; - [VertexMember(2, VertexAttribPointerType.Float)] - public Vector2 TexturePosition; - [VertexMember(1, VertexAttribPointerType.Float)] - public float Time; - - public bool Equals(TexturedTrailVertex other) - { - return Position.Equals(other.Position) - && TexturePosition.Equals(other.TexturePosition) - && Colour.Equals(other.Colour) - && Time.Equals(other.Time); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Buffers; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input; +using osu.Framework.Timing; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Graphics.ES30; + +namespace osu.Game.Rulesets.Osu.UI.Cursor +{ + internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition + { + private int currentIndex; + + private Shader shader; + private Texture texture; + + private Vector2 size => texture.Size * Scale; + + private double timeOffset; + + private float time; + + public override bool IsPresent => true; + + private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); + private const int max_sprites = 2048; + + private readonly TrailPart[] parts = new TrailPart[max_sprites]; + + private Vector2? lastPosition; + + private readonly InputResampler resampler = new InputResampler(); + + protected override DrawNode CreateDrawNode() => new TrailDrawNode(); + + protected override void ApplyDrawNode(DrawNode node) + { + base.ApplyDrawNode(node); + + TrailDrawNode tNode = (TrailDrawNode)node; + tNode.Shader = shader; + tNode.Texture = texture; + tNode.Size = size; + tNode.Time = time; + tNode.Shared = trailDrawNodeSharedData; + + for (int i = 0; i < parts.Length; ++i) + if (parts[i].InvalidationID > tNode.Parts[i].InvalidationID) + tNode.Parts[i] = parts[i]; + } + + public CursorTrail() + { + // as we are currently very dependent on having a running clock, let's make our own clock for the time being. + Clock = new FramedClock(); + + RelativeSizeAxes = Axes.Both; + + for (int i = 0; i < max_sprites; i++) + { + parts[i].InvalidationID = 0; + parts[i].WasUpdated = true; + } + } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders, TextureStore textures) + { + shader = shaders?.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE); + texture = textures.Get(@"Cursor/cursortrail"); + Scale = new Vector2(1 / texture.ScaleAdjust); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + resetTime(); + } + + protected override void Update() + { + base.Update(); + + Invalidate(Invalidation.DrawNode, shallPropagate: false); + + const int fade_clock_reset_threshold = 1000000; + + time = (float)(Time.Current - timeOffset) / 300f; + if (time > fade_clock_reset_threshold) + resetTime(); + } + + private void resetTime() + { + for (int i = 0; i < parts.Length; ++i) + { + parts[i].Time -= time; + ++parts[i].InvalidationID; + } + + time = 0; + timeOffset = Time.Current; + } + + protected override bool OnMouseMove(InputState state) + { + Vector2 pos = state.Mouse.NativeState.Position; + + if (lastPosition == null) + { + lastPosition = pos; + resampler.AddPosition(lastPosition.Value); + return base.OnMouseMove(state); + } + + foreach (Vector2 pos2 in resampler.AddPosition(pos)) + { + Trace.Assert(lastPosition.HasValue); + + // ReSharper disable once PossibleInvalidOperationException + Vector2 pos1 = lastPosition.Value; + Vector2 diff = pos2 - pos1; + float distance = diff.Length; + Vector2 direction = diff / distance; + + float interval = size.X / 2 * 0.9f; + + for (float d = interval; d < distance; d += interval) + { + lastPosition = pos1 + direction * d; + addPosition(lastPosition.Value); + } + } + + return base.OnMouseMove(state); + } + + private void addPosition(Vector2 pos) + { + parts[currentIndex].Position = pos; + parts[currentIndex].Time = time; + ++parts[currentIndex].InvalidationID; + + currentIndex = (currentIndex + 1) % max_sprites; + } + + private struct TrailPart + { + public Vector2 Position; + public float Time; + public long InvalidationID; + public bool WasUpdated; + } + + private class TrailDrawNodeSharedData + { + public VertexBuffer VertexBuffer; + } + + private class TrailDrawNode : DrawNode + { + public Shader Shader; + public Texture Texture; + + public float Time; + public TrailDrawNodeSharedData Shared; + + public readonly TrailPart[] Parts = new TrailPart[max_sprites]; + public Vector2 Size; + + public TrailDrawNode() + { + for (int i = 0; i < max_sprites; i++) + { + Parts[i].InvalidationID = 0; + Parts[i].WasUpdated = false; + } + } + + public override void Draw(Action vertexAction) + { + if (Shared.VertexBuffer == null) + Shared.VertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw); + + Shader.GetUniform("g_FadeClock").Value = Time; + + int updateStart = -1, updateEnd = 0; + for (int i = 0; i < Parts.Length; ++i) + { + if (Parts[i].WasUpdated) + { + if (updateStart == -1) + updateStart = i; + updateEnd = i + 1; + + int start = i * 4; + int end = start; + + Vector2 pos = Parts[i].Position; + float time = Parts[i].Time; + + Texture.DrawQuad( + new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y), + DrawInfo.Colour, + null, + v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex + { + Position = v.Position, + TexturePosition = v.TexturePosition, + Time = time + 1, + Colour = v.Colour, + }); + + Parts[i].WasUpdated = false; + } + else if (updateStart != -1) + { + Shared.VertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4); + updateStart = -1; + } + } + + // Update all remaining vertices that have been changed. + if (updateStart != -1) + Shared.VertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4); + + base.Draw(vertexAction); + + Shader.Bind(); + + Texture.TextureGL.Bind(); + Shared.VertexBuffer.Draw(); + + Shader.Unbind(); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct TexturedTrailVertex : IEquatable, IVertex + { + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 Position; + [VertexMember(4, VertexAttribPointerType.Float)] + public Color4 Colour; + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 TexturePosition; + [VertexMember(1, VertexAttribPointerType.Float)] + public float Time; + + public bool Equals(TexturedTrailVertex other) + { + return Position.Equals(other.Position) + && TexturePosition.Equals(other.TexturePosition) + && Colour.Equals(other.Colour) + && Time.Equals(other.Time); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index ac81d93309..e7f17dd86b 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -1,189 +1,189 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Skinning; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.UI.Cursor -{ - public class GameplayCursor : CursorContainer, IKeyBindingHandler - { - protected override Drawable CreateCursor() => new OsuCursor(); - - protected override Container Content => fadeContainer; - - private readonly Container fadeContainer; - - public GameplayCursor() - { - InternalChild = fadeContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new CursorTrail { Depth = 1 } - } - }; - } - - private int downCount; - - public bool OnPressed(OsuAction action) - { - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - downCount++; - ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); - break; - } - - return false; - } - - public bool OnReleased(OsuAction action) - { - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - if (--downCount == 0) - ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); - break; - } - - return false; - } - - public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. - - protected override void PopIn() - { - fadeContainer.FadeTo(1, 300, Easing.OutQuint); - ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); - } - - protected override void PopOut() - { - fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); - ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint); - } - - public class OsuCursor : Container - { - private Drawable cursorContainer; - - private Bindable cursorScale; - private Bindable autoCursorScale; - private Bindable beatmap; - - public OsuCursor() - { - Origin = Anchor.Centre; - Size = new Vector2(42); - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config, OsuGameBase game) - { - Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = Size.X / 6, - BorderColour = Color4.White, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Pink.Opacity(0.5f), - Radius = 5, - }, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = Size.X / 3, - BorderColour = Color4.White.Opacity(0.5f), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - }, - }, - new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.1f), - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - }, - }, - }, - } - }, restrictSize: false) - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }; - - beatmap = game.Beatmap.GetBoundCopy(); - beatmap.ValueChanged += v => calculateScale(); - - cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize); - cursorScale.ValueChanged += v => calculateScale(); - - autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize); - autoCursorScale.ValueChanged += v => calculateScale(); - - calculateScale(); - } - - private void calculateScale() - { - float scale = (float)cursorScale.Value; - - if (autoCursorScale && beatmap.Value != null) - { - // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier. - scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); - } - - cursorContainer.Scale = new Vector2(scale); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Skinning; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.UI.Cursor +{ + public class GameplayCursor : CursorContainer, IKeyBindingHandler + { + protected override Drawable CreateCursor() => new OsuCursor(); + + protected override Container Content => fadeContainer; + + private readonly Container fadeContainer; + + public GameplayCursor() + { + InternalChild = fadeContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new CursorTrail { Depth = 1 } + } + }; + } + + private int downCount; + + public bool OnPressed(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + downCount++; + ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); + break; + } + + return false; + } + + public bool OnReleased(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + if (--downCount == 0) + ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); + break; + } + + return false; + } + + public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. + + protected override void PopIn() + { + fadeContainer.FadeTo(1, 300, Easing.OutQuint); + ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); + } + + protected override void PopOut() + { + fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); + ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint); + } + + public class OsuCursor : Container + { + private Drawable cursorContainer; + + private Bindable cursorScale; + private Bindable autoCursorScale; + private Bindable beatmap; + + public OsuCursor() + { + Origin = Anchor.Centre; + Size = new Vector2(42); + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, OsuGameBase game) + { + Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = Size.X / 6, + BorderColour = Color4.White, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Pink.Opacity(0.5f), + Radius = 5, + }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = Size.X / 3, + BorderColour = Color4.White.Opacity(0.5f), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + }, + }, + new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.1f), + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + }, + }, + } + }, restrictSize: false) + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + }; + + beatmap = game.Beatmap.GetBoundCopy(); + beatmap.ValueChanged += v => calculateScale(); + + cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize); + cursorScale.ValueChanged += v => calculateScale(); + + autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize); + autoCursorScale.ValueChanged += v => calculateScale(); + + calculateScale(); + } + + private void calculateScale() + { + float scale = (float)cursorScale.Value; + + if (autoCursorScale && beatmap.Value != null) + { + // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier. + scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); + } + + cursorContainer.Scale = new Vector2(scale); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 9010f66acb..8330aa8e9d 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -1,89 +1,89 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; -using osu.Game.Rulesets.UI; -using System.Linq; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu.Judgements; - -namespace osu.Game.Rulesets.Osu.UI -{ - public class OsuPlayfield : Playfield - { - private readonly Container approachCircles; - private readonly JudgementContainer judgementLayer; - private readonly ConnectionRenderer connectionLayer; - - // Todo: This should not be a thing, but is currently required for the editor - // https://github.com/ppy/osu-framework/issues/1283 - protected virtual bool ProxyApproachCircles => true; - protected virtual bool DisplayJudgements => true; - - public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); - - public OsuPlayfield() - : base(BASE_SIZE.X) - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - AddRange(new Drawable[] - { - connectionLayer = new FollowPointRenderer - { - RelativeSizeAxes = Axes.Both, - Depth = 2, - }, - judgementLayer = new JudgementContainer - { - RelativeSizeAxes = Axes.Both, - Depth = 1, - }, - approachCircles = new Container - { - RelativeSizeAxes = Axes.Both, - Depth = -1, - }, - }); - } - - public override void Add(DrawableHitObject h) - { - h.OnJudgement += onJudgement; - - var c = h as IDrawableHitObjectWithProxiedApproach; - if (c != null && ProxyApproachCircles) - approachCircles.Add(c.ProxiedLayer.CreateProxy()); - - base.Add(h); - } - - public override void PostProcess() - { - connectionLayer.HitObjects = HitObjects.Objects - .Select(d => d.HitObject) - .OrderBy(h => h.StartTime).OfType(); - } - - private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) - { - if (!judgedObject.DisplayJudgement || !DisplayJudgements) - return; - - DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject) - { - Origin = Anchor.Centre, - Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition + ((OsuJudgement)judgement).PositionOffset - }; - - judgementLayer.Add(explosion); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; +using osu.Game.Rulesets.UI; +using System.Linq; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu.Judgements; + +namespace osu.Game.Rulesets.Osu.UI +{ + public class OsuPlayfield : Playfield + { + private readonly Container approachCircles; + private readonly JudgementContainer judgementLayer; + private readonly ConnectionRenderer connectionLayer; + + // Todo: This should not be a thing, but is currently required for the editor + // https://github.com/ppy/osu-framework/issues/1283 + protected virtual bool ProxyApproachCircles => true; + protected virtual bool DisplayJudgements => true; + + public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); + + public OsuPlayfield() + : base(BASE_SIZE.X) + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + AddRange(new Drawable[] + { + connectionLayer = new FollowPointRenderer + { + RelativeSizeAxes = Axes.Both, + Depth = 2, + }, + judgementLayer = new JudgementContainer + { + RelativeSizeAxes = Axes.Both, + Depth = 1, + }, + approachCircles = new Container + { + RelativeSizeAxes = Axes.Both, + Depth = -1, + }, + }); + } + + public override void Add(DrawableHitObject h) + { + h.OnJudgement += onJudgement; + + var c = h as IDrawableHitObjectWithProxiedApproach; + if (c != null && ProxyApproachCircles) + approachCircles.Add(c.ProxiedLayer.CreateProxy()); + + base.Add(h); + } + + public override void PostProcess() + { + connectionLayer.HitObjects = HitObjects.Objects + .Select(d => d.HitObject) + .OrderBy(h => h.StartTime).OfType(); + } + + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) + { + if (!judgedObject.DisplayJudgement || !DisplayJudgements) + return; + + DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject) + { + Origin = Anchor.Centre, + Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition + ((OsuJudgement)judgement).PositionOffset + }; + + judgementLayer.Add(explosion); + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index b825ba73b7..22c7b719cd 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -1,62 +1,62 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Cursor; -using osu.Framework.Input; -using OpenTK; -using osu.Game.Beatmaps; -using osu.Game.Input.Handlers; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Beatmaps; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Replays; -using osu.Game.Rulesets.Osu.Scoring; -using osu.Game.Rulesets.Osu.UI.Cursor; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.Replays; - -namespace osu.Game.Rulesets.Osu.UI -{ - public class OsuRulesetContainer : RulesetContainer - { - public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) - { - } - - public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this); - - protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter(); - - protected override BeatmapProcessor CreateBeatmapProcessor() => new OsuBeatmapProcessor(); - - protected override Playfield CreatePlayfield() => new OsuPlayfield(); - - public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); - - protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) - { - if (h is HitCircle circle) - return new DrawableHitCircle(circle); - - if (h is Slider slider) - return new DrawableSlider(slider); - - if (h is Spinner spinner) - return new DrawableSpinner(spinner); - return null; - } - - protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay); - - protected override Vector2 GetAspectAdjustedSize() - { - var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y); - return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y); - } - - protected override CursorContainer CreateCursor() => new GameplayCursor(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Cursor; +using osu.Framework.Input; +using OpenTK; +using osu.Game.Beatmaps; +using osu.Game.Input.Handlers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.Replays; + +namespace osu.Game.Rulesets.Osu.UI +{ + public class OsuRulesetContainer : RulesetContainer + { + public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + } + + public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this); + + protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter(); + + protected override BeatmapProcessor CreateBeatmapProcessor() => new OsuBeatmapProcessor(); + + protected override Playfield CreatePlayfield() => new OsuPlayfield(); + + public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); + + protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) + { + if (h is HitCircle circle) + return new DrawableHitCircle(circle); + + if (h is Slider slider) + return new DrawableSlider(slider); + + if (h is Spinner spinner) + return new DrawableSpinner(spinner); + return null; + } + + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay); + + protected override Vector2 GetAspectAdjustedSize() + { + var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y); + return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y); + } + + protected override CursorContainer CreateCursor() => new GameplayCursor(); + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettings.cs b/osu.Game.Rulesets.Osu/UI/OsuSettings.cs index d8f0aa9ebf..31ad6701fd 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettings.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettings.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Configuration; -using osu.Game.Overlays.Settings; - -namespace osu.Game.Rulesets.Osu.UI -{ - public class OsuSettings : SettingsSubsection - { - protected override string Header => "osu!"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new Drawable[] - { - new SettingsCheckbox - { - LabelText = "Snaking in sliders", - Bindable = config.GetBindable(OsuSetting.SnakingInSliders) - }, - new SettingsCheckbox - { - LabelText = "Snaking out sliders", - Bindable = config.GetBindable(OsuSetting.SnakingOutSliders) - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Rulesets.Osu.UI +{ + public class OsuSettings : SettingsSubsection + { + protected override string Header => "osu!"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = "Snaking in sliders", + Bindable = config.GetBindable(OsuSetting.SnakingInSliders) + }, + new SettingsCheckbox + { + LabelText = "Snaking out sliders", + Bindable = config.GetBindable(OsuSetting.SnakingOutSliders) + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 385e041ace..aa61f2d60b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -1,73 +1,73 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Taiko.Beatmaps; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Tests.Beatmaps; - -namespace osu.Game.Rulesets.Taiko.Tests -{ - public class TaikoBeatmapConversionTest : BeatmapConversionTest - { - protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - - private bool isForCurrentRuleset; - - [NonParallelizable] - [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")] - [TestCase("slider-generating-drumroll", false)] - public void Test(string name, bool isForCurrentRuleset) - { - this.isForCurrentRuleset = isForCurrentRuleset; - base.Test(name); - } - - protected override IEnumerable CreateConvertValue(HitObject hitObject) - { - yield return new ConvertValue - { - StartTime = hitObject.StartTime, - EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, - IsRim = hitObject is RimHit, - IsCentre = hitObject is CentreHit, - IsDrumRoll = hitObject is DrumRoll, - IsSwell = hitObject is Swell, - IsStrong = ((TaikoHitObject)hitObject).IsStrong - }; - } - - protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new TaikoBeatmapConverter(isForCurrentRuleset); - } - - public struct ConvertValue : IEquatable - { - /// - /// A sane value to account for osu!stable using ints everwhere. - /// - private const float conversion_lenience = 2; - - public double StartTime; - public double EndTime; - public bool IsRim; - public bool IsCentre; - public bool IsDrumRoll; - public bool IsSwell; - public bool IsStrong; - - public bool Equals(ConvertValue other) - => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) - && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) - && IsRim == other.IsRim - && IsCentre == other.IsCentre - && IsDrumRoll == other.IsDrumRoll - && IsSwell == other.IsSwell - && IsStrong == other.IsStrong; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TaikoBeatmapConversionTest : BeatmapConversionTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; + + private bool isForCurrentRuleset; + + [NonParallelizable] + [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")] + [TestCase("slider-generating-drumroll", false)] + public void Test(string name, bool isForCurrentRuleset) + { + this.isForCurrentRuleset = isForCurrentRuleset; + base.Test(name); + } + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + yield return new ConvertValue + { + StartTime = hitObject.StartTime, + EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, + IsRim = hitObject is RimHit, + IsCentre = hitObject is CentreHit, + IsDrumRoll = hitObject is DrumRoll, + IsSwell = hitObject is Swell, + IsStrong = ((TaikoHitObject)hitObject).IsStrong + }; + } + + protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new TaikoBeatmapConverter(isForCurrentRuleset); + } + + public struct ConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everwhere. + /// + private const float conversion_lenience = 2; + + public double StartTime; + public double EndTime; + public bool IsRim; + public bool IsCentre; + public bool IsDrumRoll; + public bool IsSwell; + public bool IsStrong; + + public bool Equals(ConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) + && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) + && IsRim == other.IsRim + && IsCentre == other.IsCentre + && IsDrumRoll == other.IsDrumRoll + && IsSwell == other.IsSwell + && IsStrong == other.IsStrong; + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseInputDrum.cs index 80721271d6..322e9b6bd4 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestCaseInputDrum.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseInputDrum.cs @@ -1,44 +1,44 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Taiko.Audio; -using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Taiko.Tests -{ - [TestFixture] - public class TestCaseInputDrum : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(InputDrum), - typeof(DrumSampleMapping), - typeof(SampleInfo), - typeof(SampleControlPoint) - }; - - public TestCaseInputDrum() - { - Add(new TaikoInputManager(new RulesetInfo { ID = 1 }) - { - RelativeSizeAxes = Axes.Both, - Child = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(200), - Child = new InputDrum(new ControlPointInfo()) - } - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Audio; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TestCaseInputDrum : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(InputDrum), + typeof(DrumSampleMapping), + typeof(SampleInfo), + typeof(SampleControlPoint) + }; + + public TestCaseInputDrum() + { + Add(new TaikoInputManager(new RulesetInfo { ID = 1 }) + { + RelativeSizeAxes = Axes.Both, + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200), + Child = new InputDrum(new ControlPointInfo()) + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Taiko.Tests/TestCasePerformancePoints.cs index f6b0ceb7bd..2fd9161d13 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestCasePerformancePoints.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; - -namespace osu.Game.Rulesets.Taiko.Tests -{ - [TestFixture] - public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints - { - public TestCasePerformancePoints() - : base(new TaikoRuleset()) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints + { + public TestCasePerformancePoints() + : base(new TaikoRuleset()) + { + } + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs index 3fd16ed1b5..aa7318b863 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs @@ -1,240 +1,240 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; -using osu.Framework.Timing; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Judgements; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Taiko.Objects.Drawables; -using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Tests.Beatmaps; -using osu.Game.Tests.Visual; -using OpenTK; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Taiko.Tests -{ - [TestFixture] - public class TestCaseTaikoPlayfield : OsuTestCase - { - private const double default_duration = 1000; - private const float scroll_time = 1000; - - protected override double TimePerAction => default_duration * 2; - - private readonly Random rng = new Random(1337); - private TaikoRulesetContainer rulesetContainer; - private Container playfieldContainer; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - AddStep("Hit!", () => addHitJudgement(false)); - AddStep("Kiai hit", () => addHitJudgement(true)); - AddStep("Miss :(", addMissJudgement); - AddStep("DrumRoll", () => addDrumRoll(false)); - AddStep("Strong DrumRoll", () => addDrumRoll(true)); - AddStep("Swell", () => addSwell()); - AddStep("Centre", () => addCentreHit(false)); - AddStep("Strong Centre", () => addCentreHit(true)); - AddStep("Rim", () => addRimHit(false)); - AddStep("Strong Rim", () => addRimHit(true)); - AddStep("Add bar line", () => addBarLine(false)); - AddStep("Add major bar line", () => addBarLine(true)); - AddStep("Height test 1", () => changePlayfieldSize(1)); - AddStep("Height test 2", () => changePlayfieldSize(2)); - AddStep("Height test 3", () => changePlayfieldSize(3)); - AddStep("Height test 4", () => changePlayfieldSize(4)); - AddStep("Height test 5", () => changePlayfieldSize(5)); - AddStep("Reset height", () => changePlayfieldSize(6)); - - var controlPointInfo = new ControlPointInfo(); - controlPointInfo.TimingPoints.Add(new TimingControlPoint()); - - WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap - { - HitObjects = new List { new CentreHit() }, - BeatmapInfo = new BeatmapInfo - { - BaseDifficulty = new BeatmapDifficulty(), - Metadata = new BeatmapMetadata - { - Artist = @"Unknown", - Title = @"Sample Beatmap", - AuthorString = @"peppy", - }, - }, - ControlPointInfo = controlPointInfo - }); - - var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; - - Add(playfieldContainer = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Height = 768, - Clock = new FramedClock(rateAdjustClock), - Children = new[] { rulesetContainer = new TaikoRulesetContainer(rulesets.GetRuleset(1).CreateInstance(), beatmap, true) } - }); - } - - private void changePlayfieldSize(int step) - { - double delay = 0; - - // Add new hits - switch (step) - { - case 1: - addCentreHit(false); - break; - case 2: - addCentreHit(true); - break; - case 3: - addDrumRoll(false); - break; - case 4: - addDrumRoll(true); - break; - case 5: - addSwell(); - delay = scroll_time - 100; - break; - } - - // Tween playfield height - switch (step) - { - default: - playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500); - break; - case 6: - playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500); - break; - } - } - - private void addHitJudgement(bool kiai) - { - HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great; - - var cpi = new ControlPointInfo(); - cpi.EffectPoints.Add(new EffectControlPoint - { - KiaiMode = kiai - }); - - Hit hit = new Hit(); - hit.ApplyDefaults(cpi, new BeatmapDifficulty()); - - var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; - - ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult }); - - if (RNG.Next(10) == 0) - { - ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult }); - ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoStrongHitJudgement()); - } - } - - private void addMissJudgement() - { - ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss }); - } - - private void addBarLine(bool major, double delay = scroll_time) - { - BarLine bl = new BarLine { StartTime = rulesetContainer.Playfield.Time.Current + delay }; - - rulesetContainer.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl)); - } - - private void addSwell(double duration = default_duration) - { - var swell = new Swell - { - StartTime = rulesetContainer.Playfield.Time.Current + scroll_time, - Duration = duration, - }; - - swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - rulesetContainer.Playfield.Add(new DrawableSwell(swell)); - } - - private void addDrumRoll(bool strong, double duration = default_duration) - { - addBarLine(true); - addBarLine(true, scroll_time + duration); - - var d = new DrumRoll - { - StartTime = rulesetContainer.Playfield.Time.Current + scroll_time, - IsStrong = strong, - Duration = duration, - }; - - d.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - rulesetContainer.Playfield.Add(new DrawableDrumRoll(d)); - } - - private void addCentreHit(bool strong) - { - Hit h = new Hit - { - StartTime = rulesetContainer.Playfield.Time.Current + scroll_time, - IsStrong = strong - }; - - h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - if (strong) - rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h)); - else - rulesetContainer.Playfield.Add(new DrawableCentreHit(h)); - } - - private void addRimHit(bool strong) - { - Hit h = new Hit - { - StartTime = rulesetContainer.Playfield.Time.Current + scroll_time, - IsStrong = strong - }; - - h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - if (strong) - rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h)); - else - rulesetContainer.Playfield.Add(new DrawableRimHit(h)); - } - - private class DrawableTestHit : DrawableHitObject - { - public DrawableTestHit(TaikoHitObject hitObject) - : base(hitObject) - { - } - - protected override void UpdateState(ArmedState state) - { - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Visual; +using OpenTK; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TestCaseTaikoPlayfield : OsuTestCase + { + private const double default_duration = 1000; + private const float scroll_time = 1000; + + protected override double TimePerAction => default_duration * 2; + + private readonly Random rng = new Random(1337); + private TaikoRulesetContainer rulesetContainer; + private Container playfieldContainer; + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + AddStep("Hit!", () => addHitJudgement(false)); + AddStep("Kiai hit", () => addHitJudgement(true)); + AddStep("Miss :(", addMissJudgement); + AddStep("DrumRoll", () => addDrumRoll(false)); + AddStep("Strong DrumRoll", () => addDrumRoll(true)); + AddStep("Swell", () => addSwell()); + AddStep("Centre", () => addCentreHit(false)); + AddStep("Strong Centre", () => addCentreHit(true)); + AddStep("Rim", () => addRimHit(false)); + AddStep("Strong Rim", () => addRimHit(true)); + AddStep("Add bar line", () => addBarLine(false)); + AddStep("Add major bar line", () => addBarLine(true)); + AddStep("Height test 1", () => changePlayfieldSize(1)); + AddStep("Height test 2", () => changePlayfieldSize(2)); + AddStep("Height test 3", () => changePlayfieldSize(3)); + AddStep("Height test 4", () => changePlayfieldSize(4)); + AddStep("Height test 5", () => changePlayfieldSize(5)); + AddStep("Reset height", () => changePlayfieldSize(6)); + + var controlPointInfo = new ControlPointInfo(); + controlPointInfo.TimingPoints.Add(new TimingControlPoint()); + + WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap + { + HitObjects = new List { new CentreHit() }, + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty(), + Metadata = new BeatmapMetadata + { + Artist = @"Unknown", + Title = @"Sample Beatmap", + AuthorString = @"peppy", + }, + }, + ControlPointInfo = controlPointInfo + }); + + var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; + + Add(playfieldContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Height = 768, + Clock = new FramedClock(rateAdjustClock), + Children = new[] { rulesetContainer = new TaikoRulesetContainer(rulesets.GetRuleset(1).CreateInstance(), beatmap, true) } + }); + } + + private void changePlayfieldSize(int step) + { + double delay = 0; + + // Add new hits + switch (step) + { + case 1: + addCentreHit(false); + break; + case 2: + addCentreHit(true); + break; + case 3: + addDrumRoll(false); + break; + case 4: + addDrumRoll(true); + break; + case 5: + addSwell(); + delay = scroll_time - 100; + break; + } + + // Tween playfield height + switch (step) + { + default: + playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500); + break; + case 6: + playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500); + break; + } + } + + private void addHitJudgement(bool kiai) + { + HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great; + + var cpi = new ControlPointInfo(); + cpi.EffectPoints.Add(new EffectControlPoint + { + KiaiMode = kiai + }); + + Hit hit = new Hit(); + hit.ApplyDefaults(cpi, new BeatmapDifficulty()); + + var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; + + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult }); + + if (RNG.Next(10) == 0) + { + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult }); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoStrongHitJudgement()); + } + } + + private void addMissJudgement() + { + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss }); + } + + private void addBarLine(bool major, double delay = scroll_time) + { + BarLine bl = new BarLine { StartTime = rulesetContainer.Playfield.Time.Current + delay }; + + rulesetContainer.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl)); + } + + private void addSwell(double duration = default_duration) + { + var swell = new Swell + { + StartTime = rulesetContainer.Playfield.Time.Current + scroll_time, + Duration = duration, + }; + + swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + rulesetContainer.Playfield.Add(new DrawableSwell(swell)); + } + + private void addDrumRoll(bool strong, double duration = default_duration) + { + addBarLine(true); + addBarLine(true, scroll_time + duration); + + var d = new DrumRoll + { + StartTime = rulesetContainer.Playfield.Time.Current + scroll_time, + IsStrong = strong, + Duration = duration, + }; + + d.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + rulesetContainer.Playfield.Add(new DrawableDrumRoll(d)); + } + + private void addCentreHit(bool strong) + { + Hit h = new Hit + { + StartTime = rulesetContainer.Playfield.Time.Current + scroll_time, + IsStrong = strong + }; + + h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + if (strong) + rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h)); + else + rulesetContainer.Playfield.Add(new DrawableCentreHit(h)); + } + + private void addRimHit(bool strong) + { + Hit h = new Hit + { + StartTime = rulesetContainer.Playfield.Time.Current + scroll_time, + IsStrong = strong + }; + + h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + if (strong) + rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h)); + else + rulesetContainer.Playfield.Add(new DrawableRimHit(h)); + } + + private class DrawableTestHit : DrawableHitObject + { + public DrawableTestHit(TaikoHitObject hitObject) + : base(hitObject) + { + } + + protected override void UpdateState(ArmedState state) + { + } + } + } +} 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 df73fa61cb..ccd69c574d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -1,10 +1,10 @@ - - - - WinExe - netcoreapp2.0;net461 - - - - + + + + WinExe + netcoreapp2.0;net461 + + + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs index afa3d162f4..91aafdae76 100644 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs @@ -1,61 +1,61 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Taiko.Audio -{ - public class DrumSampleMapping - { - private readonly ControlPointInfo controlPoints; - private readonly Dictionary mappings = new Dictionary(); - - public readonly List Sounds = new List(); - - public DrumSampleMapping(ControlPointInfo controlPoints) - { - this.controlPoints = controlPoints; - - IEnumerable samplePoints; - if (controlPoints.SamplePoints.Count == 0) - // Get the default sample point - samplePoints = new[] { controlPoints.SamplePointAt(double.MinValue) }; - else - samplePoints = controlPoints.SamplePoints; - - foreach (var s in samplePoints) - { - var centre = s.GetSampleInfo(); - var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP); - - // todo: this is ugly - centre.Namespace = "taiko"; - rim.Namespace = "taiko"; - - mappings[s.Time] = new DrumSample - { - Centre = addSound(centre), - Rim = addSound(rim) - }; - } - } - - private SkinnableSound addSound(SampleInfo sampleInfo) - { - var drawable = new SkinnableSound(sampleInfo); - Sounds.Add(drawable); - return drawable; - } - - public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time]; - - public class DrumSample - { - public SkinnableSound Centre; - public SkinnableSound Rim; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Taiko.Audio +{ + public class DrumSampleMapping + { + private readonly ControlPointInfo controlPoints; + private readonly Dictionary mappings = new Dictionary(); + + public readonly List Sounds = new List(); + + public DrumSampleMapping(ControlPointInfo controlPoints) + { + this.controlPoints = controlPoints; + + IEnumerable samplePoints; + if (controlPoints.SamplePoints.Count == 0) + // Get the default sample point + samplePoints = new[] { controlPoints.SamplePointAt(double.MinValue) }; + else + samplePoints = controlPoints.SamplePoints; + + foreach (var s in samplePoints) + { + var centre = s.GetSampleInfo(); + var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP); + + // todo: this is ugly + centre.Namespace = "taiko"; + rim.Namespace = "taiko"; + + mappings[s.Time] = new DrumSample + { + Centre = addSound(centre), + Rim = addSound(rim) + }; + } + } + + private SkinnableSound addSound(SampleInfo sampleInfo) + { + var drawable = new SkinnableSound(sampleInfo); + Sounds.Add(drawable); + return drawable; + } + + public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time]; + + public class DrumSample + { + public SkinnableSound Centre; + public SkinnableSound Rim; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 9d6b5b5ce4..2f175a9922 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -1,200 +1,200 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Taiko.Objects; -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.IO.Serialization; -using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Taiko.Beatmaps -{ - internal class TaikoBeatmapConverter : BeatmapConverter - { - /// - /// osu! is generally slower than taiko, so a factor is added to increase - /// speed. This must be used everywhere slider length or beat length is used. - /// - private const float legacy_velocity_multiplier = 1.4f; - - /// - /// Because swells are easier in taiko than spinners are in osu!, - /// legacy taiko multiplies a factor when converting the number of required hits. - /// - private const float swell_hit_multiplier = 1.65f; - - /// - /// Base osu! slider scoring distance. - /// - private const float osu_base_scoring_distance = 100; - - /// - /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. - /// - private const float taiko_base_distance = 100; - - private readonly bool isForCurrentRuleset; - - protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(HitObject) }; - - public TaikoBeatmapConverter(bool isForCurrentRuleset) - { - this.isForCurrentRuleset = isForCurrentRuleset; - } - - protected override Beatmap ConvertBeatmap(Beatmap original) - { - // Rewrite the beatmap info to add the slider velocity multiplier - BeatmapInfo info = original.BeatmapInfo.DeepClone(); - info.BaseDifficulty.SliderMultiplier *= legacy_velocity_multiplier; - - Beatmap converted = base.ConvertBeatmap(original); - - if (original.BeatmapInfo.RulesetID == 3) - { - // Post processing step to transform mania hit objects with the same start time into strong hits - converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => - { - TaikoHitObject first = x.First(); - if (x.Skip(1).Any()) - first.IsStrong = true; - return first; - }).ToList(); - } - - return converted; - } - - protected override IEnumerable ConvertHitObject(HitObject obj, Beatmap beatmap) - { - var distanceData = obj as IHasDistance; - var repeatsData = obj as IHasRepeats; - var endTimeData = obj as IHasEndTime; - var curveData = obj as IHasCurve; - - // Old osu! used hit sounding to determine various hit type information - List samples = obj.Samples; - - bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH); - - if (distanceData != null) - { - // Number of spans of the object - one for the initial length and for each repeat - int spans = repeatsData?.SpanCount() ?? 1; - - TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime); - - double speedAdjustment = difficultyPoint.SpeedMultiplier; - double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment; - - // The true distance, accounting for any repeats. This ends up being the drum roll distance later - double distance = distanceData.Distance * spans * legacy_velocity_multiplier; - - // The velocity of the taiko hit object - calculated as the velocity of a drum roll - double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; - // The duration of the taiko hit object - double taikoDuration = distance / taikoVelocity; - - // The velocity of the osu! hit object - calculated as the velocity of a slider - double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; - // The duration of the osu! hit object - double osuDuration = distance / osuVelocity; - - // osu-stable always uses the speed-adjusted beatlength to determine the velocities, but - // only uses it for tick rate if beatmap version < 8 - if (beatmap.BeatmapInfo.BeatmapVersion >= 8) - speedAdjustedBeatLength *= speedAdjustment; - - // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat - double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans); - - if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) - { - List> allSamples = curveData != null ? curveData.RepeatSamples : new List>(new[] { samples }); - - int i = 0; - for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) - { - List currentSamples = allSamples[i]; - bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE); - strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH); - - if (isRim) - { - yield return new RimHit - { - StartTime = j, - Samples = currentSamples, - IsStrong = strong - }; - } - else - { - yield return new CentreHit - { - StartTime = j, - Samples = currentSamples, - IsStrong = strong, - }; - } - - i = (i + 1) % allSamples.Count; - } - } - else - { - yield return new DrumRoll - { - StartTime = obj.StartTime, - Samples = obj.Samples, - IsStrong = strong, - Duration = taikoDuration, - TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4, - }; - } - } - else if (endTimeData != null) - { - double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; - - yield return new Swell - { - StartTime = obj.StartTime, - Samples = obj.Samples, - IsStrong = strong, - Duration = endTimeData.Duration, - RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier), - }; - } - else - { - bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE); - - if (isRim) - { - yield return new RimHit - { - StartTime = obj.StartTime, - Samples = obj.Samples, - IsStrong = strong, - }; - } - else - { - yield return new CentreHit - { - StartTime = obj.StartTime, - Samples = obj.Samples, - IsStrong = strong, - }; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Taiko.Objects; +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.IO.Serialization; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Taiko.Beatmaps +{ + internal class TaikoBeatmapConverter : BeatmapConverter + { + /// + /// osu! is generally slower than taiko, so a factor is added to increase + /// speed. This must be used everywhere slider length or beat length is used. + /// + private const float legacy_velocity_multiplier = 1.4f; + + /// + /// Because swells are easier in taiko than spinners are in osu!, + /// legacy taiko multiplies a factor when converting the number of required hits. + /// + private const float swell_hit_multiplier = 1.65f; + + /// + /// Base osu! slider scoring distance. + /// + private const float osu_base_scoring_distance = 100; + + /// + /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. + /// + private const float taiko_base_distance = 100; + + private readonly bool isForCurrentRuleset; + + protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(HitObject) }; + + public TaikoBeatmapConverter(bool isForCurrentRuleset) + { + this.isForCurrentRuleset = isForCurrentRuleset; + } + + protected override Beatmap ConvertBeatmap(Beatmap original) + { + // Rewrite the beatmap info to add the slider velocity multiplier + BeatmapInfo info = original.BeatmapInfo.DeepClone(); + info.BaseDifficulty.SliderMultiplier *= legacy_velocity_multiplier; + + Beatmap converted = base.ConvertBeatmap(original); + + if (original.BeatmapInfo.RulesetID == 3) + { + // Post processing step to transform mania hit objects with the same start time into strong hits + converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => + { + TaikoHitObject first = x.First(); + if (x.Skip(1).Any()) + first.IsStrong = true; + return first; + }).ToList(); + } + + return converted; + } + + protected override IEnumerable ConvertHitObject(HitObject obj, Beatmap beatmap) + { + var distanceData = obj as IHasDistance; + var repeatsData = obj as IHasRepeats; + var endTimeData = obj as IHasEndTime; + var curveData = obj as IHasCurve; + + // Old osu! used hit sounding to determine various hit type information + List samples = obj.Samples; + + bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH); + + if (distanceData != null) + { + // Number of spans of the object - one for the initial length and for each repeat + int spans = repeatsData?.SpanCount() ?? 1; + + TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime); + + double speedAdjustment = difficultyPoint.SpeedMultiplier; + double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment; + + // The true distance, accounting for any repeats. This ends up being the drum roll distance later + double distance = distanceData.Distance * spans * legacy_velocity_multiplier; + + // The velocity of the taiko hit object - calculated as the velocity of a drum roll + double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; + // The duration of the taiko hit object + double taikoDuration = distance / taikoVelocity; + + // The velocity of the osu! hit object - calculated as the velocity of a slider + double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; + // The duration of the osu! hit object + double osuDuration = distance / osuVelocity; + + // osu-stable always uses the speed-adjusted beatlength to determine the velocities, but + // only uses it for tick rate if beatmap version < 8 + if (beatmap.BeatmapInfo.BeatmapVersion >= 8) + speedAdjustedBeatLength *= speedAdjustment; + + // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat + double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans); + + if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) + { + List> allSamples = curveData != null ? curveData.RepeatSamples : new List>(new[] { samples }); + + int i = 0; + for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) + { + List currentSamples = allSamples[i]; + bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE); + strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH); + + if (isRim) + { + yield return new RimHit + { + StartTime = j, + Samples = currentSamples, + IsStrong = strong + }; + } + else + { + yield return new CentreHit + { + StartTime = j, + Samples = currentSamples, + IsStrong = strong, + }; + } + + i = (i + 1) % allSamples.Count; + } + } + else + { + yield return new DrumRoll + { + StartTime = obj.StartTime, + Samples = obj.Samples, + IsStrong = strong, + Duration = taikoDuration, + TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4, + }; + } + } + else if (endTimeData != null) + { + double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; + + yield return new Swell + { + StartTime = obj.StartTime, + Samples = obj.Samples, + IsStrong = strong, + Duration = endTimeData.Duration, + RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier), + }; + } + else + { + bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE); + + if (isRim) + { + yield return new RimHit + { + StartTime = obj.StartTime, + Samples = obj.Samples, + IsStrong = strong, + }; + } + else + { + yield return new CentreHit + { + StartTime = obj.StartTime, + Samples = obj.Samples, + IsStrong = strong, + }; + } + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs index a713b8ba8c..446dd0d11b 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs @@ -1,23 +1,23 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Taiko.Judgements -{ - public class TaikoDrumRollTickJudgement : TaikoJudgement - { - public override bool AffectsCombo => false; - - protected override int NumericResultFor(HitResult result) - { - switch (result) - { - default: - return 0; - case HitResult.Great: - return 200; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Judgements +{ + public class TaikoDrumRollTickJudgement : TaikoJudgement + { + public override bool AffectsCombo => false; + + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Great: + return 200; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs index f3dc45d78e..9b1f7a08b5 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Taiko.Judgements -{ - public class TaikoJudgement : Judgement - { - public override HitResult MaxResult => HitResult.Great; - - /// - /// Computes the numeric result value for the combo portion of the score. - /// - /// The result to compute the value for. - /// The numeric result value. - protected override int NumericResultFor(HitResult result) - { - switch (result) - { - default: - return 0; - case HitResult.Good: - return 100; - case HitResult.Great: - return 300; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Judgements +{ + public class TaikoJudgement : Judgement + { + public override HitResult MaxResult => HitResult.Great; + + /// + /// Computes the numeric result value for the combo portion of the score. + /// + /// The result to compute the value for. + /// The numeric result value. + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Good: + return 100; + case HitResult.Great: + return 300; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs index c477851335..288ad236aa 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Taiko.Judgements -{ - public class TaikoStrongHitJudgement : TaikoJudgement - { - public override bool AffectsCombo => false; - - public TaikoStrongHitJudgement() - { - Final = true; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Judgements +{ + public class TaikoStrongHitJudgement : TaikoJudgement + { + public override bool AffectsCombo => false; + + public TaikoStrongHitJudgement() + { + Final = true; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs index 239f0d5a6b..c63bf10542 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Taiko.Replays; -using osu.Game.Users; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModAutoplay : ModAutoplay - { - protected override Score CreateReplayScore(Beatmap beatmap) - { - return new Score - { - User = new User { Username = "mekkadosu!" }, - Replay = new TaikoAutoGenerator(beatmap).Generate(), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Replays; +using osu.Game.Users; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModAutoplay : ModAutoplay + { + protected override Score CreateReplayScore(Beatmap beatmap) + { + return new Score + { + User = new User { Username = "mekkadosu!" }, + Replay = new TaikoAutoGenerator(beatmap).Generate(), + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs index 703e6b4f1c..80d8da6cf6 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModDaycore : ModDaycore - { - public override double ScoreMultiplier => 0.3; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModDaycore : ModDaycore + { + public override double ScoreMultiplier => 0.3; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDoubleTime.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDoubleTime.cs index 2ae52b4549..21e48f2c8c 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDoubleTime.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDoubleTime.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModDoubleTime : ModDoubleTime - { - public override double ScoreMultiplier => 1.12; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1.12; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index be6510459e..16364964f8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModEasy : ModEasy - { - public override string Description => @"Beats move slower, less accuracy required, and three lives!"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModEasy : ModEasy + { + public override string Description => @"Beats move slower, less accuracy required, and three lives!"; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index a2c6d7f9e0..49f7786f59 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModFlashlight : ModFlashlight - { - public override double ScoreMultiplier => 1.12; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModFlashlight : ModFlashlight + { + public override double ScoreMultiplier => 1.12; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs index 6542b5a844..ef609b893d 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModHalfTime : ModHalfTime - { - public override double ScoreMultiplier => 0.3; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModHalfTime : ModHalfTime + { + public override double ScoreMultiplier => 0.3; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index ba304c41d8..908778d4d6 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModHardRock : ModHardRock - { - public override double ScoreMultiplier => 1.06; - public override bool Ranked => true; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModHardRock : ModHardRock + { + public override double ScoreMultiplier => 1.06; + public override bool Ranked => true; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index be987a1773..f4e12ede21 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModHidden : ModHidden - { - public override string Description => @"Beats fade out before you hit them!"; - public override double ScoreMultiplier => 1.06; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModHidden : ModHidden + { + public override string Description => @"Beats fade out before you hit them!"; + public override double ScoreMultiplier => 1.06; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs index 0504b7c5b6..abe4b2e25e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModNightcore : ModNightcore - { - public override double ScoreMultiplier => 1.12; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModNightcore : ModNightcore + { + public override double ScoreMultiplier => 1.12; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModNoFail.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModNoFail.cs index 3e10f58bbd..5a8e903fc6 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModNoFail.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModNoFail.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModNoFail : ModNoFail - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModNoFail : ModNoFail + { + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModPerfect.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModPerfect.cs index 7388283d41..027feed112 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModPerfect.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModPerfect.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModPerfect : ModPerfect - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModPerfect : ModPerfect + { + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs index d5ad04f595..fd72291f1f 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModRelax : ModRelax - { - public override string Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModRelax : ModRelax + { + public override string Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's."; + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSuddenDeath.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSuddenDeath.cs index 129d181616..6ed1ad3bda 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSuddenDeath.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSuddenDeath.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public class TaikoModSuddenDeath : ModSuddenDeath - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModSuddenDeath : ModSuddenDeath + { + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs b/osu.Game.Rulesets.Taiko/Objects/BarLine.cs index efd7d57ed7..f7496642dd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/BarLine.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class BarLine : TaikoHitObject - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class BarLine : TaikoHitObject + { + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/CentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/CentreHit.cs index ff7c36a8ad..898c562ea9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/CentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/CentreHit.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class CentreHit : Hit - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class CentreHit : Hit + { + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs index d3a38289a8..db5f418e18 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using OpenTK; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - /// - /// A line that scrolls alongside hit objects in the playfield and visualises control points. - /// - public class DrawableBarLine : DrawableHitObject - { - /// - /// The width of the line tracker. - /// - private const float tracker_width = 2f; - - /// - /// Fade out time calibrated to a pre-empt of 1000ms. - /// - private const float base_fadeout_time = 100f; - - /// - /// The visual line tracker. - /// - protected Box Tracker; - - /// - /// The bar line. - /// - protected readonly BarLine BarLine; - - public DrawableBarLine(BarLine barLine) - : base(barLine) - { - BarLine = barLine; - - Anchor = Anchor.CentreLeft; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Y; - Width = tracker_width; - - InternalChildren = new[] - { - Tracker = new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - EdgeSmoothness = new Vector2(0.5f, 0), - Alpha = 0.75f - } - }; - } - - protected override void UpdateState(ArmedState state) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using OpenTK; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + /// + /// A line that scrolls alongside hit objects in the playfield and visualises control points. + /// + public class DrawableBarLine : DrawableHitObject + { + /// + /// The width of the line tracker. + /// + private const float tracker_width = 2f; + + /// + /// Fade out time calibrated to a pre-empt of 1000ms. + /// + private const float base_fadeout_time = 100f; + + /// + /// The visual line tracker. + /// + protected Box Tracker; + + /// + /// The bar line. + /// + protected readonly BarLine BarLine; + + public DrawableBarLine(BarLine barLine) + : base(barLine) + { + BarLine = barLine; + + Anchor = Anchor.CentreLeft; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Y; + Width = tracker_width; + + InternalChildren = new[] + { + Tracker = new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + EdgeSmoothness = new Vector2(0.5f, 0), + Alpha = 0.75f + } + }; + } + + protected override void UpdateState(ArmedState state) + { + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs index 19a6e4eac2..300edd2e76 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs @@ -1,67 +1,67 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableBarLineMajor : DrawableBarLine - { - /// - /// The vertical offset of the triangles from the line tracker. - /// - private const float triangle_offfset = 10f; - - /// - /// The size of the triangles. - /// - private const float triangle_size = 20f; - - private readonly Container triangleContainer; - - public DrawableBarLineMajor(BarLine barLine) - : base(barLine) - { - AddInternal(triangleContainer = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new EquilateralTriangle - { - Name = "Top", - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Position = new Vector2(0, -triangle_offfset), - Size = new Vector2(-triangle_size), - EdgeSmoothness = new Vector2(1), - }, - new EquilateralTriangle - { - Name = "Bottom", - Anchor = Anchor.BottomCentre, - Origin = Anchor.TopCentre, - Position = new Vector2(0, triangle_offfset), - Size = new Vector2(triangle_size), - EdgeSmoothness = new Vector2(1), - } - } - }); - - Tracker.Alpha = 1f; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - using (triangleContainer.BeginAbsoluteSequence(HitObject.StartTime)) - triangleContainer.FadeOut(150); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableBarLineMajor : DrawableBarLine + { + /// + /// The vertical offset of the triangles from the line tracker. + /// + private const float triangle_offfset = 10f; + + /// + /// The size of the triangles. + /// + private const float triangle_size = 20f; + + private readonly Container triangleContainer; + + public DrawableBarLineMajor(BarLine barLine) + : base(barLine) + { + AddInternal(triangleContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new EquilateralTriangle + { + Name = "Top", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Position = new Vector2(0, -triangle_offfset), + Size = new Vector2(-triangle_size), + EdgeSmoothness = new Vector2(1), + }, + new EquilateralTriangle + { + Name = "Bottom", + Anchor = Anchor.BottomCentre, + Origin = Anchor.TopCentre, + Position = new Vector2(0, triangle_offfset), + Size = new Vector2(triangle_size), + EdgeSmoothness = new Vector2(1), + } + } + }); + + Tracker.Alpha = 1f; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + using (triangleContainer.BeginAbsoluteSequence(HitObject.StartTime)) + triangleContainer.FadeOut(150); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs index 56a0976ddb..dda96c2caf 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableCentreHit : DrawableHit - { - protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre }; - - public DrawableCentreHit(Hit hit) - : base(hit) - { - MainPiece.Add(new CentreHitSymbolPiece()); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - MainPiece.AccentColour = colours.PinkDarker; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableCentreHit : DrawableHit + { + protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre }; + + public DrawableCentreHit(Hit hit) + : base(hit) + { + MainPiece.Add(new CentreHitSymbolPiece()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + MainPiece.AccentColour = colours.PinkDarker; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs index c0bfaba5f5..a2dabf2b18 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableCentreHitStrong : DrawableHitStrong - { - protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre }; - - public DrawableCentreHitStrong(Hit hit) - : base(hit) - { - MainPiece.Add(new CentreHitSymbolPiece()); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - MainPiece.AccentColour = colours.PinkDarker; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableCentreHitStrong : DrawableHitStrong + { + protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre }; + + public DrawableCentreHitStrong(Hit hit) + : base(hit) + { + MainPiece.Add(new CentreHitSymbolPiece()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + MainPiece.AccentColour = colours.PinkDarker; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 2bb2d478c3..00eac4adca 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -1,107 +1,107 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.MathUtils; -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Judgements; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableDrumRoll : DrawableTaikoHitObject - { - /// - /// Number of rolling hits required to reach the dark/final colour. - /// - private const int rolling_hits_for_engaged_colour = 5; - - /// - /// Rolling number of tick hits. This increases for hits and decreases for misses. - /// - private int rollingHits; - - public DrawableDrumRoll(DrumRoll drumRoll) - : base(drumRoll) - { - RelativeSizeAxes = Axes.Y; - - Container tickContainer; - MainPiece.Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); - - foreach (var tick in drumRoll.NestedHitObjects.OfType()) - { - var newTick = new DrawableDrumRollTick(tick); - newTick.OnJudgement += onTickJudgement; - - AddNested(newTick); - tickContainer.Add(newTick); - } - } - - protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(); - - public override bool OnPressed(TaikoAction action) => false; - - private Color4 colourIdle; - private Color4 colourEngaged; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - MainPiece.AccentColour = colourIdle = colours.YellowDark; - colourEngaged = colours.YellowDarker; - } - - private void onTickJudgement(DrawableHitObject obj, Judgement judgement) - { - if (judgement.Result > HitResult.Miss) - rollingHits++; - else - rollingHits--; - - rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour); - - Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); - MainPiece.FadeAccent(newColour, 100); - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (userTriggered) - return; - - if (timeOffset < 0) - return; - - int countHit = NestedHitObjects.Count(o => o.IsHit); - if (countHit >= HitObject.RequiredGoodHits) - { - AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good }); - if (HitObject.IsStrong) - AddJudgement(new TaikoStrongHitJudgement()); - } - else - AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); - } - - protected override void UpdateState(ArmedState state) - { - switch (state) - { - case ArmedState.Hit: - case ArmedState.Miss: - this.FadeOut(100).Expire(); - break; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.MathUtils; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Judgements; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableDrumRoll : DrawableTaikoHitObject + { + /// + /// Number of rolling hits required to reach the dark/final colour. + /// + private const int rolling_hits_for_engaged_colour = 5; + + /// + /// Rolling number of tick hits. This increases for hits and decreases for misses. + /// + private int rollingHits; + + public DrawableDrumRoll(DrumRoll drumRoll) + : base(drumRoll) + { + RelativeSizeAxes = Axes.Y; + + Container tickContainer; + MainPiece.Add(tickContainer = new Container { RelativeSizeAxes = Axes.Both }); + + foreach (var tick in drumRoll.NestedHitObjects.OfType()) + { + var newTick = new DrawableDrumRollTick(tick); + newTick.OnJudgement += onTickJudgement; + + AddNested(newTick); + tickContainer.Add(newTick); + } + } + + protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(); + + public override bool OnPressed(TaikoAction action) => false; + + private Color4 colourIdle; + private Color4 colourEngaged; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + MainPiece.AccentColour = colourIdle = colours.YellowDark; + colourEngaged = colours.YellowDarker; + } + + private void onTickJudgement(DrawableHitObject obj, Judgement judgement) + { + if (judgement.Result > HitResult.Miss) + rollingHits++; + else + rollingHits--; + + rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour); + + Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); + MainPiece.FadeAccent(newColour, 100); + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (userTriggered) + return; + + if (timeOffset < 0) + return; + + int countHit = NestedHitObjects.Count(o => o.IsHit); + if (countHit >= HitObject.RequiredGoodHits) + { + AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good }); + if (HitObject.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); + } + else + AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); + } + + protected override void UpdateState(ArmedState state) + { + switch (state) + { + case ArmedState.Hit: + case ArmedState.Miss: + this.FadeOut(100).Expire(); + break; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 65a4e7bd95..7a57cf77b4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -1,53 +1,53 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Judgements; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableDrumRollTick : DrawableTaikoHitObject - { - public DrawableDrumRollTick(DrumRollTick tick) - : base(tick) - { - FillMode = FillMode.Fit; - } - - public override bool DisplayJudgement => false; - - protected override TaikoPiece CreateMainPiece() => new TickPiece - { - Filled = HitObject.FirstTick - }; - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!userTriggered) - return; - - if (!(Math.Abs(timeOffset) < HitObject.HitWindow)) - return; - - AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great }); - if (HitObject.IsStrong) - AddJudgement(new TaikoStrongHitJudgement()); - } - - protected override void UpdateState(ArmedState state) - { - switch (state) - { - case ArmedState.Hit: - this.ScaleTo(0, 100, Easing.OutQuint).Expire(); - break; - } - } - - public override bool OnPressed(TaikoAction action) => UpdateJudgement(true); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableDrumRollTick : DrawableTaikoHitObject + { + public DrawableDrumRollTick(DrumRollTick tick) + : base(tick) + { + FillMode = FillMode.Fit; + } + + public override bool DisplayJudgement => false; + + protected override TaikoPiece CreateMainPiece() => new TickPiece + { + Filled = HitObject.FirstTick + }; + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!userTriggered) + return; + + if (!(Math.Abs(timeOffset) < HitObject.HitWindow)) + return; + + AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great }); + if (HitObject.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); + } + + protected override void UpdateState(ArmedState state) + { + switch (state) + { + case ArmedState.Hit: + this.ScaleTo(0, 100, Easing.OutQuint).Expire(); + break; + } + } + + public override bool OnPressed(TaikoAction action) => UpdateJudgement(true); + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 75e1e2a247..d923b2dcdf 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -1,120 +1,120 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Judgements; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public abstract class DrawableHit : DrawableTaikoHitObject - { - /// - /// A list of keys which can result in hits for this HitObject. - /// - protected abstract TaikoAction[] HitActions { get; } - - /// - /// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully. - /// - protected bool SecondHitAllowed { get; private set; } - - /// - /// Whether the last key pressed is a valid hit key. - /// - private bool validKeyPressed; - - protected DrawableHit(Hit hit) - : base(hit) - { - FillMode = FillMode.Fit; - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!userTriggered) - { - if (!HitObject.HitWindows.CanBeHit(timeOffset)) - AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); - return; - } - - var result = HitObject.HitWindows.ResultFor(timeOffset); - if (result == HitResult.None) - return; - - if (!validKeyPressed || result == HitResult.Miss) - AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); - else - { - AddJudgement(new TaikoJudgement - { - Result = result, - Final = !HitObject.IsStrong - }); - - SecondHitAllowed = true; - } - } - - public override bool OnPressed(TaikoAction action) - { - validKeyPressed = HitActions.Contains(action); - - // Only count this as handled if the new judgement is a hit - return UpdateJudgement(true); - } - - protected override void Update() - { - base.Update(); - - Size = BaseSize * Parent.RelativeChildSize; - } - - protected override void UpdateState(ArmedState state) - { - var circlePiece = MainPiece as CirclePiece; - circlePiece?.FlashBox.FinishTransforms(); - - var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime; - using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true)) - { - switch (State.Value) - { - case ArmedState.Idle: - this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); - break; - case ArmedState.Miss: - this.FadeOut(100) - .Expire(); - break; - case ArmedState.Hit: - var flash = circlePiece?.FlashBox; - if (flash != null) - { - flash.FadeTo(0.9f); - flash.FadeOut(300); - } - - const float gravity_time = 300; - const float gravity_travel_height = 200; - - this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad); - - this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out) - .Then() - .MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In); - - this.FadeOut(800) - .Expire(); - - break; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public abstract class DrawableHit : DrawableTaikoHitObject + { + /// + /// A list of keys which can result in hits for this HitObject. + /// + protected abstract TaikoAction[] HitActions { get; } + + /// + /// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully. + /// + protected bool SecondHitAllowed { get; private set; } + + /// + /// Whether the last key pressed is a valid hit key. + /// + private bool validKeyPressed; + + protected DrawableHit(Hit hit) + : base(hit) + { + FillMode = FillMode.Fit; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!userTriggered) + { + if (!HitObject.HitWindows.CanBeHit(timeOffset)) + AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); + return; + } + + var result = HitObject.HitWindows.ResultFor(timeOffset); + if (result == HitResult.None) + return; + + if (!validKeyPressed || result == HitResult.Miss) + AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); + else + { + AddJudgement(new TaikoJudgement + { + Result = result, + Final = !HitObject.IsStrong + }); + + SecondHitAllowed = true; + } + } + + public override bool OnPressed(TaikoAction action) + { + validKeyPressed = HitActions.Contains(action); + + // Only count this as handled if the new judgement is a hit + return UpdateJudgement(true); + } + + protected override void Update() + { + base.Update(); + + Size = BaseSize * Parent.RelativeChildSize; + } + + protected override void UpdateState(ArmedState state) + { + var circlePiece = MainPiece as CirclePiece; + circlePiece?.FlashBox.FinishTransforms(); + + var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime; + using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true)) + { + switch (State.Value) + { + case ArmedState.Idle: + this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); + break; + case ArmedState.Miss: + this.FadeOut(100) + .Expire(); + break; + case ArmedState.Hit: + var flash = circlePiece?.FlashBox; + if (flash != null) + { + flash.FadeTo(0.9f); + flash.FadeOut(300); + } + + const float gravity_time = 300; + const float gravity_travel_height = 200; + + this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad); + + this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out) + .Then() + .MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In); + + this.FadeOut(800) + .Expire(); + + break; + } + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index cf6236e872..c416d50062 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -1,89 +1,89 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Judgements; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public abstract class DrawableHitStrong : DrawableHit - { - /// - /// The lenience for the second key press. - /// This does not adjust by map difficulty in ScoreV2 yet. - /// - private const double second_hit_window = 30; - - private double firstHitTime; - private bool firstKeyHeld; - private TaikoAction firstHitAction; - - protected DrawableHitStrong(Hit hit) - : base(hit) - { - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (!SecondHitAllowed) - { - base.CheckForJudgements(userTriggered, timeOffset); - return; - } - - if (!userTriggered) - { - if (timeOffset > second_hit_window) - AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.None }); - return; - } - - // If we get here, we're assured that the key pressed is the correct secondary key - - if (Math.Abs(firstHitTime - Time.Current) < second_hit_window) - AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great }); - } - - public override bool OnReleased(TaikoAction action) - { - if (action == firstHitAction) - firstKeyHeld = false; - return base.OnReleased(action); - } - - public override bool OnPressed(TaikoAction action) - { - if (AllJudged) - return false; - - // Check if we've handled the first key - if (!SecondHitAllowed) - { - // First key hasn't been handled yet, attempt to handle it - bool handled = base.OnPressed(action); - - if (handled) - { - firstHitTime = Time.Current; - firstHitAction = action; - firstKeyHeld = true; - } - - return handled; - } - - // Don't handle represses of the first key - if (firstHitAction == action) - return false; - - // Don't handle invalid hit action presses - if (!HitActions.Contains(action)) - return false; - - // Assume the intention was to hit the strong hit with both keys only if the first key is still being held down - return firstKeyHeld && UpdateJudgement(true); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Judgements; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public abstract class DrawableHitStrong : DrawableHit + { + /// + /// The lenience for the second key press. + /// This does not adjust by map difficulty in ScoreV2 yet. + /// + private const double second_hit_window = 30; + + private double firstHitTime; + private bool firstKeyHeld; + private TaikoAction firstHitAction; + + protected DrawableHitStrong(Hit hit) + : base(hit) + { + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (!SecondHitAllowed) + { + base.CheckForJudgements(userTriggered, timeOffset); + return; + } + + if (!userTriggered) + { + if (timeOffset > second_hit_window) + AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.None }); + return; + } + + // If we get here, we're assured that the key pressed is the correct secondary key + + if (Math.Abs(firstHitTime - Time.Current) < second_hit_window) + AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great }); + } + + public override bool OnReleased(TaikoAction action) + { + if (action == firstHitAction) + firstKeyHeld = false; + return base.OnReleased(action); + } + + public override bool OnPressed(TaikoAction action) + { + if (AllJudged) + return false; + + // Check if we've handled the first key + if (!SecondHitAllowed) + { + // First key hasn't been handled yet, attempt to handle it + bool handled = base.OnPressed(action); + + if (handled) + { + firstHitTime = Time.Current; + firstHitAction = action; + firstKeyHeld = true; + } + + return handled; + } + + // Don't handle represses of the first key + if (firstHitAction == action) + return false; + + // Don't handle invalid hit action presses + if (!HitActions.Contains(action)) + return false; + + // Assume the intention was to hit the strong hit with both keys only if the first key is still being held down + return firstKeyHeld && UpdateJudgement(true); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs index e8492ac168..f2194c6d56 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableRimHit : DrawableHit - { - protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim }; - - public DrawableRimHit(Hit hit) - : base(hit) - { - MainPiece.Add(new RimHitSymbolPiece()); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - MainPiece.AccentColour = colours.BlueDarker; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableRimHit : DrawableHit + { + protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim }; + + public DrawableRimHit(Hit hit) + : base(hit) + { + MainPiece.Add(new RimHitSymbolPiece()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + MainPiece.AccentColour = colours.BlueDarker; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs index b5e232a65d..728fe416f7 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableRimHitStrong : DrawableHitStrong - { - protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim }; - - public DrawableRimHitStrong(Hit hit) - : base(hit) - { - MainPiece.Add(new RimHitSymbolPiece()); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - MainPiece.AccentColour = colours.BlueDarker; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableRimHitStrong : DrawableHitStrong + { + protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim }; + + public DrawableRimHitStrong(Hit hit) + : base(hit) + { + MainPiece.Add(new RimHitSymbolPiece()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + MainPiece.AccentColour = colours.BlueDarker; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 37f1300d47..33cc29bccf 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -1,225 +1,225 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Taiko.Judgements; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public class DrawableSwell : DrawableTaikoHitObject - { - /// - /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime. - /// This is only ever invoked once. - /// - public event Action OnStart; - - private const float target_ring_thick_border = 1.4f; - private const float target_ring_thin_border = 1f; - private const float target_ring_scale = 5f; - private const float inner_ring_alpha = 0.65f; - - private readonly Container bodyContainer; - private readonly CircularContainer targetRing; - private readonly CircularContainer expandingRing; - - /// - /// The amount of times the user has hit this swell. - /// - private int userHits; - - private bool hasStarted; - private readonly SwellSymbolPiece symbol; - - public DrawableSwell(Swell swell) - : base(swell) - { - FillMode = FillMode.Fit; - - AddInternal(bodyContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Depth = 1, - Children = new Drawable[] - { - expandingRing = new CircularContainer - { - Name = "Expanding ring", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Alpha = 0, - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Masking = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = inner_ring_alpha, - } - } - }, - targetRing = new CircularContainer - { - Name = "Target ring (thick border)", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = target_ring_thick_border, - Blending = BlendingMode.Additive, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - }, - new CircularContainer - { - Name = "Target ring (thin border)", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = target_ring_thin_border, - BorderColour = Color4.White, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - } - } - } - } - }); - - MainPiece.Add(symbol = new SwellSymbolPiece()); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - MainPiece.AccentColour = colours.YellowDark; - expandingRing.Colour = colours.YellowLight; - targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize - Width *= Parent.RelativeChildSize.X; - } - - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (userTriggered) - { - userHits++; - - var completion = (float)userHits / HitObject.RequiredHits; - - expandingRing - .FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50) - .Then() - .FadeTo(completion / 8, 2000, Easing.OutQuint); - - symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint); - - expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); - - if (userHits == HitObject.RequiredHits) - AddJudgement(new TaikoJudgement { Result = HitResult.Great }); - } - else - { - if (timeOffset < 0) - return; - - //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP - AddJudgement(userHits > HitObject.RequiredHits / 2 - ? new TaikoJudgement { Result = HitResult.Good } - : new TaikoJudgement { Result = HitResult.Miss }); - } - } - - protected override void UpdateState(ArmedState state) - { - const float preempt = 100; - const float out_transition_time = 300; - - double untilStartTime = HitObject.StartTime - Time.Current; - double untilJudgement = untilStartTime + (Judgements.FirstOrDefault()?.TimeOffset ?? 0) + HitObject.Duration; - - targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint); - this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out); - - switch (state) - { - case ArmedState.Hit: - bodyContainer.Delay(untilJudgement).ScaleTo(1.4f, out_transition_time); - break; - } - - Expire(); - } - - protected override void Update() - { - base.Update(); - - Size = BaseSize * Parent.RelativeChildSize; - - // Make the swell stop at the hit target - X = Math.Max(0, X); - - double t = Math.Min(HitObject.StartTime, Time.Current); - if (t == HitObject.StartTime && !hasStarted) - { - OnStart?.Invoke(); - hasStarted = true; - } - } - - private bool? lastWasCentre; - - public override bool OnPressed(TaikoAction action) - { - // Don't handle keys before the swell starts - if (Time.Current < HitObject.StartTime) - return false; - - var isCentre = action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre; - - // Ensure alternating centre and rim hits - if (lastWasCentre == isCentre) - return false; - lastWasCentre = isCentre; - - UpdateJudgement(true); - - return true; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public class DrawableSwell : DrawableTaikoHitObject + { + /// + /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime. + /// This is only ever invoked once. + /// + public event Action OnStart; + + private const float target_ring_thick_border = 1.4f; + private const float target_ring_thin_border = 1f; + private const float target_ring_scale = 5f; + private const float inner_ring_alpha = 0.65f; + + private readonly Container bodyContainer; + private readonly CircularContainer targetRing; + private readonly CircularContainer expandingRing; + + /// + /// The amount of times the user has hit this swell. + /// + private int userHits; + + private bool hasStarted; + private readonly SwellSymbolPiece symbol; + + public DrawableSwell(Swell swell) + : base(swell) + { + FillMode = FillMode.Fit; + + AddInternal(bodyContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Depth = 1, + Children = new Drawable[] + { + expandingRing = new CircularContainer + { + Name = "Expanding ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Masking = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = inner_ring_alpha, + } + } + }, + targetRing = new CircularContainer + { + Name = "Target ring (thick border)", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = target_ring_thick_border, + Blending = BlendingMode.Additive, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + }, + new CircularContainer + { + Name = "Target ring (thin border)", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = target_ring_thin_border, + BorderColour = Color4.White, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + } + } + } + } + }); + + MainPiece.Add(symbol = new SwellSymbolPiece()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + MainPiece.AccentColour = colours.YellowDark; + expandingRing.Colour = colours.YellowLight; + targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize + Width *= Parent.RelativeChildSize.X; + } + + protected override void CheckForJudgements(bool userTriggered, double timeOffset) + { + if (userTriggered) + { + userHits++; + + var completion = (float)userHits / HitObject.RequiredHits; + + expandingRing + .FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50) + .Then() + .FadeTo(completion / 8, 2000, Easing.OutQuint); + + symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint); + + expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); + + if (userHits == HitObject.RequiredHits) + AddJudgement(new TaikoJudgement { Result = HitResult.Great }); + } + else + { + if (timeOffset < 0) + return; + + //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP + AddJudgement(userHits > HitObject.RequiredHits / 2 + ? new TaikoJudgement { Result = HitResult.Good } + : new TaikoJudgement { Result = HitResult.Miss }); + } + } + + protected override void UpdateState(ArmedState state) + { + const float preempt = 100; + const float out_transition_time = 300; + + double untilStartTime = HitObject.StartTime - Time.Current; + double untilJudgement = untilStartTime + (Judgements.FirstOrDefault()?.TimeOffset ?? 0) + HitObject.Duration; + + targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint); + this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out); + + switch (state) + { + case ArmedState.Hit: + bodyContainer.Delay(untilJudgement).ScaleTo(1.4f, out_transition_time); + break; + } + + Expire(); + } + + protected override void Update() + { + base.Update(); + + Size = BaseSize * Parent.RelativeChildSize; + + // Make the swell stop at the hit target + X = Math.Max(0, X); + + double t = Math.Min(HitObject.StartTime, Time.Current); + if (t == HitObject.StartTime && !hasStarted) + { + OnStart?.Invoke(); + hasStarted = true; + } + } + + private bool? lastWasCentre; + + public override bool OnPressed(TaikoAction action) + { + // Don't handle keys before the swell starts + if (Time.Current < HitObject.StartTime) + return false; + + var isCentre = action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre; + + // Ensure alternating centre and rim hits + if (lastWasCentre == isCentre) + return false; + lastWasCentre = isCentre; + + UpdateJudgement(true); + + return true; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index f20ad5b4aa..971fd8854d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -1,52 +1,52 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; -using OpenTK; -using System.Linq; -using osu.Game.Audio; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables -{ - public abstract class DrawableTaikoHitObject : DrawableHitObject, IKeyBindingHandler - where TaikoHitType : TaikoHitObject - { - public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); - - protected readonly Vector2 BaseSize; - - protected readonly TaikoPiece MainPiece; - - public new TaikoHitType HitObject; - - protected DrawableTaikoHitObject(TaikoHitType hitObject) - : base(hitObject) - { - HitObject = hitObject; - - Anchor = Anchor.CentreLeft; - Origin = Anchor.Custom; - - RelativeSizeAxes = Axes.Both; - Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); - - InternalChild = MainPiece = CreateMainPiece(); - MainPiece.KiaiMode = HitObject.Kiai; - } - - // Normal and clap samples are handled by the drum - protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP); - - protected override string SampleNamespace => "Taiko"; - - protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); - - public abstract bool OnPressed(TaikoAction action); - - public virtual bool OnReleased(TaikoAction action) => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; +using OpenTK; +using System.Linq; +using osu.Game.Audio; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public abstract class DrawableTaikoHitObject : DrawableHitObject, IKeyBindingHandler + where TaikoHitType : TaikoHitObject + { + public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); + + protected readonly Vector2 BaseSize; + + protected readonly TaikoPiece MainPiece; + + public new TaikoHitType HitObject; + + protected DrawableTaikoHitObject(TaikoHitType hitObject) + : base(hitObject) + { + HitObject = hitObject; + + Anchor = Anchor.CentreLeft; + Origin = Anchor.Custom; + + RelativeSizeAxes = Axes.Both; + Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); + + InternalChild = MainPiece = CreateMainPiece(); + MainPiece.KiaiMode = HitObject.Kiai; + } + + // Normal and clap samples are handled by the drum + protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP); + + protected override string SampleNamespace => "Taiko"; + + protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); + + public abstract bool OnPressed(TaikoAction action); + + public virtual bool OnReleased(TaikoAction action) => false; + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs index 05c3f45d80..27e2b3c762 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitSymbolPiece.cs @@ -1,36 +1,36 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - /// - /// The symbol used for centre hit pieces. - /// - public class CentreHitSymbolPiece : Container - { - public CentreHitSymbolPiece() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - Size = new Vector2(CirclePiece.SYMBOL_SIZE); - Padding = new MarginPadding(CirclePiece.SYMBOL_BORDER); - - Children = new[] - { - new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] { new Box { RelativeSizeAxes = Axes.Both } } - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces +{ + /// + /// The symbol used for centre hit pieces. + /// + public class CentreHitSymbolPiece : Container + { + public CentreHitSymbolPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(CirclePiece.SYMBOL_SIZE); + Padding = new MarginPadding(CirclePiece.SYMBOL_BORDER); + + Children = new[] + { + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] { new Box { RelativeSizeAxes = Axes.Both } } + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index b68dac9e85..7a4931dc71 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -1,158 +1,158 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Backgrounds; -using OpenTK.Graphics; -using osu.Game.Beatmaps.ControlPoints; -using osu.Framework.Audio.Track; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - /// - /// A circle piece which is used uniformly through osu!taiko to visualise hitobjects. - /// - /// Note that this can actually be non-circle if the width is changed. See - /// for a usage example. - /// - /// - public class CirclePiece : TaikoPiece - { - public const float SYMBOL_SIZE = 0.45f; - public const float SYMBOL_BORDER = 8; - private const double pre_beat_transition_time = 80; - - /// - /// The colour of the inner circle and outer glows. - /// - public override Color4 AccentColour - { - get { return base.AccentColour; } - set - { - base.AccentColour = value; - - background.Colour = AccentColour; - - resetEdgeEffects(); - } - } - - /// - /// Whether Kiai mode effects are enabled for this circle piece. - /// - public override bool KiaiMode - { - get { return base.KiaiMode; } - set - { - base.KiaiMode = value; - - resetEdgeEffects(); - } - } - - protected override Container Content => content; - - private readonly Container content; - - private readonly Container background; - - public Box FlashBox; - - public CirclePiece() - { - EarlyActivationMilliseconds = pre_beat_transition_time; - - AddRangeInternal(new Drawable[] - { - background = new CircularContainer - { - Name = "Background", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] - { - new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }, - new Triangles - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - ColourLight = Color4.White, - ColourDark = Color4.White.Darken(0.1f) - } - } - }, - new CircularContainer - { - Name = "Ring", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - BorderThickness = 8, - BorderColour = Color4.White, - Masking = true, - Children = new[] - { - FlashBox = new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - Blending = BlendingMode.Additive, - Alpha = 0, - AlwaysPresent = true - } - } - }, - content = new Container - { - Name = "Content", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - } - }); - } - - private const float edge_alpha_kiai = 0.5f; - - private void resetEdgeEffects() - { - background.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = AccentColour.Opacity(KiaiMode ? edge_alpha_kiai : 1f), - Radius = KiaiMode ? 32 : 8 - }; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) - { - if (!effectPoint.KiaiMode) - return; - - if (beatIndex % (int)timingPoint.TimeSignature != 0) - return; - - double duration = timingPoint.BeatLength * 2; - - background - .FadeEdgeEffectTo(1, pre_beat_transition_time, Easing.OutQuint) - .Then() - .FadeEdgeEffectTo(edge_alpha_kiai, duration, Easing.OutQuint); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Backgrounds; +using OpenTK.Graphics; +using osu.Game.Beatmaps.ControlPoints; +using osu.Framework.Audio.Track; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces +{ + /// + /// A circle piece which is used uniformly through osu!taiko to visualise hitobjects. + /// + /// Note that this can actually be non-circle if the width is changed. See + /// for a usage example. + /// + /// + public class CirclePiece : TaikoPiece + { + public const float SYMBOL_SIZE = 0.45f; + public const float SYMBOL_BORDER = 8; + private const double pre_beat_transition_time = 80; + + /// + /// The colour of the inner circle and outer glows. + /// + public override Color4 AccentColour + { + get { return base.AccentColour; } + set + { + base.AccentColour = value; + + background.Colour = AccentColour; + + resetEdgeEffects(); + } + } + + /// + /// Whether Kiai mode effects are enabled for this circle piece. + /// + public override bool KiaiMode + { + get { return base.KiaiMode; } + set + { + base.KiaiMode = value; + + resetEdgeEffects(); + } + } + + protected override Container Content => content; + + private readonly Container content; + + private readonly Container background; + + public Box FlashBox; + + public CirclePiece() + { + EarlyActivationMilliseconds = pre_beat_transition_time; + + AddRangeInternal(new Drawable[] + { + background = new CircularContainer + { + Name = "Background", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + }, + new Triangles + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + ColourLight = Color4.White, + ColourDark = Color4.White.Darken(0.1f) + } + } + }, + new CircularContainer + { + Name = "Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + BorderThickness = 8, + BorderColour = Color4.White, + Masking = true, + Children = new[] + { + FlashBox = new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + Blending = BlendingMode.Additive, + Alpha = 0, + AlwaysPresent = true + } + } + }, + content = new Container + { + Name = "Content", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + } + }); + } + + private const float edge_alpha_kiai = 0.5f; + + private void resetEdgeEffects() + { + background.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = AccentColour.Opacity(KiaiMode ? edge_alpha_kiai : 1f), + Radius = KiaiMode ? 32 : 8 + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + if (!effectPoint.KiaiMode) + return; + + if (beatIndex % (int)timingPoint.TimeSignature != 0) + return; + + double duration = timingPoint.BeatLength * 2; + + background + .FadeEdgeEffectTo(1, pre_beat_transition_time, Easing.OutQuint) + .Then() + .FadeEdgeEffectTo(edge_alpha_kiai, duration, Easing.OutQuint); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs index 26d099c101..c5b8228439 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs @@ -1,30 +1,30 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - public class ElongatedCirclePiece : CirclePiece - { - public ElongatedCirclePiece() - { - RelativeSizeAxes = Axes.Y; - } - - protected override void Update() - { - base.Update(); - - var padding = Content.DrawHeight * Content.Width / 2; - - Content.Padding = new MarginPadding - { - Left = padding, - Right = padding, - }; - - Width = Parent.DrawSize.X + DrawHeight; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces +{ + public class ElongatedCirclePiece : CirclePiece + { + public ElongatedCirclePiece() + { + RelativeSizeAxes = Axes.Y; + } + + protected override void Update() + { + base.Update(); + + var padding = Content.DrawHeight * Content.Width / 2; + + Content.Padding = new MarginPadding + { + Left = padding, + Right = padding, + }; + + Width = Parent.DrawSize.X + DrawHeight; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs index 7614f1d6d8..d3487fb65c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitSymbolPiece.cs @@ -1,39 +1,39 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - /// - /// The symbol used for rim hit pieces. - /// - public class RimHitSymbolPiece : CircularContainer - { - public RimHitSymbolPiece() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - Size = new Vector2(CirclePiece.SYMBOL_SIZE); - - BorderThickness = CirclePiece.SYMBOL_BORDER; - BorderColour = Color4.White; - Masking = true; - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces +{ + /// + /// The symbol used for rim hit pieces. + /// + public class RimHitSymbolPiece : CircularContainer + { + public RimHitSymbolPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(CirclePiece.SYMBOL_SIZE); + + BorderThickness = CirclePiece.SYMBOL_BORDER; + BorderColour = Color4.White; + Masking = true; + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs index bccfaceb56..d70ac64a27 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs @@ -1,36 +1,36 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - /// - /// The symbol used for swell pieces. - /// - public class SwellSymbolPiece : Container - { - public SwellSymbolPiece() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - Size = new Vector2(CirclePiece.SYMBOL_SIZE); - Padding = new MarginPadding(CirclePiece.SYMBOL_BORDER); - - Children = new[] - { - new SpriteIcon - { - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.fa_asterisk, - Shadow = false - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces +{ + /// + /// The symbol used for swell pieces. + /// + public class SwellSymbolPiece : Container + { + public SwellSymbolPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(CirclePiece.SYMBOL_SIZE); + Padding = new MarginPadding(CirclePiece.SYMBOL_BORDER); + + Children = new[] + { + new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_asterisk, + Shadow = false + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs index fa2d1c78e3..e630847aec 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; -using OpenTK.Graphics; -using osu.Game.Graphics.Containers; -using osu.Framework.Graphics; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - public class TaikoPiece : BeatSyncedContainer, IHasAccentColour - { - private Color4 accentColour; - /// - /// The colour of the inner circle and outer glows. - /// - public virtual Color4 AccentColour - { - get { return accentColour; } - set { accentColour = value; } - } - - private bool kiaiMode; - /// - /// Whether Kiai mode effects are enabled for this circle piece. - /// - public virtual bool KiaiMode - { - get { return kiaiMode; } - set - { - kiaiMode = value; - } - } - - public TaikoPiece() - { - RelativeSizeAxes = Axes.Both; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using OpenTK.Graphics; +using osu.Game.Graphics.Containers; +using osu.Framework.Graphics; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces +{ + public class TaikoPiece : BeatSyncedContainer, IHasAccentColour + { + private Color4 accentColour; + /// + /// The colour of the inner circle and outer glows. + /// + public virtual Color4 AccentColour + { + get { return accentColour; } + set { accentColour = value; } + } + + private bool kiaiMode; + /// + /// Whether Kiai mode effects are enabled for this circle piece. + /// + public virtual bool KiaiMode + { + get { return kiaiMode; } + set + { + kiaiMode = value; + } + } + + public TaikoPiece() + { + RelativeSizeAxes = Axes.Both; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs index 0ff2742be6..976836a5ed 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs @@ -1,65 +1,65 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces -{ - public class TickPiece : TaikoPiece - { - /// - /// Any tick that is not the first for a drumroll is not filled, but is instead displayed - /// as a hollow circle. This is what controls the border width of that circle. - /// - private const float tick_border_width = 5; - - /// - /// The size of a tick. - /// - private const float tick_size = 0.35f; - - private bool filled; - public bool Filled - { - get { return filled; } - set - { - filled = value; - fillBox.Alpha = filled ? 1 : 0; - } - } - - private readonly Box fillBox; - - public TickPiece() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - FillMode = FillMode.Fit; - Size = new Vector2(tick_size); - - Add(new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = tick_border_width, - BorderColour = Color4.White, - Children = new[] - { - fillBox = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces +{ + public class TickPiece : TaikoPiece + { + /// + /// Any tick that is not the first for a drumroll is not filled, but is instead displayed + /// as a hollow circle. This is what controls the border width of that circle. + /// + private const float tick_border_width = 5; + + /// + /// The size of a tick. + /// + private const float tick_size = 0.35f; + + private bool filled; + public bool Filled + { + get { return filled; } + set + { + filled = value; + fillBox.Alpha = filled ? 1 : 0; + } + } + + private readonly Box fillBox; + + public TickPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + Size = new Vector2(tick_size); + + Add(new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = tick_border_width, + BorderColour = Color4.White, + Children = new[] + { + fillBox = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 181fee8393..64219c7b52 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -1,82 +1,82 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using System; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class DrumRoll : TaikoHitObject, IHasEndTime - { - /// - /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. - /// - private const float base_distance = 100; - - public double EndTime => StartTime + Duration; - - public double Duration { get; set; } - - /// - /// Numer of ticks per beat length. - /// - public int TickRate = 1; - - /// - /// Number of drum roll ticks required for a "Good" hit. - /// - public double RequiredGoodHits { get; protected set; } - - /// - /// Number of drum roll ticks required for a "Great" hit. - /// - public double RequiredGreatHits { get; protected set; } - - /// - /// The length (in milliseconds) between ticks of this drumroll. - /// Half of this value is the hit window of the ticks. - /// - private double tickSpacing = 100; - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - - tickSpacing = timingPoint.BeatLength / TickRate; - - RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty); - RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty); - } - - protected override void CreateNestedHitObjects() - { - base.CreateNestedHitObjects(); - - createTicks(); - } - - private void createTicks() - { - if (tickSpacing == 0) - return; - - bool first = true; - for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) - { - AddNested(new DrumRollTick - { - FirstTick = first, - TickSpacing = tickSpacing, - StartTime = t, - IsStrong = IsStrong - }); - - first = false; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using System; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class DrumRoll : TaikoHitObject, IHasEndTime + { + /// + /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. + /// + private const float base_distance = 100; + + public double EndTime => StartTime + Duration; + + public double Duration { get; set; } + + /// + /// Numer of ticks per beat length. + /// + public int TickRate = 1; + + /// + /// Number of drum roll ticks required for a "Good" hit. + /// + public double RequiredGoodHits { get; protected set; } + + /// + /// Number of drum roll ticks required for a "Great" hit. + /// + public double RequiredGreatHits { get; protected set; } + + /// + /// The length (in milliseconds) between ticks of this drumroll. + /// Half of this value is the hit window of the ticks. + /// + private double tickSpacing = 100; + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + + tickSpacing = timingPoint.BeatLength / TickRate; + + RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty); + RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty); + } + + protected override void CreateNestedHitObjects() + { + base.CreateNestedHitObjects(); + + createTicks(); + } + + private void createTicks() + { + if (tickSpacing == 0) + return; + + bool first = true; + for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) + { + AddNested(new DrumRollTick + { + FirstTick = first, + TickSpacing = tickSpacing, + StartTime = t, + IsStrong = IsStrong + }); + + first = false; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs index f72db4db6d..e546d6427f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class DrumRollTick : TaikoHitObject - { - /// - /// Whether this is the first (initial) tick of the slider. - /// - public bool FirstTick; - - /// - /// The length (in milliseconds) between this tick and the next. - /// Half of this value is the hit window of the tick. - /// - public double TickSpacing; - - /// - /// The time allowed to hit this tick. - /// - public double HitWindow => TickSpacing / 2; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class DrumRollTick : TaikoHitObject + { + /// + /// Whether this is the first (initial) tick of the slider. + /// + public bool FirstTick; + + /// + /// The length (in milliseconds) between this tick and the next. + /// Half of this value is the hit window of the tick. + /// + public double TickSpacing; + + /// + /// The time allowed to hit this tick. + /// + public double HitWindow => TickSpacing / 2; + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index c91a1f1714..0b47aa490b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class Hit : TaikoHitObject - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class Hit : TaikoHitObject + { + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/RimHit.cs b/osu.Game.Rulesets.Taiko/Objects/RimHit.cs index d161970c38..2e52e57f1f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/RimHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/RimHit.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class RimHit : Hit - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class RimHit : Hit + { + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index e17d11e3e7..eb6f931af4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class Swell : TaikoHitObject, IHasEndTime - { - public double EndTime => StartTime + Duration; - - public double Duration { get; set; } - - /// - /// The number of hits required to complete the swell successfully. - /// - public int RequiredHits = 10; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public class Swell : TaikoHitObject, IHasEndTime + { + public double EndTime => StartTime + Duration; + + public double Duration { get; set; } + + /// + /// The number of hits required to complete the swell successfully. + /// + public int RequiredHits = 10; + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 6e0d0e380a..63de096238 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public abstract class TaikoHitObject : HitObject - { - /// - /// Default size of a drawable taiko hit object. - /// - public const float DEFAULT_SIZE = 0.45f; - - /// - /// Scale multiplier for a strong drawable taiko hit object. - /// - public const float STRONG_SCALE = 1.4f; - - /// - /// Default size of a strong drawable taiko hit object. - /// - public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE; - - /// - /// Whether this HitObject is a "strong" type. - /// Strong hit objects give more points for hitting the hit object with both keys. - /// - public bool IsStrong; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public abstract class TaikoHitObject : HitObject + { + /// + /// Default size of a drawable taiko hit object. + /// + public const float DEFAULT_SIZE = 0.45f; + + /// + /// Scale multiplier for a strong drawable taiko hit object. + /// + public const float STRONG_SCALE = 1.4f; + + /// + /// Default size of a strong drawable taiko hit object. + /// + public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE; + + /// + /// Whether this HitObject is a "strong" type. + /// Strong hit objects give more points for hitting the hit object with both keys. + /// + public bool IsStrong; + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs index 54f08e9015..70a516af79 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs @@ -1,127 +1,127 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Rulesets.Taiko.Objects -{ - internal class TaikoHitObjectDifficulty - { - /// - /// Factor by how much individual / overall strain decays per second. - /// - /// - /// These values are results of tweaking a lot and taking into account general feedback. - /// - internal const double DECAY_BASE = 0.30; - - private const double type_change_bonus = 0.75; - private const double rhythm_change_bonus = 1.0; - private const double rhythm_change_base_threshold = 0.2; - private const double rhythm_change_base = 2.0; - - internal TaikoHitObject BaseHitObject; - - /// - /// Measures note density in a way - /// - internal double Strain = 1; - - private double timeElapsed; - private int sameTypeSince = 1; - - private bool isRim => BaseHitObject is RimHit; - - public TaikoHitObjectDifficulty(TaikoHitObject baseHitObject) - { - BaseHitObject = baseHitObject; - } - - internal void CalculateStrains(TaikoHitObjectDifficulty previousHitObject, double timeRate) - { - // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make. - // See Taiko feedback thread. - timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; - double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000); - - double addition = 1; - - // Only if we are no slider or spinner we get an extra addition - if (previousHitObject.BaseHitObject is Hit && BaseHitObject is Hit - && BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime < 1000) // And we only want to check out hitobjects which aren't so far in the past - { - addition += typeChangeAddition(previousHitObject); - addition += rhythmChangeAddition(previousHitObject); - } - - double additionFactor = 1.0; - // Scale AdditionFactor linearly from 0.4 to 1 for TimeElapsed from 0 to 50 - if (timeElapsed < 50.0) - additionFactor = 0.4 + 0.6 * timeElapsed / 50.0; - - Strain = previousHitObject.Strain * decay + addition * additionFactor; - } - - private TypeSwitch lastTypeSwitchEven = TypeSwitch.None; - private double typeChangeAddition(TaikoHitObjectDifficulty previousHitObject) - { - // If we don't have the same hit type, trigger a type change! - if (previousHitObject.isRim != isRim) - { - lastTypeSwitchEven = previousHitObject.sameTypeSince % 2 == 0 ? TypeSwitch.Even : TypeSwitch.Odd; - - // We only want a bonus if the parity of the type switch changes! - switch (previousHitObject.lastTypeSwitchEven) - { - case TypeSwitch.Even: - if (lastTypeSwitchEven == TypeSwitch.Odd) - return type_change_bonus; - break; - case TypeSwitch.Odd: - if (lastTypeSwitchEven == TypeSwitch.Even) - return type_change_bonus; - break; - } - } - // No type change? Increment counter and keep track of last type switch - else - { - lastTypeSwitchEven = previousHitObject.lastTypeSwitchEven; - sameTypeSince = previousHitObject.sameTypeSince + 1; - } - - return 0; - } - - private double rhythmChangeAddition(TaikoHitObjectDifficulty previousHitObject) - { - // We don't want a division by zero if some random mapper decides to put 2 HitObjects at the same time. - if (timeElapsed == 0 || previousHitObject.timeElapsed == 0) - return 0; - - double timeElapsedRatio = Math.Max(previousHitObject.timeElapsed / timeElapsed, timeElapsed / previousHitObject.timeElapsed); - - if (timeElapsedRatio >= 8) - return 0; - - double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0; - - if (isWithinChangeThreshold(difference)) - return rhythm_change_bonus; - - return 0; - } - - private bool isWithinChangeThreshold(double value) - { - return value > rhythm_change_base_threshold && value < 1 - rhythm_change_base_threshold; - } - - private enum TypeSwitch - { - None, - Even, - Odd - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Rulesets.Taiko.Objects +{ + internal class TaikoHitObjectDifficulty + { + /// + /// Factor by how much individual / overall strain decays per second. + /// + /// + /// These values are results of tweaking a lot and taking into account general feedback. + /// + internal const double DECAY_BASE = 0.30; + + private const double type_change_bonus = 0.75; + private const double rhythm_change_bonus = 1.0; + private const double rhythm_change_base_threshold = 0.2; + private const double rhythm_change_base = 2.0; + + internal TaikoHitObject BaseHitObject; + + /// + /// Measures note density in a way + /// + internal double Strain = 1; + + private double timeElapsed; + private int sameTypeSince = 1; + + private bool isRim => BaseHitObject is RimHit; + + public TaikoHitObjectDifficulty(TaikoHitObject baseHitObject) + { + BaseHitObject = baseHitObject; + } + + internal void CalculateStrains(TaikoHitObjectDifficulty previousHitObject, double timeRate) + { + // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make. + // See Taiko feedback thread. + timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; + double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000); + + double addition = 1; + + // Only if we are no slider or spinner we get an extra addition + if (previousHitObject.BaseHitObject is Hit && BaseHitObject is Hit + && BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime < 1000) // And we only want to check out hitobjects which aren't so far in the past + { + addition += typeChangeAddition(previousHitObject); + addition += rhythmChangeAddition(previousHitObject); + } + + double additionFactor = 1.0; + // Scale AdditionFactor linearly from 0.4 to 1 for TimeElapsed from 0 to 50 + if (timeElapsed < 50.0) + additionFactor = 0.4 + 0.6 * timeElapsed / 50.0; + + Strain = previousHitObject.Strain * decay + addition * additionFactor; + } + + private TypeSwitch lastTypeSwitchEven = TypeSwitch.None; + private double typeChangeAddition(TaikoHitObjectDifficulty previousHitObject) + { + // If we don't have the same hit type, trigger a type change! + if (previousHitObject.isRim != isRim) + { + lastTypeSwitchEven = previousHitObject.sameTypeSince % 2 == 0 ? TypeSwitch.Even : TypeSwitch.Odd; + + // We only want a bonus if the parity of the type switch changes! + switch (previousHitObject.lastTypeSwitchEven) + { + case TypeSwitch.Even: + if (lastTypeSwitchEven == TypeSwitch.Odd) + return type_change_bonus; + break; + case TypeSwitch.Odd: + if (lastTypeSwitchEven == TypeSwitch.Even) + return type_change_bonus; + break; + } + } + // No type change? Increment counter and keep track of last type switch + else + { + lastTypeSwitchEven = previousHitObject.lastTypeSwitchEven; + sameTypeSince = previousHitObject.sameTypeSince + 1; + } + + return 0; + } + + private double rhythmChangeAddition(TaikoHitObjectDifficulty previousHitObject) + { + // We don't want a division by zero if some random mapper decides to put 2 HitObjects at the same time. + if (timeElapsed == 0 || previousHitObject.timeElapsed == 0) + return 0; + + double timeElapsedRatio = Math.Max(previousHitObject.timeElapsed / timeElapsed, timeElapsed / previousHitObject.timeElapsed); + + if (timeElapsedRatio >= 8) + return 0; + + double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0; + + if (isWithinChangeThreshold(difference)) + return rhythm_change_bonus; + + return 0; + } + + private bool isWithinChangeThreshold(double value) + { + return value > rhythm_change_base_threshold && value < 1 - rhythm_change_base_threshold; + } + + private enum TypeSwitch + { + None, + Even, + Odd + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs index 77218af5e1..26f70bc927 100644 --- a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs +++ b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Runtime.CompilerServices; - -// We publish our internal attributes to other sub-projects of the framework. -// Note, that we omit visual tests as they are meant to test the framework -// behavior "in the wild". - -[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")] -[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Dynamic")] +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Runtime.CompilerServices; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")] +[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Dynamic")] diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 1a556fe01d..e7b2789010 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -1,130 +1,130 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Replays; -using osu.Game.Users; - -namespace osu.Game.Rulesets.Taiko.Replays -{ - public class TaikoAutoGenerator : AutoGenerator - { - private const double swell_hit_speed = 50; - - public TaikoAutoGenerator(Beatmap beatmap) - : base(beatmap) - { - Replay = new Replay - { - User = new User - { - Username = @"Autoplay", - } - }; - } - - protected Replay Replay; - protected List Frames => Replay.Frames; - - public override Replay Generate() - { - bool hitButton = true; - - Frames.Add(new TaikoReplayFrame(-100000)); - Frames.Add(new TaikoReplayFrame(Beatmap.HitObjects[0].StartTime - 1000)); - - for (int i = 0; i < Beatmap.HitObjects.Count; i++) - { - TaikoHitObject h = Beatmap.HitObjects[i]; - - IHasEndTime endTimeData = h as IHasEndTime; - double endTime = endTimeData?.EndTime ?? h.StartTime; - - Swell swell = h as Swell; - DrumRoll drumRoll = h as DrumRoll; - Hit hit = h as Hit; - - if (swell != null) - { - int d = 0; - int count = 0; - int req = swell.RequiredHits; - double hitRate = Math.Min(swell_hit_speed, swell.Duration / req); - for (double j = h.StartTime; j < endTime; j += hitRate) - { - TaikoAction action; - - switch (d) - { - default: - case 0: - action = TaikoAction.LeftCentre; - break; - case 1: - action = TaikoAction.LeftRim; - break; - case 2: - action = TaikoAction.RightCentre; - break; - case 3: - action = TaikoAction.RightRim; - break; - } - - Frames.Add(new TaikoReplayFrame(j, action)); - d = (d + 1) % 4; - if (++count == req) - break; - } - } - else if (drumRoll != null) - { - foreach (var tick in drumRoll.NestedHitObjects.OfType()) - { - Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre)); - hitButton = !hitButton; - } - } - else if (hit != null) - { - TaikoAction[] actions; - - if (hit is CentreHit) - { - actions = h.IsStrong - ? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre } - : new[] { hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre }; - } - else - { - actions = h.IsStrong - ? new[] { TaikoAction.LeftRim, TaikoAction.RightRim } - : new[] { hitButton ? TaikoAction.LeftRim : TaikoAction.RightRim }; - } - - Frames.Add(new TaikoReplayFrame(h.StartTime, actions)); - } - else - throw new InvalidOperationException("Unknown hit object type."); - - Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); - - if (i < Beatmap.HitObjects.Count - 1) - { - double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000; - if (waitTime > endTime) - Frames.Add(new TaikoReplayFrame(waitTime)); - } - - hitButton = !hitButton; - } - - return Replay; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Replays; +using osu.Game.Users; + +namespace osu.Game.Rulesets.Taiko.Replays +{ + public class TaikoAutoGenerator : AutoGenerator + { + private const double swell_hit_speed = 50; + + public TaikoAutoGenerator(Beatmap beatmap) + : base(beatmap) + { + Replay = new Replay + { + User = new User + { + Username = @"Autoplay", + } + }; + } + + protected Replay Replay; + protected List Frames => Replay.Frames; + + public override Replay Generate() + { + bool hitButton = true; + + Frames.Add(new TaikoReplayFrame(-100000)); + Frames.Add(new TaikoReplayFrame(Beatmap.HitObjects[0].StartTime - 1000)); + + for (int i = 0; i < Beatmap.HitObjects.Count; i++) + { + TaikoHitObject h = Beatmap.HitObjects[i]; + + IHasEndTime endTimeData = h as IHasEndTime; + double endTime = endTimeData?.EndTime ?? h.StartTime; + + Swell swell = h as Swell; + DrumRoll drumRoll = h as DrumRoll; + Hit hit = h as Hit; + + if (swell != null) + { + int d = 0; + int count = 0; + int req = swell.RequiredHits; + double hitRate = Math.Min(swell_hit_speed, swell.Duration / req); + for (double j = h.StartTime; j < endTime; j += hitRate) + { + TaikoAction action; + + switch (d) + { + default: + case 0: + action = TaikoAction.LeftCentre; + break; + case 1: + action = TaikoAction.LeftRim; + break; + case 2: + action = TaikoAction.RightCentre; + break; + case 3: + action = TaikoAction.RightRim; + break; + } + + Frames.Add(new TaikoReplayFrame(j, action)); + d = (d + 1) % 4; + if (++count == req) + break; + } + } + else if (drumRoll != null) + { + foreach (var tick in drumRoll.NestedHitObjects.OfType()) + { + Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre)); + hitButton = !hitButton; + } + } + else if (hit != null) + { + TaikoAction[] actions; + + if (hit is CentreHit) + { + actions = h.IsStrong + ? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre } + : new[] { hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre }; + } + else + { + actions = h.IsStrong + ? new[] { TaikoAction.LeftRim, TaikoAction.RightRim } + : new[] { hitButton ? TaikoAction.LeftRim : TaikoAction.RightRim }; + } + + Frames.Add(new TaikoReplayFrame(h.StartTime, actions)); + } + else + throw new InvalidOperationException("Unknown hit object type."); + + Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); + + if (i < Beatmap.HitObjects.Count - 1) + { + double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000; + if (waitTime > endTime) + Frames.Add(new TaikoReplayFrame(waitTime)); + } + + hitButton = !hitButton; + } + + return Replay; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs index c80bddc304..6ccbd575e5 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Replays; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Input; - -namespace osu.Game.Rulesets.Taiko.Replays -{ - internal class TaikoFramedReplayInputHandler : FramedReplayInputHandler - { - public TaikoFramedReplayInputHandler(Replay replay) - : base(replay) - { - } - - protected override bool IsImportant(TaikoReplayFrame frame) => frame.Actions.Any(); - - public override List GetPendingStates() => new List { new ReplayState { PressedActions = CurrentFrame.Actions } }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Replays; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Input; + +namespace osu.Game.Rulesets.Taiko.Replays +{ + internal class TaikoFramedReplayInputHandler : FramedReplayInputHandler + { + public TaikoFramedReplayInputHandler(Replay replay) + : base(replay) + { + } + + protected override bool IsImportant(TaikoReplayFrame frame) => frame.Actions.Any(); + + public override List GetPendingStates() => new List { new ReplayState { PressedActions = CurrentFrame.Actions } }; + } +} diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs index 6cd63f6c70..e510b34ad7 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Replays.Legacy; -using osu.Game.Rulesets.Replays.Types; - -namespace osu.Game.Rulesets.Taiko.Replays -{ - public class TaikoReplayFrame : ReplayFrame, IConvertibleReplayFrame - { - public List Actions = new List(); - - public TaikoReplayFrame() - { - } - - public TaikoReplayFrame(double time, params TaikoAction[] actions) - : base(time) - { - Actions.AddRange(actions); - } - - public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) - { - if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); - if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); - if (legacyFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre); - if (legacyFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; + +namespace osu.Game.Rulesets.Taiko.Replays +{ + public class TaikoReplayFrame : ReplayFrame, IConvertibleReplayFrame + { + public List Actions = new List(); + + public TaikoReplayFrame() + { + } + + public TaikoReplayFrame(double time, params TaikoAction[] actions) + : base(time) + { + Actions.AddRange(actions); + } + + public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) + { + if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); + if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); + if (legacyFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre); + if (legacyFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index b91f37d84d..c7f75e44fa 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -1,144 +1,144 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Judgements; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Taiko.Scoring -{ - internal class TaikoScoreProcessor : ScoreProcessor - { - /// - /// The HP awarded by a hit. - /// - private const double hp_hit_great = 0.03; - - /// - /// The HP awarded for a hit. - /// - private const double hp_hit_good = 0.011; - - /// - /// The minimum HP deducted for a . - /// This occurs when HP Drain = 0. - /// - private const double hp_miss_min = -0.0018; - - /// - /// The median HP deducted for a . - /// This occurs when HP Drain = 5. - /// - private const double hp_miss_mid = -0.0075; - - /// - /// The maximum HP deducted for a . - /// This occurs when HP Drain = 10. - /// - private const double hp_miss_max = -0.12; - - /// - /// The HP awarded for a hit. - /// - /// hits award less HP as they're more spammable, although in hindsight - /// this probably awards too little HP and is kept at this value for now for compatibility. - /// - /// - private const double hp_hit_tick = 0.00000003; - - /// - /// Taiko fails at the end of the map if the player has not half-filled their HP bar. - /// - protected override bool DefaultFailCondition => JudgedHits == MaxHits && Health.Value <= 0.5; - - private double hpIncreaseTick; - private double hpIncreaseGreat; - private double hpIncreaseGood; - private double hpIncreaseMiss; - - public TaikoScoreProcessor() - { - } - - public TaikoScoreProcessor(RulesetContainer rulesetContainer) - : base(rulesetContainer) - { - } - - protected override void SimulateAutoplay(Beatmap beatmap) - { - double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); - - hpIncreaseTick = hp_hit_tick; - hpIncreaseGreat = hpMultiplierNormal * hp_hit_great; - hpIncreaseGood = hpMultiplierNormal * hp_hit_good; - hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max); - - foreach (var obj in beatmap.HitObjects) - { - if (obj is Hit) - { - AddJudgement(new TaikoJudgement { Result = HitResult.Great }); - if (obj.IsStrong) - AddJudgement(new TaikoStrongHitJudgement()); - } - else if (obj is DrumRoll) - { - for (int i = 0; i < ((DrumRoll)obj).NestedHitObjects.OfType().Count(); i++) - { - AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great }); - - if (obj.IsStrong) - AddJudgement(new TaikoStrongHitJudgement()); - } - - AddJudgement(new TaikoJudgement { Result = HitResult.Great }); - - if (obj.IsStrong) - AddJudgement(new TaikoStrongHitJudgement()); - } - else if (obj is Swell) - { - AddJudgement(new TaikoJudgement { Result = HitResult.Great }); - } - } - } - - protected override void OnNewJudgement(Judgement judgement) - { - base.OnNewJudgement(judgement); - - bool isTick = judgement is TaikoDrumRollTickJudgement; - - // Apply HP changes - switch (judgement.Result) - { - case HitResult.Miss: - // Missing ticks shouldn't drop HP - if (!isTick) - Health.Value += hpIncreaseMiss; - break; - case HitResult.Good: - Health.Value += hpIncreaseGood; - break; - case HitResult.Great: - if (isTick) - Health.Value += hpIncreaseTick; - else - Health.Value += hpIncreaseGreat; - break; - } - } - - protected override void Reset(bool storeResults) - { - base.Reset(storeResults); - - Health.Value = 0; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Taiko.Scoring +{ + internal class TaikoScoreProcessor : ScoreProcessor + { + /// + /// The HP awarded by a hit. + /// + private const double hp_hit_great = 0.03; + + /// + /// The HP awarded for a hit. + /// + private const double hp_hit_good = 0.011; + + /// + /// The minimum HP deducted for a . + /// This occurs when HP Drain = 0. + /// + private const double hp_miss_min = -0.0018; + + /// + /// The median HP deducted for a . + /// This occurs when HP Drain = 5. + /// + private const double hp_miss_mid = -0.0075; + + /// + /// The maximum HP deducted for a . + /// This occurs when HP Drain = 10. + /// + private const double hp_miss_max = -0.12; + + /// + /// The HP awarded for a hit. + /// + /// hits award less HP as they're more spammable, although in hindsight + /// this probably awards too little HP and is kept at this value for now for compatibility. + /// + /// + private const double hp_hit_tick = 0.00000003; + + /// + /// Taiko fails at the end of the map if the player has not half-filled their HP bar. + /// + protected override bool DefaultFailCondition => JudgedHits == MaxHits && Health.Value <= 0.5; + + private double hpIncreaseTick; + private double hpIncreaseGreat; + private double hpIncreaseGood; + private double hpIncreaseMiss; + + public TaikoScoreProcessor() + { + } + + public TaikoScoreProcessor(RulesetContainer rulesetContainer) + : base(rulesetContainer) + { + } + + protected override void SimulateAutoplay(Beatmap beatmap) + { + double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); + + hpIncreaseTick = hp_hit_tick; + hpIncreaseGreat = hpMultiplierNormal * hp_hit_great; + hpIncreaseGood = hpMultiplierNormal * hp_hit_good; + hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max); + + foreach (var obj in beatmap.HitObjects) + { + if (obj is Hit) + { + AddJudgement(new TaikoJudgement { Result = HitResult.Great }); + if (obj.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); + } + else if (obj is DrumRoll) + { + for (int i = 0; i < ((DrumRoll)obj).NestedHitObjects.OfType().Count(); i++) + { + AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great }); + + if (obj.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); + } + + AddJudgement(new TaikoJudgement { Result = HitResult.Great }); + + if (obj.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); + } + else if (obj is Swell) + { + AddJudgement(new TaikoJudgement { Result = HitResult.Great }); + } + } + } + + protected override void OnNewJudgement(Judgement judgement) + { + base.OnNewJudgement(judgement); + + bool isTick = judgement is TaikoDrumRollTickJudgement; + + // Apply HP changes + switch (judgement.Result) + { + case HitResult.Miss: + // Missing ticks shouldn't drop HP + if (!isTick) + Health.Value += hpIncreaseMiss; + break; + case HitResult.Good: + Health.Value += hpIncreaseGood; + break; + case HitResult.Great: + if (isTick) + Health.Value += hpIncreaseTick; + else + Health.Value += hpIncreaseGreat; + break; + } + } + + protected override void Reset(bool storeResults) + { + base.Reset(storeResults); + + Health.Value = 0; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs index 8b4e77573f..58661d7881 100644 --- a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs @@ -1,138 +1,138 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Taiko.Beatmaps; -using osu.Game.Rulesets.Taiko.Objects; -using System.Collections.Generic; -using System; - -namespace osu.Game.Rulesets.Taiko -{ - internal class TaikoDifficultyCalculator : DifficultyCalculator - { - private const double star_scaling_factor = 0.04125; - - /// - /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. - /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. - /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. - /// - private const double strain_step = 400; - - /// - /// The weighting of each strain value decays to this number * it's previous value - /// - private const double decay_weight = 0.9; - - /// - /// HitObjects are stored as a member variable. - /// - private readonly List difficultyHitObjects = new List(); - - public TaikoDifficultyCalculator(Beatmap beatmap) - : base(beatmap) - { - } - - public override double Calculate(Dictionary categoryDifficulty = null) - { - // Fill our custom DifficultyHitObject class, that carries additional information - difficultyHitObjects.Clear(); - - foreach (var hitObject in Beatmap.HitObjects) - difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject)); - - // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. - difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); - - if (!calculateStrainValues()) return 0; - - double starRating = calculateDifficulty() * star_scaling_factor; - - if (categoryDifficulty != null) - { - categoryDifficulty.Add("Strain", starRating); - categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate); - } - - return starRating; - } - - private bool calculateStrainValues() - { - // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - using (List.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) - { - if (!hitObjectsEnumerator.MoveNext()) return false; - - TaikoHitObjectDifficulty current = hitObjectsEnumerator.Current; - - // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. - while (hitObjectsEnumerator.MoveNext()) - { - var next = hitObjectsEnumerator.Current; - next?.CalculateStrains(current, TimeRate); - current = next; - } - - return true; - } - } - - private double calculateDifficulty() - { - double actualStrainStep = strain_step * TimeRate; - - // Find the highest strain value within each strain step - List highestStrains = new List(); - double intervalEndTime = actualStrainStep; - double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval - - TaikoHitObjectDifficulty previousHitObject = null; - foreach (var hitObject in difficultyHitObjects) - { - // While we are beyond the current interval push the currently available maximum to our strain list - while (hitObject.BaseHitObject.StartTime > intervalEndTime) - { - highestStrains.Add(maximumStrain); - - // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay - // until the beginning of the next interval. - if (previousHitObject == null) - { - maximumStrain = 0; - } - else - { - double decay = Math.Pow(TaikoHitObjectDifficulty.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); - maximumStrain = previousHitObject.Strain * decay; - } - - // Go to the next time interval - intervalEndTime += actualStrainStep; - } - - // Obtain maximum strain - maximumStrain = Math.Max(hitObject.Strain, maximumStrain); - - previousHitObject = hitObject; - } - - // Build the weighted sum over the highest strains for each interval - double difficulty = 0; - double weight = 1; - highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - foreach (double strain in highestStrains) - { - difficulty += weight * strain; - weight *= decay_weight; - } - - return difficulty; - } - - protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new TaikoBeatmapConverter(true); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Taiko.Objects; +using System.Collections.Generic; +using System; + +namespace osu.Game.Rulesets.Taiko +{ + internal class TaikoDifficultyCalculator : DifficultyCalculator + { + private const double star_scaling_factor = 0.04125; + + /// + /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. + /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. + /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. + /// + private const double strain_step = 400; + + /// + /// The weighting of each strain value decays to this number * it's previous value + /// + private const double decay_weight = 0.9; + + /// + /// HitObjects are stored as a member variable. + /// + private readonly List difficultyHitObjects = new List(); + + public TaikoDifficultyCalculator(Beatmap beatmap) + : base(beatmap) + { + } + + public override double Calculate(Dictionary categoryDifficulty = null) + { + // Fill our custom DifficultyHitObject class, that carries additional information + difficultyHitObjects.Clear(); + + foreach (var hitObject in Beatmap.HitObjects) + difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject)); + + // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. + difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); + + if (!calculateStrainValues()) return 0; + + double starRating = calculateDifficulty() * star_scaling_factor; + + if (categoryDifficulty != null) + { + categoryDifficulty.Add("Strain", starRating); + categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate); + } + + return starRating; + } + + private bool calculateStrainValues() + { + // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. + using (List.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) + { + if (!hitObjectsEnumerator.MoveNext()) return false; + + TaikoHitObjectDifficulty current = hitObjectsEnumerator.Current; + + // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. + while (hitObjectsEnumerator.MoveNext()) + { + var next = hitObjectsEnumerator.Current; + next?.CalculateStrains(current, TimeRate); + current = next; + } + + return true; + } + } + + private double calculateDifficulty() + { + double actualStrainStep = strain_step * TimeRate; + + // Find the highest strain value within each strain step + List highestStrains = new List(); + double intervalEndTime = actualStrainStep; + double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval + + TaikoHitObjectDifficulty previousHitObject = null; + foreach (var hitObject in difficultyHitObjects) + { + // While we are beyond the current interval push the currently available maximum to our strain list + while (hitObject.BaseHitObject.StartTime > intervalEndTime) + { + highestStrains.Add(maximumStrain); + + // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay + // until the beginning of the next interval. + if (previousHitObject == null) + { + maximumStrain = 0; + } + else + { + double decay = Math.Pow(TaikoHitObjectDifficulty.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); + maximumStrain = previousHitObject.Strain * decay; + } + + // Go to the next time interval + intervalEndTime += actualStrainStep; + } + + // Obtain maximum strain + maximumStrain = Math.Max(hitObject.Strain, maximumStrain); + + previousHitObject = hitObject; + } + + // Build the weighted sum over the highest strains for each interval + double difficulty = 0; + double weight = 1; + highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + foreach (double strain in highestStrains) + { + difficulty += weight * strain; + weight *= decay_weight; + } + + return difficulty; + } + + protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new TaikoBeatmapConverter(true); + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs index f668a09f21..dc683ae2f5 100644 --- a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs +++ b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Taiko -{ - public class TaikoInputManager : RulesetInputManager - { - public TaikoInputManager(RulesetInfo ruleset) - : base(ruleset, 0, SimultaneousBindingMode.Unique) - { - } - } - - public enum TaikoAction - { - [Description("Left (Rim)")] - LeftRim, - [Description("Left (Centre)")] - LeftCentre, - [Description("Right (Centre)")] - RightCentre, - [Description("Right (Rim)")] - RightRim - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Taiko +{ + public class TaikoInputManager : RulesetInputManager + { + public TaikoInputManager(RulesetInfo ruleset) + : base(ruleset, 0, SimultaneousBindingMode.Unique) + { + } + } + + public enum TaikoAction + { + [Description("Left (Rim)")] + LeftRim, + [Description("Left (Centre)")] + LeftCentre, + [Description("Right (Centre)")] + RightCentre, + [Description("Right (Rim)")] + RightRim + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 0a9719f27b..cb5e020601 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -1,115 +1,115 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Mods; -using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Rulesets.UI; -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Taiko.Replays; - -namespace osu.Game.Rulesets.Taiko -{ - public class TaikoRuleset : Ruleset - { - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoRulesetContainer(this, beatmap, isForCurrentRuleset); - - public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] - { - new KeyBinding(InputKey.D, TaikoAction.LeftRim), - new KeyBinding(InputKey.F, TaikoAction.LeftCentre), - new KeyBinding(InputKey.J, TaikoAction.RightCentre), - new KeyBinding(InputKey.K, TaikoAction.RightRim), - new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), - new KeyBinding(InputKey.MouseLeft, TaikoAction.RightCentre), - new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim), - new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim), - }; - - public override IEnumerable GetModsFor(ModType type) - { - switch (type) - { - case ModType.DifficultyReduction: - return new Mod[] - { - new TaikoModEasy(), - new TaikoModNoFail(), - new MultiMod - { - Mods = new Mod[] - { - new TaikoModHalfTime(), - new TaikoModDaycore(), - }, - }, - }; - - case ModType.DifficultyIncrease: - return new Mod[] - { - new TaikoModHardRock(), - new MultiMod - { - Mods = new Mod[] - { - new TaikoModSuddenDeath(), - new TaikoModPerfect(), - }, - }, - new MultiMod - { - Mods = new Mod[] - { - new TaikoModDoubleTime(), - new TaikoModNightcore(), - }, - }, - new TaikoModHidden(), - new TaikoModFlashlight(), - }; - - case ModType.Special: - return new Mod[] - { - new TaikoModRelax(), - null, - null, - new MultiMod - { - Mods = new Mod[] - { - new TaikoModAutoplay(), - new ModCinema(), - }, - }, - }; - - default: - return new Mod[] { }; - } - } - - public override string Description => "osu!taiko"; - - public override string ShortName => "taiko"; - - public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; - - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap); - - public override int? LegacyID => 1; - - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); - - public TaikoRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Taiko.Replays; + +namespace osu.Game.Rulesets.Taiko +{ + public class TaikoRuleset : Ruleset + { + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoRulesetContainer(this, beatmap, isForCurrentRuleset); + + public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] + { + new KeyBinding(InputKey.D, TaikoAction.LeftRim), + new KeyBinding(InputKey.F, TaikoAction.LeftCentre), + new KeyBinding(InputKey.J, TaikoAction.RightCentre), + new KeyBinding(InputKey.K, TaikoAction.RightRim), + new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), + new KeyBinding(InputKey.MouseLeft, TaikoAction.RightCentre), + new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim), + new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim), + }; + + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new TaikoModEasy(), + new TaikoModNoFail(), + new MultiMod + { + Mods = new Mod[] + { + new TaikoModHalfTime(), + new TaikoModDaycore(), + }, + }, + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new TaikoModHardRock(), + new MultiMod + { + Mods = new Mod[] + { + new TaikoModSuddenDeath(), + new TaikoModPerfect(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new TaikoModDoubleTime(), + new TaikoModNightcore(), + }, + }, + new TaikoModHidden(), + new TaikoModFlashlight(), + }; + + case ModType.Special: + return new Mod[] + { + new TaikoModRelax(), + null, + null, + new MultiMod + { + Mods = new Mod[] + { + new TaikoModAutoplay(), + new ModCinema(), + }, + }, + }; + + default: + return new Mod[] { }; + } + } + + public override string Description => "osu!taiko"; + + public override string ShortName => "taiko"; + + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; + + public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap); + + public override int? LegacyID => 1; + + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); + + public TaikoRuleset(RulesetInfo rulesetInfo = null) + : base(rulesetInfo) + { + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index 6274232ffd..b07a3ce8df 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -1,50 +1,50 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Drawables; -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Judgements; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Taiko.UI -{ - /// - /// Text that is shown as judgement when a hit object is hit or missed. - /// - public class DrawableTaikoJudgement : DrawableJudgement - { - /// - /// Creates a new judgement text. - /// - /// The object which is being judged. - /// The judgement to visualise. - public DrawableTaikoJudgement(Judgement judgement, DrawableHitObject judgedObject) - : base(judgement, judgedObject) - { - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - switch (Judgement.Result) - { - case HitResult.Good: - Colour = colours.GreenLight; - break; - case HitResult.Great: - Colour = colours.BlueLight; - break; - } - } - - protected override void LoadComplete() - { - if (Judgement.IsHit) - this.MoveToY(-100, 500); - - base.LoadComplete(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Drawables; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Judgements; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.UI +{ + /// + /// Text that is shown as judgement when a hit object is hit or missed. + /// + public class DrawableTaikoJudgement : DrawableJudgement + { + /// + /// Creates a new judgement text. + /// + /// The object which is being judged. + /// The judgement to visualise. + public DrawableTaikoJudgement(Judgement judgement, DrawableHitObject judgedObject) + : base(judgement, judgedObject) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + switch (Judgement.Result) + { + case HitResult.Good: + Colour = colours.GreenLight; + break; + case HitResult.Great: + Colour = colours.BlueLight; + break; + } + } + + protected override void LoadComplete() + { + if (Judgement.IsHit) + this.MoveToY(-100, 500); + + base.LoadComplete(); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 1a9915f78e..ee2c1d5ad5 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -1,80 +1,80 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects; - -namespace osu.Game.Rulesets.Taiko.UI -{ - /// - /// A circle explodes from the hit target to indicate a hitobject has been hit. - /// - internal class HitExplosion : CircularContainer - { - public readonly DrawableHitObject JudgedObject; - - private readonly Box innerFill; - - private readonly bool isRim; - - public HitExplosion(DrawableHitObject judgedObject, bool isRim) - { - this.isRim = isRim; - - JudgedObject = judgedObject; - - Anchor = Anchor.CentreLeft; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - Size = new Vector2(TaikoHitObject.DEFAULT_SIZE); - - RelativePositionAxes = Axes.Both; - - BorderColour = Color4.White; - BorderThickness = 1; - - Alpha = 0.15f; - Masking = true; - - Children = new[] - { - innerFill = new Box - { - RelativeSizeAxes = Axes.Both, - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - innerFill.Colour = isRim ? colours.BlueDarker : colours.PinkDarker; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - this.ScaleTo(3f, 1000, Easing.OutQuint); - this.FadeOut(500); - - Expire(); - } - - /// - /// Transforms this hit explosion to visualise a secondary hit. - /// - public void VisualiseSecondHit() - { - this.ResizeTo(new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), 50); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.UI +{ + /// + /// A circle explodes from the hit target to indicate a hitobject has been hit. + /// + internal class HitExplosion : CircularContainer + { + public readonly DrawableHitObject JudgedObject; + + private readonly Box innerFill; + + private readonly bool isRim; + + public HitExplosion(DrawableHitObject judgedObject, bool isRim) + { + this.isRim = isRim; + + JudgedObject = judgedObject; + + Anchor = Anchor.CentreLeft; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE); + + RelativePositionAxes = Axes.Both; + + BorderColour = Color4.White; + BorderThickness = 1; + + Alpha = 0.15f; + Masking = true; + + Children = new[] + { + innerFill = new Box + { + RelativeSizeAxes = Axes.Both, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + innerFill.Colour = isRim ? colours.BlueDarker : colours.PinkDarker; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.ScaleTo(3f, 1000, Easing.OutQuint); + this.FadeOut(500); + + Expire(); + } + + /// + /// Transforms this hit explosion to visualise a secondary hit. + /// + public void VisualiseSecondHit() + { + this.ResizeTo(new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), 50); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs index fa2b6e737f..9d824ca5b7 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs @@ -1,92 +1,92 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Taiko.Objects; - -namespace osu.Game.Rulesets.Taiko.UI -{ - /// - /// A component that is displayed at the hit position in the taiko playfield. - /// - internal class HitTarget : Container - { - /// - /// Thickness of all drawn line pieces. - /// - private const float border_thickness = 2.5f; - - public HitTarget() - { - Children = new Drawable[] - { - new Box - { - Name = "Bar Upper", - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Y, - Size = new Vector2(border_thickness, (1 - TaikoHitObject.DEFAULT_STRONG_SIZE) / 2f), - Alpha = 0.1f - }, - new CircularContainer - { - Name = "Strong Hit Ring", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Scale = new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), - Masking = true, - BorderColour = Color4.White, - BorderThickness = border_thickness, - Alpha = 0.1f, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - }, - new CircularContainer - { - Name = "Normal Hit Ring", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Scale = new Vector2(TaikoHitObject.DEFAULT_SIZE), - Masking = true, - BorderColour = Color4.White, - BorderThickness = border_thickness, - Alpha = 0.5f, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - }, - new Box - { - Name = "Bar Lower", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.Y, - Size = new Vector2(border_thickness, (1 - TaikoHitObject.DEFAULT_STRONG_SIZE) / 2f), - Alpha = 0.1f - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.UI +{ + /// + /// A component that is displayed at the hit position in the taiko playfield. + /// + internal class HitTarget : Container + { + /// + /// Thickness of all drawn line pieces. + /// + private const float border_thickness = 2.5f; + + public HitTarget() + { + Children = new Drawable[] + { + new Box + { + Name = "Bar Upper", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + Size = new Vector2(border_thickness, (1 - TaikoHitObject.DEFAULT_STRONG_SIZE) / 2f), + Alpha = 0.1f + }, + new CircularContainer + { + Name = "Strong Hit Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), + Masking = true, + BorderColour = Color4.White, + BorderThickness = border_thickness, + Alpha = 0.1f, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + new CircularContainer + { + Name = "Normal Hit Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(TaikoHitObject.DEFAULT_SIZE), + Masking = true, + BorderColour = Color4.White, + BorderThickness = border_thickness, + Alpha = 0.5f, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + new Box + { + Name = "Bar Lower", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Y, + Size = new Vector2(border_thickness, (1 - TaikoHitObject.DEFAULT_STRONG_SIZE) / 2f), + Alpha = 0.1f + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index b918f495fc..524535bfde 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -1,193 +1,193 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Audio; - -namespace osu.Game.Rulesets.Taiko.UI -{ - /// - /// A component of the playfield that captures input and displays input as a drum. - /// - internal class InputDrum : Container - { - private const float middle_split = 0.025f; - - private readonly ControlPointInfo controlPoints; - - public InputDrum(ControlPointInfo controlPoints) - { - this.controlPoints = controlPoints; - - RelativeSizeAxes = Axes.Both; - FillMode = FillMode.Fit; - } - - [BackgroundDependencyLoader] - private void load() - { - var sampleMappings = new DrumSampleMapping(controlPoints); - - Children = new Drawable[] - { - new TaikoHalfDrum(false, sampleMappings) - { - Name = "Left Half", - Anchor = Anchor.Centre, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.X, - X = -middle_split / 2, - RimAction = TaikoAction.LeftRim, - CentreAction = TaikoAction.LeftCentre - }, - new TaikoHalfDrum(true, sampleMappings) - { - Name = "Right Half", - Anchor = Anchor.Centre, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.X, - X = middle_split / 2, - RimAction = TaikoAction.RightRim, - CentreAction = TaikoAction.RightCentre - } - }; - - AddRangeInternal(sampleMappings.Sounds); - } - - /// - /// A half-drum. Contains one centre and one rim hit. - /// - private class TaikoHalfDrum : Container, IKeyBindingHandler - { - /// - /// The key to be used for the rim of the half-drum. - /// - public TaikoAction RimAction; - - /// - /// The key to be used for the centre of the half-drum. - /// - public TaikoAction CentreAction; - - private readonly Sprite rim; - private readonly Sprite rimHit; - private readonly Sprite centre; - private readonly Sprite centreHit; - - private readonly DrumSampleMapping sampleMappings; - - public TaikoHalfDrum(bool flipped, DrumSampleMapping sampleMappings) - { - this.sampleMappings = sampleMappings; - - Masking = true; - - Children = new Drawable[] - { - rim = new Sprite - { - Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both - }, - rimHit = new Sprite - { - Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Blending = BlendingMode.Additive, - }, - centre = new Sprite - { - Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.7f) - }, - centreHit = new Sprite - { - Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.7f), - Alpha = 0, - Blending = BlendingMode.Additive - } - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures, OsuColour colours) - { - rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer"); - rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit"); - centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner"); - centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit"); - - rimHit.Colour = colours.Blue; - centreHit.Colour = colours.Pink; - } - - public bool OnPressed(TaikoAction action) - { - Drawable target = null; - Drawable back = null; - - var drumSample = sampleMappings.SampleAt(Time.Current); - - if (action == CentreAction) - { - target = centreHit; - back = centre; - - drumSample.Centre?.Play(); - } - else if (action == RimAction) - { - target = rimHit; - back = rim; - - drumSample.Rim?.Play(); - } - - if (target != null) - { - const float scale_amount = 0.05f; - const float alpha_amount = 0.5f; - - const float down_time = 40; - const float up_time = 1000; - - back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint) - .Then() - .ScaleTo(1, up_time, Easing.OutQuint); - - target.Animate( - t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint), - t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint) - ).Then( - t => t.ScaleTo(1, up_time, Easing.OutQuint), - t => t.FadeOut(up_time, Easing.OutQuint) - ); - } - - return false; - } - - public bool OnReleased(TaikoAction action) => false; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Bindings; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics; +using osu.Game.Rulesets.Taiko.Audio; + +namespace osu.Game.Rulesets.Taiko.UI +{ + /// + /// A component of the playfield that captures input and displays input as a drum. + /// + internal class InputDrum : Container + { + private const float middle_split = 0.025f; + + private readonly ControlPointInfo controlPoints; + + public InputDrum(ControlPointInfo controlPoints) + { + this.controlPoints = controlPoints; + + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + } + + [BackgroundDependencyLoader] + private void load() + { + var sampleMappings = new DrumSampleMapping(controlPoints); + + Children = new Drawable[] + { + new TaikoHalfDrum(false, sampleMappings) + { + Name = "Left Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.X, + X = -middle_split / 2, + RimAction = TaikoAction.LeftRim, + CentreAction = TaikoAction.LeftCentre + }, + new TaikoHalfDrum(true, sampleMappings) + { + Name = "Right Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.X, + X = middle_split / 2, + RimAction = TaikoAction.RightRim, + CentreAction = TaikoAction.RightCentre + } + }; + + AddRangeInternal(sampleMappings.Sounds); + } + + /// + /// A half-drum. Contains one centre and one rim hit. + /// + private class TaikoHalfDrum : Container, IKeyBindingHandler + { + /// + /// The key to be used for the rim of the half-drum. + /// + public TaikoAction RimAction; + + /// + /// The key to be used for the centre of the half-drum. + /// + public TaikoAction CentreAction; + + private readonly Sprite rim; + private readonly Sprite rimHit; + private readonly Sprite centre; + private readonly Sprite centreHit; + + private readonly DrumSampleMapping sampleMappings; + + public TaikoHalfDrum(bool flipped, DrumSampleMapping sampleMappings) + { + this.sampleMappings = sampleMappings; + + Masking = true; + + Children = new Drawable[] + { + rim = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both + }, + rimHit = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Blending = BlendingMode.Additive, + }, + centre = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f) + }, + centreHit = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f), + Alpha = 0, + Blending = BlendingMode.Additive + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures, OsuColour colours) + { + rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer"); + rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit"); + centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner"); + centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit"); + + rimHit.Colour = colours.Blue; + centreHit.Colour = colours.Pink; + } + + public bool OnPressed(TaikoAction action) + { + Drawable target = null; + Drawable back = null; + + var drumSample = sampleMappings.SampleAt(Time.Current); + + if (action == CentreAction) + { + target = centreHit; + back = centre; + + drumSample.Centre?.Play(); + } + else if (action == RimAction) + { + target = rimHit; + back = rim; + + drumSample.Rim?.Play(); + } + + if (target != null) + { + const float scale_amount = 0.05f; + const float alpha_amount = 0.5f; + + const float down_time = 40; + const float up_time = 1000; + + back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint) + .Then() + .ScaleTo(1, up_time, Easing.OutQuint); + + target.Animate( + t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint), + t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint) + ).Then( + t => t.ScaleTo(1, up_time, Easing.OutQuint), + t => t.FadeOut(up_time, Easing.OutQuint) + ); + } + + return false; + } + + public bool OnReleased(TaikoAction action) => false; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index 0f5065f3ec..ac3cf8305a 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -1,68 +1,68 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects; - -namespace osu.Game.Rulesets.Taiko.UI -{ - public class KiaiHitExplosion : CircularContainer - { - public readonly DrawableHitObject JudgedObject; - - private readonly bool isRim; - - public KiaiHitExplosion(DrawableHitObject judgedObject, bool isRim) - { - this.isRim = isRim; - - JudgedObject = judgedObject; - - Anchor = Anchor.CentreLeft; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1); - - Masking = true; - Alpha = 0.25f; - - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = isRim ? colours.BlueDarker : colours.PinkDarker, - Radius = 60, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint); - this.FadeOut(250); - - Expire(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public class KiaiHitExplosion : CircularContainer + { + public readonly DrawableHitObject JudgedObject; + + private readonly bool isRim; + + public KiaiHitExplosion(DrawableHitObject judgedObject, bool isRim) + { + this.isRim = isRim; + + JudgedObject = judgedObject; + + Anchor = Anchor.CentreLeft; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1); + + Masking = true; + Alpha = 0.25f; + + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = isRim ? colours.BlueDarker : colours.PinkDarker, + Radius = 60, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint); + this.FadeOut(250); + + Expire(); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index effb9eb54f..417a7c2581 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -1,267 +1,267 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Taiko.Objects; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Rulesets.Taiko.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Extensions.Color4Extensions; -using System.Linq; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Taiko.Objects.Drawables; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.UI.Scrolling; - -namespace osu.Game.Rulesets.Taiko.UI -{ - public class TaikoPlayfield : ScrollingPlayfield - { - /// - /// Default height of a when inside a . - /// - public const float DEFAULT_HEIGHT = 178; - - /// - /// The offset from which the center of the hit target lies at. - /// - public const float HIT_TARGET_OFFSET = 100; - - /// - /// The size of the left area of the playfield. This area contains the input drum. - /// - private const float left_area_size = 240; - - protected override bool UserScrollSpeedAdjustment => false; - - private readonly Container hitExplosionContainer; - private readonly Container kiaiExplosionContainer; - private readonly JudgementContainer judgementContainer; - - protected override Container Content => content; - private readonly Container content; - - private readonly Container topLevelHitContainer; - - private readonly Container barlineContainer; - - private readonly Container overlayBackgroundContainer; - private readonly Container backgroundContainer; - - private readonly Box overlayBackground; - private readonly Box background; - - public TaikoPlayfield(ControlPointInfo controlPoints) - : base(ScrollingDirection.Left) - { - AddRangeInternal(new Drawable[] - { - backgroundContainer = new Container - { - Name = "Transparent playfield background", - RelativeSizeAxes = Axes.Both, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.2f), - Radius = 5, - }, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.6f - }, - } - }, - new Container - { - Name = "Right area", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = left_area_size }, - Children = new Drawable[] - { - new Container - { - Name = "Masked elements before hit objects", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Masking = true, - Children = new Drawable[] - { - hitExplosionContainer = new Container - { - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Blending = BlendingMode.Additive, - }, - new HitTarget - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit - } - } - }, - barlineContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET } - }, - content = new Container - { - Name = "Hit objects", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Masking = true - }, - kiaiExplosionContainer = new Container - { - Name = "Kiai hit explosions", - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Blending = BlendingMode.Additive - }, - judgementContainer = new JudgementContainer - { - Name = "Judgements", - RelativeSizeAxes = Axes.Y, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Blending = BlendingMode.Additive - }, - } - }, - overlayBackgroundContainer = new Container - { - Name = "Left overlay", - RelativeSizeAxes = Axes.Y, - Size = new Vector2(left_area_size, 1), - Children = new Drawable[] - { - overlayBackground = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new InputDrum(controlPoints) - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Scale = new Vector2(0.9f), - Margin = new MarginPadding { Right = 20 } - }, - new Box - { - Anchor = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = 10, - Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), - }, - } - }, - new Container - { - Name = "Border", - RelativeSizeAxes = Axes.Both, - Masking = true, - MaskingSmoothness = 0, - BorderThickness = 2, - AlwaysPresent = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - }, - topLevelHitContainer = new Container - { - Name = "Top level hit objects", - RelativeSizeAxes = Axes.Both, - } - }); - - VisibleTimeRange.Value = 6000; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - overlayBackgroundContainer.BorderColour = colours.Gray0; - overlayBackground.Colour = colours.Gray1; - - backgroundContainer.BorderColour = colours.Gray1; - background.Colour = colours.Gray0; - } - - public override void Add(DrawableHitObject h) - { - h.OnJudgement += OnJudgement; - - base.Add(h); - - var barline = h as DrawableBarLine; - if (barline != null) - barlineContainer.Add(barline.CreateProxy()); - - // Swells should be moved at the very top of the playfield when they reach the hit target - var swell = h as DrawableSwell; - if (swell != null) - swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); - } - - internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) - { - if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null) - { - judgementContainer.Add(new DrawableTaikoJudgement(judgement, judgedObject) - { - Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft, - Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre, - RelativePositionAxes = Axes.X, - X = judgement.IsHit ? judgedObject.Position.X : 0, - }); - } - - if (!judgement.IsHit) - return; - - bool isRim = judgedObject.HitObject is RimHit; - - if (judgement is TaikoStrongHitJudgement) - hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit(); - else - { - if (judgedObject.X >= -0.05f && judgedObject is DrawableHit) - { - // If we're far enough away from the left stage, we should bring outselves in front of it - // Todo: The following try-catch is temporary for replay rewinding support - try - { - topLevelHitContainer.Add(judgedObject.CreateProxy()); - } - catch - { - } - } - - hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); - - if (judgedObject.HitObject.Kiai) - kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim)); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Taiko.Objects; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Extensions.Color4Extensions; +using System.Linq; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public class TaikoPlayfield : ScrollingPlayfield + { + /// + /// Default height of a when inside a . + /// + public const float DEFAULT_HEIGHT = 178; + + /// + /// The offset from which the center of the hit target lies at. + /// + public const float HIT_TARGET_OFFSET = 100; + + /// + /// The size of the left area of the playfield. This area contains the input drum. + /// + private const float left_area_size = 240; + + protected override bool UserScrollSpeedAdjustment => false; + + private readonly Container hitExplosionContainer; + private readonly Container kiaiExplosionContainer; + private readonly JudgementContainer judgementContainer; + + protected override Container Content => content; + private readonly Container content; + + private readonly Container topLevelHitContainer; + + private readonly Container barlineContainer; + + private readonly Container overlayBackgroundContainer; + private readonly Container backgroundContainer; + + private readonly Box overlayBackground; + private readonly Box background; + + public TaikoPlayfield(ControlPointInfo controlPoints) + : base(ScrollingDirection.Left) + { + AddRangeInternal(new Drawable[] + { + backgroundContainer = new Container + { + Name = "Transparent playfield background", + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Radius = 5, + }, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.6f + }, + } + }, + new Container + { + Name = "Right area", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = left_area_size }, + Children = new Drawable[] + { + new Container + { + Name = "Masked elements before hit objects", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Masking = true, + Children = new Drawable[] + { + hitExplosionContainer = new Container + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Blending = BlendingMode.Additive, + }, + new HitTarget + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit + } + } + }, + barlineContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = HIT_TARGET_OFFSET } + }, + content = new Container + { + Name = "Hit objects", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Masking = true + }, + kiaiExplosionContainer = new Container + { + Name = "Kiai hit explosions", + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Blending = BlendingMode.Additive + }, + judgementContainer = new JudgementContainer + { + Name = "Judgements", + RelativeSizeAxes = Axes.Y, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Blending = BlendingMode.Additive + }, + } + }, + overlayBackgroundContainer = new Container + { + Name = "Left overlay", + RelativeSizeAxes = Axes.Y, + Size = new Vector2(left_area_size, 1), + Children = new Drawable[] + { + overlayBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new InputDrum(controlPoints) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Scale = new Vector2(0.9f), + Margin = new MarginPadding { Right = 20 } + }, + new Box + { + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 10, + Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + }, + } + }, + new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + MaskingSmoothness = 0, + BorderThickness = 2, + AlwaysPresent = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + topLevelHitContainer = new Container + { + Name = "Top level hit objects", + RelativeSizeAxes = Axes.Both, + } + }); + + VisibleTimeRange.Value = 6000; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + overlayBackgroundContainer.BorderColour = colours.Gray0; + overlayBackground.Colour = colours.Gray1; + + backgroundContainer.BorderColour = colours.Gray1; + background.Colour = colours.Gray0; + } + + public override void Add(DrawableHitObject h) + { + h.OnJudgement += OnJudgement; + + base.Add(h); + + var barline = h as DrawableBarLine; + if (barline != null) + barlineContainer.Add(barline.CreateProxy()); + + // Swells should be moved at the very top of the playfield when they reach the hit target + var swell = h as DrawableSwell; + if (swell != null) + swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); + } + + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + { + if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null) + { + judgementContainer.Add(new DrawableTaikoJudgement(judgement, judgedObject) + { + Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft, + Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre, + RelativePositionAxes = Axes.X, + X = judgement.IsHit ? judgedObject.Position.X : 0, + }); + } + + if (!judgement.IsHit) + return; + + bool isRim = judgedObject.HitObject is RimHit; + + if (judgement is TaikoStrongHitJudgement) + hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit(); + else + { + if (judgedObject.X >= -0.05f && judgedObject is DrawableHit) + { + // If we're far enough away from the left stage, we should bring outselves in front of it + // Todo: The following try-catch is temporary for replay rewinding support + try + { + topLevelHitContainer.Add(judgedObject.CreateProxy()); + } + catch + { + } + } + + hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); + + if (judgedObject.HitObject.Kiai) + kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim)); + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index eb282c53ca..3d3c6ab2f3 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -1,139 +1,139 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Beatmaps; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Taiko.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.Taiko.Replays; -using OpenTK; -using System.Linq; -using osu.Framework.Input; -using osu.Game.Input.Handlers; -using osu.Game.Rulesets.UI.Scrolling; - -namespace osu.Game.Rulesets.Taiko.UI -{ - public class TaikoRulesetContainer : ScrollingRulesetContainer - { - public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) - { - } - - [BackgroundDependencyLoader] - private void load() - { - loadBarLines(); - } - - private void loadBarLines() - { - TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1]; - double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime; - - var timingPoints = Beatmap.ControlPointInfo.TimingPoints.ToList(); - - if (timingPoints.Count == 0) - return; - - int currentIndex = 0; - int currentBeat = 0; - double time = timingPoints[currentIndex].Time; - while (time <= lastHitTime) - { - int nextIndex = currentIndex + 1; - if (nextIndex < timingPoints.Count && time > timingPoints[nextIndex].Time) - { - currentIndex = nextIndex; - time = timingPoints[currentIndex].Time; - currentBeat = 0; - } - - var currentPoint = timingPoints[currentIndex]; - - var barLine = new BarLine - { - StartTime = time, - }; - - barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); - - bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0; - Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine)); - - double bl = currentPoint.BeatLength; - if (bl < 800) - bl *= (int)currentPoint.TimeSignature; - - time += bl; - currentBeat++; - } - } - - protected override Vector2 GetAspectAdjustedSize() - { - const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; - const float default_aspect = 16f / 9f; - - float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect; - - return new Vector2(1, default_relative_height * aspectAdjust); - } - - protected override Vector2 PlayfieldArea => Vector2.One; - - public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); - - protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset); - - public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); - - protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }; - - protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h) - { - var centreHit = h as CentreHit; - if (centreHit != null) - { - if (h.IsStrong) - return new DrawableCentreHitStrong(centreHit); - return new DrawableCentreHit(centreHit); - } - - var rimHit = h as RimHit; - if (rimHit != null) - { - if (h.IsStrong) - return new DrawableRimHitStrong(rimHit); - return new DrawableRimHit(rimHit); - } - - var drumRoll = h as DrumRoll; - if (drumRoll != null) - { - return new DrawableDrumRoll(drumRoll); - } - - var swell = h as Swell; - if (swell != null) - return new DrawableSwell(swell); - - return null; - } - - protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.Taiko.Replays; +using OpenTK; +using System.Linq; +using osu.Framework.Input; +using osu.Game.Input.Handlers; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public class TaikoRulesetContainer : ScrollingRulesetContainer + { + public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + } + + [BackgroundDependencyLoader] + private void load() + { + loadBarLines(); + } + + private void loadBarLines() + { + TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1]; + double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime; + + var timingPoints = Beatmap.ControlPointInfo.TimingPoints.ToList(); + + if (timingPoints.Count == 0) + return; + + int currentIndex = 0; + int currentBeat = 0; + double time = timingPoints[currentIndex].Time; + while (time <= lastHitTime) + { + int nextIndex = currentIndex + 1; + if (nextIndex < timingPoints.Count && time > timingPoints[nextIndex].Time) + { + currentIndex = nextIndex; + time = timingPoints[currentIndex].Time; + currentBeat = 0; + } + + var currentPoint = timingPoints[currentIndex]; + + var barLine = new BarLine + { + StartTime = time, + }; + + barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); + + bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0; + Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine)); + + double bl = currentPoint.BeatLength; + if (bl < 800) + bl *= (int)currentPoint.TimeSignature; + + time += bl; + currentBeat++; + } + } + + protected override Vector2 GetAspectAdjustedSize() + { + const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; + const float default_aspect = 16f / 9f; + + float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect; + + return new Vector2(1, default_relative_height * aspectAdjust); + } + + protected override Vector2 PlayfieldArea => Vector2.One; + + public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); + + protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset); + + public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); + + protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }; + + protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h) + { + var centreHit = h as CentreHit; + if (centreHit != null) + { + if (h.IsStrong) + return new DrawableCentreHitStrong(centreHit); + return new DrawableCentreHit(centreHit); + } + + var rimHit = h as RimHit; + if (rimHit != null) + { + if (h.IsStrong) + return new DrawableRimHitStrong(rimHit); + return new DrawableRimHit(rimHit); + } + + var drumRoll = h as DrumRoll; + if (drumRoll != null) + { + return new DrawableDrumRoll(drumRoll); + } + + var swell = h as Swell; + if (swell != null) + return new DrawableSwell(swell); + + return null; + } + + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay); + } +} diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index bc878b599b..4985aa9365 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -1,215 +1,215 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using NUnit.Framework; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Tests.Resources; -using System.Linq; -using osu.Game.Audio; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Beatmaps.Formats; -using osu.Game.Beatmaps.Timing; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Beatmaps.Formats -{ - [TestFixture] - public class LegacyBeatmapDecoderTest - { - [Test] - public void TestDecodeBeatmapGeneral() - { - var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) - { - var beatmap = decoder.Decode(stream); - var beatmapInfo = beatmap.BeatmapInfo; - var metadata = beatmap.Metadata; - - Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile); - Assert.AreEqual(0, beatmapInfo.AudioLeadIn); - Assert.AreEqual(164471, metadata.PreviewTime); - Assert.IsFalse(beatmapInfo.Countdown); - Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); - Assert.IsTrue(beatmapInfo.RulesetID == 0); - Assert.IsFalse(beatmapInfo.LetterboxInBreaks); - Assert.IsFalse(beatmapInfo.SpecialStyle); - Assert.IsFalse(beatmapInfo.WidescreenStoryboard); - } - } - - [Test] - public void TestDecodeBeatmapEditor() - { - var decoder = new LegacyBeatmapDecoder(); - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) - { - var beatmapInfo = decoder.Decode(stream).BeatmapInfo; - - int[] expectedBookmarks = - { - 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, - 95901, 106450, 116999, 119637, 130186, 140735, 151285, - 161834, 164471, 175020, 185570, 196119, 206669, 209306 - }; - Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length); - for (int i = 0; i < expectedBookmarks.Length; i++) - Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]); - Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing); - Assert.AreEqual(4, beatmapInfo.BeatDivisor); - Assert.AreEqual(4, beatmapInfo.GridSize); - Assert.AreEqual(2, beatmapInfo.TimelineZoom); - } - } - - [Test] - public void TestDecodeBeatmapMetadata() - { - var decoder = new LegacyBeatmapDecoder(); - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) - { - var beatmap = decoder.Decode(stream); - var beatmapInfo = beatmap.BeatmapInfo; - var metadata = beatmap.Metadata; - - Assert.AreEqual("Renatus", metadata.Title); - Assert.AreEqual("Renatus", metadata.TitleUnicode); - Assert.AreEqual("Soleily", metadata.Artist); - Assert.AreEqual("Soleily", metadata.ArtistUnicode); - Assert.AreEqual("Gamu", metadata.AuthorString); - Assert.AreEqual("Insane", beatmapInfo.Version); - Assert.AreEqual(string.Empty, metadata.Source); - Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags); - Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID); - Assert.AreEqual(241526, metadata.OnlineBeatmapSetID); - } - } - - [Test] - public void TestDecodeBeatmapDifficulty() - { - var decoder = new LegacyBeatmapDecoder(); - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) - { - var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty; - - Assert.AreEqual(6.5f, difficulty.DrainRate); - Assert.AreEqual(4, difficulty.CircleSize); - Assert.AreEqual(8, difficulty.OverallDifficulty); - Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8, difficulty.SliderMultiplier); - Assert.AreEqual(2, difficulty.SliderTickRate); - } - } - - [Test] - public void TestDecodeBeatmapEvents() - { - var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) - { - var beatmap = decoder.Decode(stream); - var metadata = beatmap.Metadata; - var breakPoint = beatmap.Breaks[0]; - - Assert.AreEqual("machinetop_background.jpg", metadata.BackgroundFile); - Assert.AreEqual(122474, breakPoint.StartTime); - Assert.AreEqual(140135, breakPoint.EndTime); - Assert.IsTrue(breakPoint.HasEffect); - } - } - - [Test] - public void TestDecodeBeatmapTimingPoints() - { - var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) - { - var beatmap = decoder.Decode(stream); - var controlPoints = beatmap.ControlPointInfo; - - Assert.AreEqual(4, controlPoints.TimingPoints.Count); - var timingPoint = controlPoints.TimingPoints[0]; - Assert.AreEqual(956, timingPoint.Time); - Assert.AreEqual(329.67032967033d, timingPoint.BeatLength); - Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature); - - Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); - var difficultyPoint = controlPoints.DifficultyPoints[0]; - Assert.AreEqual(116999, difficultyPoint.Time); - Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier); - - Assert.AreEqual(34, controlPoints.SamplePoints.Count); - var soundPoint = controlPoints.SamplePoints[0]; - Assert.AreEqual(956, soundPoint.Time); - Assert.AreEqual("soft", soundPoint.SampleBank); - Assert.AreEqual(60, soundPoint.SampleVolume); - - Assert.AreEqual(8, controlPoints.EffectPoints.Count); - var effectPoint = controlPoints.EffectPoints[0]; - Assert.AreEqual(53703, effectPoint.Time); - Assert.IsTrue(effectPoint.KiaiMode); - Assert.IsFalse(effectPoint.OmitFirstBarLine); - } - } - - [Test] - public void TestDecodeBeatmapColors() - { - var decoder = new LegacySkinDecoder(); - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) - { - var comboColors = decoder.Decode(stream).ComboColours; - - Color4[] expectedColors = - { - new Color4(142, 199, 255, 255), - new Color4(255, 128, 128, 255), - new Color4(128, 255, 255, 255), - new Color4(128, 255, 128, 255), - new Color4(255, 187, 255, 255), - new Color4(255, 177, 140, 255), - }; - Assert.AreEqual(expectedColors.Length, comboColors.Count); - for (int i = 0; i < expectedColors.Length; i++) - Assert.AreEqual(expectedColors[i], comboColors[i]); - } - } - - [Test] - public void TestDecodeBeatmapHitObjects() - { - var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; - using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - using (var stream = new StreamReader(resStream)) - { - var hitObjects = decoder.Decode(stream).HitObjects; - - var curveData = hitObjects[0] as IHasCurve; - var positionData = hitObjects[0] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.IsNotNull(curveData); - Assert.AreEqual(new Vector2(192, 168), positionData.Position); - Assert.AreEqual(956, hitObjects[0].StartTime); - Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); - - positionData = hitObjects[1] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.AreEqual(new Vector2(304, 56), positionData.Position); - Assert.AreEqual(1285, hitObjects[1].StartTime); - Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using NUnit.Framework; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Tests.Resources; +using System.Linq; +using osu.Game.Audio; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.Timing; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class LegacyBeatmapDecoderTest + { + [Test] + public void TestDecodeBeatmapGeneral() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.Decode(stream); + var beatmapInfo = beatmap.BeatmapInfo; + var metadata = beatmap.Metadata; + + Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile); + Assert.AreEqual(0, beatmapInfo.AudioLeadIn); + Assert.AreEqual(164471, metadata.PreviewTime); + Assert.IsFalse(beatmapInfo.Countdown); + Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); + Assert.IsTrue(beatmapInfo.RulesetID == 0); + Assert.IsFalse(beatmapInfo.LetterboxInBreaks); + Assert.IsFalse(beatmapInfo.SpecialStyle); + Assert.IsFalse(beatmapInfo.WidescreenStoryboard); + } + } + + [Test] + public void TestDecodeBeatmapEditor() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmapInfo = decoder.Decode(stream).BeatmapInfo; + + int[] expectedBookmarks = + { + 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, + 95901, 106450, 116999, 119637, 130186, 140735, 151285, + 161834, 164471, 175020, 185570, 196119, 206669, 209306 + }; + Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length); + for (int i = 0; i < expectedBookmarks.Length; i++) + Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]); + Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing); + Assert.AreEqual(4, beatmapInfo.BeatDivisor); + Assert.AreEqual(4, beatmapInfo.GridSize); + Assert.AreEqual(2, beatmapInfo.TimelineZoom); + } + } + + [Test] + public void TestDecodeBeatmapMetadata() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.Decode(stream); + var beatmapInfo = beatmap.BeatmapInfo; + var metadata = beatmap.Metadata; + + Assert.AreEqual("Renatus", metadata.Title); + Assert.AreEqual("Renatus", metadata.TitleUnicode); + Assert.AreEqual("Soleily", metadata.Artist); + Assert.AreEqual("Soleily", metadata.ArtistUnicode); + Assert.AreEqual("Gamu", metadata.AuthorString); + Assert.AreEqual("Insane", beatmapInfo.Version); + Assert.AreEqual(string.Empty, metadata.Source); + Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags); + Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID); + Assert.AreEqual(241526, metadata.OnlineBeatmapSetID); + } + } + + [Test] + public void TestDecodeBeatmapDifficulty() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty; + + Assert.AreEqual(6.5f, difficulty.DrainRate); + Assert.AreEqual(4, difficulty.CircleSize); + Assert.AreEqual(8, difficulty.OverallDifficulty); + Assert.AreEqual(9, difficulty.ApproachRate); + Assert.AreEqual(1.8, difficulty.SliderMultiplier); + Assert.AreEqual(2, difficulty.SliderTickRate); + } + } + + [Test] + public void TestDecodeBeatmapEvents() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.Decode(stream); + var metadata = beatmap.Metadata; + var breakPoint = beatmap.Breaks[0]; + + Assert.AreEqual("machinetop_background.jpg", metadata.BackgroundFile); + Assert.AreEqual(122474, breakPoint.StartTime); + Assert.AreEqual(140135, breakPoint.EndTime); + Assert.IsTrue(breakPoint.HasEffect); + } + } + + [Test] + public void TestDecodeBeatmapTimingPoints() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.Decode(stream); + var controlPoints = beatmap.ControlPointInfo; + + Assert.AreEqual(4, controlPoints.TimingPoints.Count); + var timingPoint = controlPoints.TimingPoints[0]; + Assert.AreEqual(956, timingPoint.Time); + Assert.AreEqual(329.67032967033d, timingPoint.BeatLength); + Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature); + + Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); + var difficultyPoint = controlPoints.DifficultyPoints[0]; + Assert.AreEqual(116999, difficultyPoint.Time); + Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier); + + Assert.AreEqual(34, controlPoints.SamplePoints.Count); + var soundPoint = controlPoints.SamplePoints[0]; + Assert.AreEqual(956, soundPoint.Time); + Assert.AreEqual("soft", soundPoint.SampleBank); + Assert.AreEqual(60, soundPoint.SampleVolume); + + Assert.AreEqual(8, controlPoints.EffectPoints.Count); + var effectPoint = controlPoints.EffectPoints[0]; + Assert.AreEqual(53703, effectPoint.Time); + Assert.IsTrue(effectPoint.KiaiMode); + Assert.IsFalse(effectPoint.OmitFirstBarLine); + } + } + + [Test] + public void TestDecodeBeatmapColors() + { + var decoder = new LegacySkinDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var comboColors = decoder.Decode(stream).ComboColours; + + Color4[] expectedColors = + { + new Color4(142, 199, 255, 255), + new Color4(255, 128, 128, 255), + new Color4(128, 255, 255, 255), + new Color4(128, 255, 128, 255), + new Color4(255, 187, 255, 255), + new Color4(255, 177, 140, 255), + }; + Assert.AreEqual(expectedColors.Length, comboColors.Count); + for (int i = 0; i < expectedColors.Length; i++) + Assert.AreEqual(expectedColors[i], comboColors[i]); + } + } + + [Test] + public void TestDecodeBeatmapHitObjects() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var hitObjects = decoder.Decode(stream).HitObjects; + + var curveData = hitObjects[0] as IHasCurve; + var positionData = hitObjects[0] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.IsNotNull(curveData); + Assert.AreEqual(new Vector2(192, 168), positionData.Position); + Assert.AreEqual(956, hitObjects[0].StartTime); + Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); + + positionData = hitObjects[1] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.AreEqual(new Vector2(304, 56), positionData.Position); + Assert.AreEqual(1285, hitObjects[1].StartTime); + Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); + } + } + } +} diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 1c0801c634..3431be91f9 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -1,90 +1,90 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using System.Linq; -using NUnit.Framework; -using OpenTK; -using osu.Framework.Graphics; -using osu.Game.Beatmaps.Formats; -using osu.Game.Storyboards; -using osu.Game.Tests.Resources; - -namespace osu.Game.Tests.Beatmaps.Formats -{ - [TestFixture] - public class LegacyStoryboardDecoderTest - { - [Test] - public void TestDecodeStoryboardEvents() - { - var decoder = new LegacyStoryboardDecoder(); - using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) - using (var stream = new StreamReader(resStream)) - { - var storyboard = decoder.Decode(stream); - - Assert.IsTrue(storyboard.HasDrawable); - Assert.AreEqual(4, storyboard.Layers.Count()); - - StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3); - Assert.IsNotNull(background); - Assert.AreEqual(16, background.Elements.Count()); - Assert.IsTrue(background.EnabledWhenFailing); - Assert.IsTrue(background.EnabledWhenPassing); - Assert.AreEqual("Background", background.Name); - - StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2); - Assert.IsNotNull(fail); - Assert.AreEqual(0, fail.Elements.Count()); - Assert.IsTrue(fail.EnabledWhenFailing); - Assert.IsFalse(fail.EnabledWhenPassing); - Assert.AreEqual("Fail", fail.Name); - - StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1); - Assert.IsNotNull(pass); - Assert.AreEqual(0, pass.Elements.Count()); - Assert.IsFalse(pass.EnabledWhenFailing); - Assert.IsTrue(pass.EnabledWhenPassing); - Assert.AreEqual("Pass", pass.Name); - - StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0); - Assert.IsNotNull(foreground); - Assert.AreEqual(151, foreground.Elements.Count()); - Assert.IsTrue(foreground.EnabledWhenFailing); - Assert.IsTrue(foreground.EnabledWhenPassing); - Assert.AreEqual("Foreground", foreground.Name); - - int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite)); - int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation)); - int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSample)); - - Assert.AreEqual(15, spriteCount); - Assert.AreEqual(1, animationCount); - Assert.AreEqual(0, sampleCount); - Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount); - - var sprite = background.Elements.ElementAt(0) as StoryboardSprite; - Assert.NotNull(sprite); - Assert.IsTrue(sprite.HasCommands); - Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition); - Assert.IsTrue(sprite.IsDrawable); - Assert.AreEqual(Anchor.Centre, sprite.Origin); - Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path); - - var animation = background.Elements.ElementAt(12) as StoryboardAnimation; - Assert.NotNull(animation); - Assert.AreEqual(141175, animation.EndTime); - Assert.AreEqual(10, animation.FrameCount); - Assert.AreEqual(30, animation.FrameDelay); - Assert.IsTrue(animation.HasCommands); - Assert.AreEqual(new Vector2(320, 240), animation.InitialPosition); - Assert.IsTrue(animation.IsDrawable); - Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType); - Assert.AreEqual(Anchor.Centre, animation.Origin); - Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path); - Assert.AreEqual(78993, animation.StartTime); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using System.Linq; +using NUnit.Framework; +using OpenTK; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Formats; +using osu.Game.Storyboards; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class LegacyStoryboardDecoderTest + { + [Test] + public void TestDecodeStoryboardEvents() + { + var decoder = new LegacyStoryboardDecoder(); + using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) + using (var stream = new StreamReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + Assert.IsTrue(storyboard.HasDrawable); + Assert.AreEqual(4, storyboard.Layers.Count()); + + StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3); + Assert.IsNotNull(background); + Assert.AreEqual(16, background.Elements.Count()); + Assert.IsTrue(background.EnabledWhenFailing); + Assert.IsTrue(background.EnabledWhenPassing); + Assert.AreEqual("Background", background.Name); + + StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2); + Assert.IsNotNull(fail); + Assert.AreEqual(0, fail.Elements.Count()); + Assert.IsTrue(fail.EnabledWhenFailing); + Assert.IsFalse(fail.EnabledWhenPassing); + Assert.AreEqual("Fail", fail.Name); + + StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1); + Assert.IsNotNull(pass); + Assert.AreEqual(0, pass.Elements.Count()); + Assert.IsFalse(pass.EnabledWhenFailing); + Assert.IsTrue(pass.EnabledWhenPassing); + Assert.AreEqual("Pass", pass.Name); + + StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0); + Assert.IsNotNull(foreground); + Assert.AreEqual(151, foreground.Elements.Count()); + Assert.IsTrue(foreground.EnabledWhenFailing); + Assert.IsTrue(foreground.EnabledWhenPassing); + Assert.AreEqual("Foreground", foreground.Name); + + int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite)); + int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation)); + int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSample)); + + Assert.AreEqual(15, spriteCount); + Assert.AreEqual(1, animationCount); + Assert.AreEqual(0, sampleCount); + Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount); + + var sprite = background.Elements.ElementAt(0) as StoryboardSprite; + Assert.NotNull(sprite); + Assert.IsTrue(sprite.HasCommands); + Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition); + Assert.IsTrue(sprite.IsDrawable); + Assert.AreEqual(Anchor.Centre, sprite.Origin); + Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path); + + var animation = background.Elements.ElementAt(12) as StoryboardAnimation; + Assert.NotNull(animation); + Assert.AreEqual(141175, animation.EndTime); + Assert.AreEqual(10, animation.FrameCount); + Assert.AreEqual(30, animation.FrameDelay); + Assert.IsTrue(animation.HasCommands); + Assert.AreEqual(new Vector2(320, 240), animation.InitialPosition); + Assert.IsTrue(animation.IsDrawable); + Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType); + Assert.AreEqual(Anchor.Centre, animation.Origin); + Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path); + Assert.AreEqual(78993, animation.StartTime); + } + } + } +} diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index f37672b5cc..9c9589f398 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -1,157 +1,157 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using System.Linq; -using DeepEqual.Syntax; -using NUnit.Framework; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; -using osu.Game.IO.Serialization; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Tests.Resources; -using OpenTK; - -namespace osu.Game.Tests.Beatmaps.Formats -{ - [TestFixture] - public class OsuJsonDecoderTest - { - private const string normal = "Soleily - Renatus (Gamu) [Insane].osu"; - private const string marathon = "Within Temptation - The Unforgiving (Armin) [Marathon].osu"; - private const string with_sb = "Kozato snow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu"; - - [Test] - public void TestDecodeMetadata() - { - var beatmap = decodeAsJson(normal); - var meta = beatmap.BeatmapInfo.Metadata; - Assert.AreEqual(241526, meta.OnlineBeatmapSetID); - Assert.AreEqual("Soleily", meta.Artist); - Assert.AreEqual("Soleily", meta.ArtistUnicode); - Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); - Assert.AreEqual("Gamu", meta.AuthorString); - Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); - Assert.AreEqual(164471, meta.PreviewTime); - Assert.AreEqual(string.Empty, meta.Source); - Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); - Assert.AreEqual("Renatus", meta.Title); - Assert.AreEqual("Renatus", meta.TitleUnicode); - } - - [Test] - public void TestDecodeGeneral() - { - var beatmap = decodeAsJson(normal); - var beatmapInfo = beatmap.BeatmapInfo; - Assert.AreEqual(0, beatmapInfo.AudioLeadIn); - Assert.AreEqual(false, beatmapInfo.Countdown); - Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); - Assert.AreEqual(false, beatmapInfo.SpecialStyle); - Assert.IsTrue(beatmapInfo.RulesetID == 0); - Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); - Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); - } - - [Test] - public void TestDecodeEditor() - { - var beatmap = decodeAsJson(normal); - var beatmapInfo = beatmap.BeatmapInfo; - - int[] expectedBookmarks = - { - 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, - 95901, 106450, 116999, 119637, 130186, 140735, 151285, - 161834, 164471, 175020, 185570, 196119, 206669, 209306 - }; - Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length); - for (int i = 0; i < expectedBookmarks.Length; i++) - Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]); - Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing); - Assert.AreEqual(4, beatmapInfo.BeatDivisor); - Assert.AreEqual(4, beatmapInfo.GridSize); - Assert.AreEqual(2, beatmapInfo.TimelineZoom); - } - - [Test] - public void TestDecodeDifficulty() - { - var beatmap = decodeAsJson(normal); - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; - Assert.AreEqual(6.5f, difficulty.DrainRate); - Assert.AreEqual(4, difficulty.CircleSize); - Assert.AreEqual(8, difficulty.OverallDifficulty); - Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8, difficulty.SliderMultiplier); - Assert.AreEqual(2, difficulty.SliderTickRate); - } - - [Test] - public void TestDecodeHitObjects() - { - var beatmap = decodeAsJson(normal); - - var curveData = beatmap.HitObjects[0] as IHasCurve; - var positionData = beatmap.HitObjects[0] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.IsNotNull(curveData); - Assert.AreEqual(new Vector2(192, 168), positionData.Position); - Assert.AreEqual(956, beatmap.HitObjects[0].StartTime); - Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); - - positionData = beatmap.HitObjects[1] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.AreEqual(new Vector2(304, 56), positionData.Position); - Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime); - Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); - } - - [TestCase(normal)] - [TestCase(marathon)] - // Currently fails: - // [TestCase(with_sb)] - public void TestParity(string beatmap) - { - var beatmaps = decode(beatmap); - beatmaps.jsonDecoded.ShouldDeepEqual(beatmaps.legacyDecoded); - } - - /// - /// Reads a .osu file first with a , serializes the resulting to JSON - /// and then deserializes the result back into a through an . - /// - /// The .osu file to decode. - /// The after being decoded by an . - private Beatmap decodeAsJson(string filename) => decode(filename).jsonDecoded; - - /// - /// Reads a .osu file first with a , serializes the resulting to JSON - /// and then deserializes the result back into a through an . - /// - /// The .osu file to decode. - /// The after being decoded by an . - private (Beatmap legacyDecoded, Beatmap jsonDecoded) decode(string filename) - { - using (var stream = Resource.OpenResource(filename)) - using (var sr = new StreamReader(stream)) - { - - var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); - using (var ms = new MemoryStream()) - using (var sw = new StreamWriter(ms)) - using (var sr2 = new StreamReader(ms)) - { - sw.Write(legacyDecoded.Serialize()); - sw.Flush(); - - ms.Position = 0; - return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2)); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using System.Linq; +using DeepEqual.Syntax; +using NUnit.Framework; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Tests.Resources; +using OpenTK; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class OsuJsonDecoderTest + { + private const string normal = "Soleily - Renatus (Gamu) [Insane].osu"; + private const string marathon = "Within Temptation - The Unforgiving (Armin) [Marathon].osu"; + private const string with_sb = "Kozato snow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu"; + + [Test] + public void TestDecodeMetadata() + { + var beatmap = decodeAsJson(normal); + var meta = beatmap.BeatmapInfo.Metadata; + Assert.AreEqual(241526, meta.OnlineBeatmapSetID); + Assert.AreEqual("Soleily", meta.Artist); + Assert.AreEqual("Soleily", meta.ArtistUnicode); + Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); + Assert.AreEqual("Gamu", meta.AuthorString); + Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); + Assert.AreEqual(164471, meta.PreviewTime); + Assert.AreEqual(string.Empty, meta.Source); + Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); + Assert.AreEqual("Renatus", meta.Title); + Assert.AreEqual("Renatus", meta.TitleUnicode); + } + + [Test] + public void TestDecodeGeneral() + { + var beatmap = decodeAsJson(normal); + var beatmapInfo = beatmap.BeatmapInfo; + Assert.AreEqual(0, beatmapInfo.AudioLeadIn); + Assert.AreEqual(false, beatmapInfo.Countdown); + Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); + Assert.AreEqual(false, beatmapInfo.SpecialStyle); + Assert.IsTrue(beatmapInfo.RulesetID == 0); + Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); + Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); + } + + [Test] + public void TestDecodeEditor() + { + var beatmap = decodeAsJson(normal); + var beatmapInfo = beatmap.BeatmapInfo; + + int[] expectedBookmarks = + { + 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, + 95901, 106450, 116999, 119637, 130186, 140735, 151285, + 161834, 164471, 175020, 185570, 196119, 206669, 209306 + }; + Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length); + for (int i = 0; i < expectedBookmarks.Length; i++) + Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]); + Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing); + Assert.AreEqual(4, beatmapInfo.BeatDivisor); + Assert.AreEqual(4, beatmapInfo.GridSize); + Assert.AreEqual(2, beatmapInfo.TimelineZoom); + } + + [Test] + public void TestDecodeDifficulty() + { + var beatmap = decodeAsJson(normal); + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + Assert.AreEqual(6.5f, difficulty.DrainRate); + Assert.AreEqual(4, difficulty.CircleSize); + Assert.AreEqual(8, difficulty.OverallDifficulty); + Assert.AreEqual(9, difficulty.ApproachRate); + Assert.AreEqual(1.8, difficulty.SliderMultiplier); + Assert.AreEqual(2, difficulty.SliderTickRate); + } + + [Test] + public void TestDecodeHitObjects() + { + var beatmap = decodeAsJson(normal); + + var curveData = beatmap.HitObjects[0] as IHasCurve; + var positionData = beatmap.HitObjects[0] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.IsNotNull(curveData); + Assert.AreEqual(new Vector2(192, 168), positionData.Position); + Assert.AreEqual(956, beatmap.HitObjects[0].StartTime); + Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); + + positionData = beatmap.HitObjects[1] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.AreEqual(new Vector2(304, 56), positionData.Position); + Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime); + Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); + } + + [TestCase(normal)] + [TestCase(marathon)] + // Currently fails: + // [TestCase(with_sb)] + public void TestParity(string beatmap) + { + var beatmaps = decode(beatmap); + beatmaps.jsonDecoded.ShouldDeepEqual(beatmaps.legacyDecoded); + } + + /// + /// Reads a .osu file first with a , serializes the resulting to JSON + /// and then deserializes the result back into a through an . + /// + /// The .osu file to decode. + /// The after being decoded by an . + private Beatmap decodeAsJson(string filename) => decode(filename).jsonDecoded; + + /// + /// Reads a .osu file first with a , serializes the resulting to JSON + /// and then deserializes the result back into a through an . + /// + /// The .osu file to decode. + /// The after being decoded by an . + private (Beatmap legacyDecoded, Beatmap jsonDecoded) decode(string filename) + { + using (var stream = Resource.OpenResource(filename)) + using (var sr = new StreamReader(stream)) + { + + var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); + using (var ms = new MemoryStream()) + using (var sw = new StreamWriter(ms)) + using (var sr2 = new StreamReader(ms)) + { + sw.Write(legacyDecoded.Serialize()); + sw.Flush(); + + ms.Position = 0; + return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2)); + } + } + } + } +} diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 4f5eae85c8..6453cdbd3e 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -1,297 +1,297 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using NUnit.Framework; -using osu.Framework.Platform; -using osu.Game.IPC; -using osu.Framework.Allocation; -using osu.Game.Beatmaps; - -namespace osu.Game.Tests.Beatmaps.IO -{ - [TestFixture] - public class ImportBeatmapTest - { - private const string osz_path = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; - - [Test] - public void TestImportWhenClosed() - { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenClosed")) - { - try - { - loadOszIntoOsu(loadOsu(host)); - } - finally - { - host.Exit(); - } - } - } - - [Test] - public void TestImportThenDelete() - { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDelete")) - { - try - { - var osu = loadOsu(host); - - var imported = loadOszIntoOsu(osu); - - deleteBeatmapSet(imported, osu); - } - finally - { - host.Exit(); - } - } - } - - [Test] - public void TestImportThenImport() - { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport")) - { - try - { - var osu = loadOsu(host); - - var imported = loadOszIntoOsu(osu); - var importedSecondTime = loadOszIntoOsu(osu); - - // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. - Assert.IsTrue(imported.ID == importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); - - var manager = osu.Dependencies.Get(); - - Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 1); - Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1); - } - finally - { - host.Exit(); - } - } - } - - [Test] - public void TestImportThenImportDifferentHash() - { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImportDifferentHash")) - { - try - { - var osu = loadOsu(host); - var manager = osu.Dependencies.Get(); - - var imported = loadOszIntoOsu(osu); - - //var change = manager.QueryBeatmapSets(_ => true).First(); - imported.Hash += "-changed"; - manager.Update(imported); - - var importedSecondTime = loadOszIntoOsu(osu); - - // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. - Assert.IsTrue(imported.ID != importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID); - - Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 1); - Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1); - } - finally - { - host.Exit(); - } - } - } - - [Test] - public void TestImportThenDeleteThenImport() - { - //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDeleteThenImport")) - { - try - { - var osu = loadOsu(host); - - var imported = loadOszIntoOsu(osu); - - deleteBeatmapSet(imported, osu); - - var importedSecondTime = loadOszIntoOsu(osu); - - // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. - Assert.IsTrue(imported.ID == importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); - } - finally - { - host.Exit(); - } - } - } - - [Test] - [NonParallelizable] - [Ignore("Binding IPC on Appveyor isn't working (port in use). Need to figure out why")] - public void TestImportOverIPC() - { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost("host", true)) - using (HeadlessGameHost client = new CleanRunHeadlessGameHost("client", true)) - { - try - { - Assert.IsTrue(host.IsPrimaryInstance); - Assert.IsFalse(client.IsPrimaryInstance); - - var osu = loadOsu(host); - - var temp = prepareTempCopy(osz_path); - Assert.IsTrue(File.Exists(temp)); - - var importer = new ArchiveImportIPCChannel(client); - if (!importer.ImportAsync(temp).Wait(10000)) - Assert.Fail(@"IPC took too long to send"); - - ensureLoaded(osu); - - waitForOrAssert(() => !File.Exists(temp), "Temporary still exists after IPC import", 5000); - } - finally - { - host.Exit(); - } - } - } - - [Test] - public void TestImportWhenFileOpen() - { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenFileOpen")) - { - try - { - var osu = loadOsu(host); - var temp = prepareTempCopy(osz_path); - Assert.IsTrue(File.Exists(temp), "Temporary file copy never substantiated"); - using (File.OpenRead(temp)) - osu.Dependencies.Get().Import(temp); - ensureLoaded(osu); - File.Delete(temp); - Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't"); - } - finally - { - host.Exit(); - } - } - } - - private BeatmapSetInfo loadOszIntoOsu(OsuGameBase osu) - { - var temp = prepareTempCopy(osz_path); - - Assert.IsTrue(File.Exists(temp)); - - var manager = osu.Dependencies.Get(); - - manager.Import(temp); - - var imported = manager.GetAllUsableBeatmapSets(); - - ensureLoaded(osu); - - waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000); - - return imported.FirstOrDefault(); - } - - private void deleteBeatmapSet(BeatmapSetInfo imported, OsuGameBase osu) - { - var manager = osu.Dependencies.Get(); - manager.Delete(imported); - - Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 0); - Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1); - Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending); - } - - private string prepareTempCopy(string path) - { - var temp = Path.GetTempFileName(); - return new FileInfo(path).CopyTo(temp, true).FullName; - } - - private OsuGameBase loadOsu(GameHost host) - { - var osu = new OsuGameBase(); - Task.Run(() => host.Run(osu)); - waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); - return osu; - } - - private void ensureLoaded(OsuGameBase osu, int timeout = 60000) - { - IEnumerable resultSets = null; - var store = osu.Dependencies.Get(); - waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(), - @"BeatmapSet did not import to the database in allocated time.", timeout); - - //ensure we were stored to beatmap database backing... - Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1)."); - IEnumerable queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); - IEnumerable queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); - - //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. - waitForOrAssert(() => queryBeatmaps().Count() == 12, - @"Beatmaps did not import to the database in allocated time", timeout); - waitForOrAssert(() => queryBeatmapSets().Count() == 1, - @"BeatmapSet did not import to the database in allocated time", timeout); - int countBeatmapSetBeatmaps = 0; - int countBeatmaps = 0; - waitForOrAssert(() => - (countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count) == - (countBeatmaps = queryBeatmaps().Count()), - $@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).", timeout); - - var set = queryBeatmapSets().First(); - foreach (BeatmapInfo b in set.Beatmaps) - Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID)); - Assert.IsTrue(set.Beatmaps.Count > 0); - var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap; - Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap; - Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap; - Assert.IsTrue(beatmap?.HitObjects.Count > 0); - beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; - Assert.IsTrue(beatmap?.HitObjects.Count > 0); - } - - private void waitForOrAssert(Func result, string failureMessage, int timeout = 60000) - { - Action waitAction = () => - { - while (!result()) Thread.Sleep(200); - }; - - Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), failureMessage); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Platform; +using osu.Game.IPC; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; + +namespace osu.Game.Tests.Beatmaps.IO +{ + [TestFixture] + public class ImportBeatmapTest + { + private const string osz_path = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; + + [Test] + public void TestImportWhenClosed() + { + //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenClosed")) + { + try + { + loadOszIntoOsu(loadOsu(host)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestImportThenDelete() + { + //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDelete")) + { + try + { + var osu = loadOsu(host); + + var imported = loadOszIntoOsu(osu); + + deleteBeatmapSet(imported, osu); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestImportThenImport() + { + //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport")) + { + try + { + var osu = loadOsu(host); + + var imported = loadOszIntoOsu(osu); + var importedSecondTime = loadOszIntoOsu(osu); + + // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. + Assert.IsTrue(imported.ID == importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); + + var manager = osu.Dependencies.Get(); + + Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 1); + Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestImportThenImportDifferentHash() + { + //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImportDifferentHash")) + { + try + { + var osu = loadOsu(host); + var manager = osu.Dependencies.Get(); + + var imported = loadOszIntoOsu(osu); + + //var change = manager.QueryBeatmapSets(_ => true).First(); + imported.Hash += "-changed"; + manager.Update(imported); + + var importedSecondTime = loadOszIntoOsu(osu); + + // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. + Assert.IsTrue(imported.ID != importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID); + + Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 1); + Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestImportThenDeleteThenImport() + { + //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDeleteThenImport")) + { + try + { + var osu = loadOsu(host); + + var imported = loadOszIntoOsu(osu); + + deleteBeatmapSet(imported, osu); + + var importedSecondTime = loadOszIntoOsu(osu); + + // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash. + Assert.IsTrue(imported.ID == importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); + } + finally + { + host.Exit(); + } + } + } + + [Test] + [NonParallelizable] + [Ignore("Binding IPC on Appveyor isn't working (port in use). Need to figure out why")] + public void TestImportOverIPC() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("host", true)) + using (HeadlessGameHost client = new CleanRunHeadlessGameHost("client", true)) + { + try + { + Assert.IsTrue(host.IsPrimaryInstance); + Assert.IsFalse(client.IsPrimaryInstance); + + var osu = loadOsu(host); + + var temp = prepareTempCopy(osz_path); + Assert.IsTrue(File.Exists(temp)); + + var importer = new ArchiveImportIPCChannel(client); + if (!importer.ImportAsync(temp).Wait(10000)) + Assert.Fail(@"IPC took too long to send"); + + ensureLoaded(osu); + + waitForOrAssert(() => !File.Exists(temp), "Temporary still exists after IPC import", 5000); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestImportWhenFileOpen() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenFileOpen")) + { + try + { + var osu = loadOsu(host); + var temp = prepareTempCopy(osz_path); + Assert.IsTrue(File.Exists(temp), "Temporary file copy never substantiated"); + using (File.OpenRead(temp)) + osu.Dependencies.Get().Import(temp); + ensureLoaded(osu); + File.Delete(temp); + Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't"); + } + finally + { + host.Exit(); + } + } + } + + private BeatmapSetInfo loadOszIntoOsu(OsuGameBase osu) + { + var temp = prepareTempCopy(osz_path); + + Assert.IsTrue(File.Exists(temp)); + + var manager = osu.Dependencies.Get(); + + manager.Import(temp); + + var imported = manager.GetAllUsableBeatmapSets(); + + ensureLoaded(osu); + + waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000); + + return imported.FirstOrDefault(); + } + + private void deleteBeatmapSet(BeatmapSetInfo imported, OsuGameBase osu) + { + var manager = osu.Dependencies.Get(); + manager.Delete(imported); + + Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 0); + Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1); + Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending); + } + + private string prepareTempCopy(string path) + { + var temp = Path.GetTempFileName(); + return new FileInfo(path).CopyTo(temp, true).FullName; + } + + private OsuGameBase loadOsu(GameHost host) + { + var osu = new OsuGameBase(); + Task.Run(() => host.Run(osu)); + waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); + return osu; + } + + private void ensureLoaded(OsuGameBase osu, int timeout = 60000) + { + IEnumerable resultSets = null; + var store = osu.Dependencies.Get(); + waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(), + @"BeatmapSet did not import to the database in allocated time.", timeout); + + //ensure we were stored to beatmap database backing... + Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1)."); + IEnumerable queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); + IEnumerable queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); + + //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. + waitForOrAssert(() => queryBeatmaps().Count() == 12, + @"Beatmaps did not import to the database in allocated time", timeout); + waitForOrAssert(() => queryBeatmapSets().Count() == 1, + @"BeatmapSet did not import to the database in allocated time", timeout); + int countBeatmapSetBeatmaps = 0; + int countBeatmaps = 0; + waitForOrAssert(() => + (countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count) == + (countBeatmaps = queryBeatmaps().Count()), + $@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).", timeout); + + var set = queryBeatmapSets().First(); + foreach (BeatmapInfo b in set.Beatmaps) + Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID)); + Assert.IsTrue(set.Beatmaps.Count > 0); + var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap; + Assert.IsTrue(beatmap?.HitObjects.Count > 0); + beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap; + Assert.IsTrue(beatmap?.HitObjects.Count > 0); + beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap; + Assert.IsTrue(beatmap?.HitObjects.Count > 0); + beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; + Assert.IsTrue(beatmap?.HitObjects.Count > 0); + } + + private void waitForOrAssert(Func result, string failureMessage, int timeout = 60000) + { + Action waitAction = () => + { + while (!result()) Thread.Sleep(200); + }; + + Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), failureMessage); + } + } +} diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 29d25accbb..c6863d1cb5 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -1,83 +1,83 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using System.Linq; -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Tests.Resources; -using osu.Game.Beatmaps.Formats; -using osu.Game.IO.Archives; - -namespace osu.Game.Tests.Beatmaps.IO -{ - [TestFixture] - public class OszArchiveReaderTest - { - [Test] - public void TestReadBeatmaps() - { - using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) - { - var reader = new ZipArchiveReader(osz); - string[] expected = - { - "Soleily - Renatus (Deif) [Platter].osu", - "Soleily - Renatus (Deif) [Rain].osu", - "Soleily - Renatus (Deif) [Salad].osu", - "Soleily - Renatus (ExPew) [Another].osu", - "Soleily - Renatus (ExPew) [Hyper].osu", - "Soleily - Renatus (ExPew) [Normal].osu", - "Soleily - Renatus (Gamu) [Hard].osu", - "Soleily - Renatus (Gamu) [Insane].osu", - "Soleily - Renatus (Gamu) [Normal].osu", - "Soleily - Renatus (MMzz) [Futsuu].osu", - "Soleily - Renatus (MMzz) [Muzukashii].osu", - "Soleily - Renatus (MMzz) [Oni].osu" - }; - var maps = reader.Filenames.ToArray(); - foreach (var map in expected) - Assert.Contains(map, maps); - } - } - - [Test] - public void TestReadMetadata() - { - using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) - { - var reader = new ZipArchiveReader(osz); - - BeatmapMetadata meta; - using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - meta = Decoder.GetDecoder(stream).Decode(stream).Metadata; - - Assert.AreEqual(241526, meta.OnlineBeatmapSetID); - Assert.AreEqual("Soleily", meta.Artist); - Assert.AreEqual("Soleily", meta.ArtistUnicode); - Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); - Assert.AreEqual("Deif", meta.AuthorString); - Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); - Assert.AreEqual(164471 + LegacyBeatmapDecoder.UniversalOffset, meta.PreviewTime); - Assert.AreEqual(string.Empty, meta.Source); - Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); - Assert.AreEqual("Renatus", meta.Title); - Assert.AreEqual("Renatus", meta.TitleUnicode); - } - } - - [Test] - public void TestReadFile() - { - using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) - { - var reader = new ZipArchiveReader(osz); - using (var stream = new StreamReader( - reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - { - Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim()); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Tests.Resources; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO.Archives; + +namespace osu.Game.Tests.Beatmaps.IO +{ + [TestFixture] + public class OszArchiveReaderTest + { + [Test] + public void TestReadBeatmaps() + { + using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) + { + var reader = new ZipArchiveReader(osz); + string[] expected = + { + "Soleily - Renatus (Deif) [Platter].osu", + "Soleily - Renatus (Deif) [Rain].osu", + "Soleily - Renatus (Deif) [Salad].osu", + "Soleily - Renatus (ExPew) [Another].osu", + "Soleily - Renatus (ExPew) [Hyper].osu", + "Soleily - Renatus (ExPew) [Normal].osu", + "Soleily - Renatus (Gamu) [Hard].osu", + "Soleily - Renatus (Gamu) [Insane].osu", + "Soleily - Renatus (Gamu) [Normal].osu", + "Soleily - Renatus (MMzz) [Futsuu].osu", + "Soleily - Renatus (MMzz) [Muzukashii].osu", + "Soleily - Renatus (MMzz) [Oni].osu" + }; + var maps = reader.Filenames.ToArray(); + foreach (var map in expected) + Assert.Contains(map, maps); + } + } + + [Test] + public void TestReadMetadata() + { + using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) + { + var reader = new ZipArchiveReader(osz); + + BeatmapMetadata meta; + using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) + meta = Decoder.GetDecoder(stream).Decode(stream).Metadata; + + Assert.AreEqual(241526, meta.OnlineBeatmapSetID); + Assert.AreEqual("Soleily", meta.Artist); + Assert.AreEqual("Soleily", meta.ArtistUnicode); + Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); + Assert.AreEqual("Deif", meta.AuthorString); + Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); + Assert.AreEqual(164471 + LegacyBeatmapDecoder.UniversalOffset, meta.PreviewTime); + Assert.AreEqual(string.Empty, meta.Source); + Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); + Assert.AreEqual("Renatus", meta.Title); + Assert.AreEqual("Renatus", meta.TitleUnicode); + } + } + + [Test] + public void TestReadFile() + { + using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) + { + var reader = new ZipArchiveReader(osz); + using (var stream = new StreamReader( + reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) + { + Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim()); + } + } + } + } +} diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index f102e4c59f..e91cd2a624 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -1,247 +1,247 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Game.Online.Chat; - -namespace osu.Game.Tests.Chat -{ - [TestFixture] - public class MessageFormatterTests - { - [Test] - public void TestBareLink() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a http://www.basic-link.com/?test=test." }); - - Assert.AreEqual(result.Content, result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("http://www.basic-link.com/?test=test", result.Links[0].Url); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(36, result.Links[0].Length); - } - - [Test] - public void TestMultipleComplexLinks() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a http://test.io/link#fragment. (see https://twitter.com). Also, This string should not be altered. http://example.com/" }); - - Assert.AreEqual(result.Content, result.DisplayContent); - Assert.AreEqual(3, result.Links.Count); - - Assert.AreEqual("http://test.io/link#fragment", result.Links[0].Url); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(28, result.Links[0].Length); - - Assert.AreEqual("https://twitter.com", result.Links[1].Url); - Assert.AreEqual(45, result.Links[1].Index); - Assert.AreEqual(19, result.Links[1].Length); - - Assert.AreEqual("http://example.com/", result.Links[2].Url); - Assert.AreEqual(108, result.Links[2].Index); - Assert.AreEqual(19, result.Links[2].Length); - } - - [Test] - public void TestAjaxLinks() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "https://twitter.com/#!/hashbanglinks" }); - - Assert.AreEqual(result.Content, result.DisplayContent); - Assert.AreEqual(result.Content, result.Links[0].Url); - Assert.AreEqual(0, result.Links[0].Index); - Assert.AreEqual(36, result.Links[0].Length); - } - - [Test] - public void TestUnixHomeLinks() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "http://www.chiark.greenend.org.uk/~sgtatham/putty/" }); - - Assert.AreEqual(result.Content, result.DisplayContent); - Assert.AreEqual(result.Content, result.Links[0].Url); - Assert.AreEqual(0, result.Links[0].Index); - Assert.AreEqual(50, result.Links[0].Length); - } - - [Test] - public void TestCaseInsensitiveLinks() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "look: http://puu.sh/7Ggh8xcC6/asf0asd9876.NEF" }); - - Assert.AreEqual(result.Content, result.DisplayContent); - Assert.AreEqual(6, result.Links[0].Index); - Assert.AreEqual(39, result.Links[0].Length); - } - - [Test] - public void TestWikiLink() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [[Wiki Link]]." }); - - Assert.AreEqual("This is a Wiki Link.", result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki Link", result.Links[0].Url); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(9, result.Links[0].Length); - } - - [Test] - public void TestMultiWikiLink() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [[Wiki Link]] [[Wiki:Link]][[Wiki.Link]]." }); - - Assert.AreEqual("This is a Wiki Link Wiki:LinkWiki.Link.", result.DisplayContent); - Assert.AreEqual(3, result.Links.Count); - - Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki Link", result.Links[0].Url); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(9, result.Links[0].Length); - - Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki:Link", result.Links[1].Url); - Assert.AreEqual(20, result.Links[1].Index); - Assert.AreEqual(9, result.Links[1].Length); - - Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki.Link", result.Links[2].Url); - Assert.AreEqual(29, result.Links[2].Index); - Assert.AreEqual(9, result.Links[2].Length); - } - - [Test] - public void TestOldFormatLink() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (simple test)[https://osu.ppy.sh] of links." }); - - Assert.AreEqual("This is a simple test of links.", result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(11, result.Links[0].Length); - } - - [Test] - public void TestNewFormatLink() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh simple test]." }); - - Assert.AreEqual("This is a simple test.", result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(11, result.Links[0].Length); - } - - [Test] - public void TestMarkdownFormatLink() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [simple test](https://osu.ppy.sh)." }); - - Assert.AreEqual("This is a simple test.", result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(11, result.Links[0].Length); - } - - [Test] - public void TestChannelLink() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is an #english and #japanese." }); - - Assert.AreEqual(result.Content, result.DisplayContent); - Assert.AreEqual(2, result.Links.Count); - Assert.AreEqual("osu://chan/#english", result.Links[0].Url); - Assert.AreEqual("osu://chan/#japanese", result.Links[1].Url); - } - - [Test] - public void TestOsuProtocol() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a custom protocol osu://chan/#english." }); - - Assert.AreEqual(result.Content, result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("osu://chan/#english", result.Links[0].Url); - Assert.AreEqual(26, result.Links[0].Index); - Assert.AreEqual(19, result.Links[0].Length); - - result = MessageFormatter.FormatMessage(new Message { Content = "This is a [custom protocol](osu://chan/#english)." }); - - Assert.AreEqual("This is a custom protocol.", result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("osu://chan/#english", result.Links[0].Url); - Assert.AreEqual("#english", result.Links[0].Argument); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(15, result.Links[0].Length); - } - - [Test] - public void TestOsuMpProtocol() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "Join my multiplayer game osump://12346." }); - - Assert.AreEqual(result.Content, result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("osump://12346", result.Links[0].Url); - Assert.AreEqual(25, result.Links[0].Index); - Assert.AreEqual(13, result.Links[0].Length); - } - - [Test] - public void TestRecursiveBreaking() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh [[simple test]]]." }); - - Assert.AreEqual("This is a [[simple test]].", result.DisplayContent); - Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); - Assert.AreEqual(10, result.Links[0].Index); - Assert.AreEqual(15, result.Links[0].Length); - } - - [Test] - public void TestLinkComplex() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [http://www.simple-test.com simple test] with some [traps] and [[wiki links]]. Don't forget to visit https://osu.ppy.sh (now!)[http://google.com]\uD83D\uDE12" }); - - Assert.AreEqual("This is a simple test with some [traps] and wiki links. Don't forget to visit https://osu.ppy.sh now!\0\0\0", result.DisplayContent); - Assert.AreEqual(5, result.Links.Count); - - Link f = result.Links.Find(l => l.Url == "https://osu.ppy.sh/wiki/wiki links"); - Assert.AreEqual(44, f.Index); - Assert.AreEqual(10, f.Length); - - f = result.Links.Find(l => l.Url == "http://www.simple-test.com"); - Assert.AreEqual(10, f.Index); - Assert.AreEqual(11, f.Length); - - f = result.Links.Find(l => l.Url == "http://google.com"); - Assert.AreEqual(97, f.Index); - Assert.AreEqual(4, f.Length); - - f = result.Links.Find(l => l.Url == "https://osu.ppy.sh"); - Assert.AreEqual(78, f.Index); - Assert.AreEqual(18, f.Length); - - f = result.Links.Find(l => l.Url == "\uD83D\uDE12"); - Assert.AreEqual(101, f.Index); - Assert.AreEqual(3, f.Length); - } - - [Test] - public void TestEmoji() - { - Message result = MessageFormatter.FormatMessage(new Message { Content = "Hello world\uD83D\uDE12<--This is an emoji,There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20" }); - Assert.AreEqual("Hello world\0\0\0<--This is an emoji,There are more:\0\0\0\0\0\0,\0\0\0", result.DisplayContent); - Assert.AreEqual(result.Links.Count, 4); - Assert.AreEqual(result.Links[0].Index, 11); - Assert.AreEqual(result.Links[1].Index, 49); - Assert.AreEqual(result.Links[2].Index, 52); - Assert.AreEqual(result.Links[3].Index, 56); - Assert.AreEqual(result.Links[0].Url, "\uD83D\uDE12"); - Assert.AreEqual(result.Links[1].Url, "\uD83D\uDE10"); - Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00"); - Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Online.Chat; + +namespace osu.Game.Tests.Chat +{ + [TestFixture] + public class MessageFormatterTests + { + [Test] + public void TestBareLink() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a http://www.basic-link.com/?test=test." }); + + Assert.AreEqual(result.Content, result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("http://www.basic-link.com/?test=test", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(36, result.Links[0].Length); + } + + [Test] + public void TestMultipleComplexLinks() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a http://test.io/link#fragment. (see https://twitter.com). Also, This string should not be altered. http://example.com/" }); + + Assert.AreEqual(result.Content, result.DisplayContent); + Assert.AreEqual(3, result.Links.Count); + + Assert.AreEqual("http://test.io/link#fragment", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(28, result.Links[0].Length); + + Assert.AreEqual("https://twitter.com", result.Links[1].Url); + Assert.AreEqual(45, result.Links[1].Index); + Assert.AreEqual(19, result.Links[1].Length); + + Assert.AreEqual("http://example.com/", result.Links[2].Url); + Assert.AreEqual(108, result.Links[2].Index); + Assert.AreEqual(19, result.Links[2].Length); + } + + [Test] + public void TestAjaxLinks() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "https://twitter.com/#!/hashbanglinks" }); + + Assert.AreEqual(result.Content, result.DisplayContent); + Assert.AreEqual(result.Content, result.Links[0].Url); + Assert.AreEqual(0, result.Links[0].Index); + Assert.AreEqual(36, result.Links[0].Length); + } + + [Test] + public void TestUnixHomeLinks() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "http://www.chiark.greenend.org.uk/~sgtatham/putty/" }); + + Assert.AreEqual(result.Content, result.DisplayContent); + Assert.AreEqual(result.Content, result.Links[0].Url); + Assert.AreEqual(0, result.Links[0].Index); + Assert.AreEqual(50, result.Links[0].Length); + } + + [Test] + public void TestCaseInsensitiveLinks() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "look: http://puu.sh/7Ggh8xcC6/asf0asd9876.NEF" }); + + Assert.AreEqual(result.Content, result.DisplayContent); + Assert.AreEqual(6, result.Links[0].Index); + Assert.AreEqual(39, result.Links[0].Length); + } + + [Test] + public void TestWikiLink() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [[Wiki Link]]." }); + + Assert.AreEqual("This is a Wiki Link.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki Link", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(9, result.Links[0].Length); + } + + [Test] + public void TestMultiWikiLink() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [[Wiki Link]] [[Wiki:Link]][[Wiki.Link]]." }); + + Assert.AreEqual("This is a Wiki Link Wiki:LinkWiki.Link.", result.DisplayContent); + Assert.AreEqual(3, result.Links.Count); + + Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki Link", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(9, result.Links[0].Length); + + Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki:Link", result.Links[1].Url); + Assert.AreEqual(20, result.Links[1].Index); + Assert.AreEqual(9, result.Links[1].Length); + + Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki.Link", result.Links[2].Url); + Assert.AreEqual(29, result.Links[2].Index); + Assert.AreEqual(9, result.Links[2].Length); + } + + [Test] + public void TestOldFormatLink() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (simple test)[https://osu.ppy.sh] of links." }); + + Assert.AreEqual("This is a simple test of links.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(11, result.Links[0].Length); + } + + [Test] + public void TestNewFormatLink() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh simple test]." }); + + Assert.AreEqual("This is a simple test.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(11, result.Links[0].Length); + } + + [Test] + public void TestMarkdownFormatLink() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [simple test](https://osu.ppy.sh)." }); + + Assert.AreEqual("This is a simple test.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(11, result.Links[0].Length); + } + + [Test] + public void TestChannelLink() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is an #english and #japanese." }); + + Assert.AreEqual(result.Content, result.DisplayContent); + Assert.AreEqual(2, result.Links.Count); + Assert.AreEqual("osu://chan/#english", result.Links[0].Url); + Assert.AreEqual("osu://chan/#japanese", result.Links[1].Url); + } + + [Test] + public void TestOsuProtocol() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a custom protocol osu://chan/#english." }); + + Assert.AreEqual(result.Content, result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("osu://chan/#english", result.Links[0].Url); + Assert.AreEqual(26, result.Links[0].Index); + Assert.AreEqual(19, result.Links[0].Length); + + result = MessageFormatter.FormatMessage(new Message { Content = "This is a [custom protocol](osu://chan/#english)." }); + + Assert.AreEqual("This is a custom protocol.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("osu://chan/#english", result.Links[0].Url); + Assert.AreEqual("#english", result.Links[0].Argument); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(15, result.Links[0].Length); + } + + [Test] + public void TestOsuMpProtocol() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "Join my multiplayer game osump://12346." }); + + Assert.AreEqual(result.Content, result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("osump://12346", result.Links[0].Url); + Assert.AreEqual(25, result.Links[0].Index); + Assert.AreEqual(13, result.Links[0].Length); + } + + [Test] + public void TestRecursiveBreaking() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh [[simple test]]]." }); + + Assert.AreEqual("This is a [[simple test]].", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(15, result.Links[0].Length); + } + + [Test] + public void TestLinkComplex() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [http://www.simple-test.com simple test] with some [traps] and [[wiki links]]. Don't forget to visit https://osu.ppy.sh (now!)[http://google.com]\uD83D\uDE12" }); + + Assert.AreEqual("This is a simple test with some [traps] and wiki links. Don't forget to visit https://osu.ppy.sh now!\0\0\0", result.DisplayContent); + Assert.AreEqual(5, result.Links.Count); + + Link f = result.Links.Find(l => l.Url == "https://osu.ppy.sh/wiki/wiki links"); + Assert.AreEqual(44, f.Index); + Assert.AreEqual(10, f.Length); + + f = result.Links.Find(l => l.Url == "http://www.simple-test.com"); + Assert.AreEqual(10, f.Index); + Assert.AreEqual(11, f.Length); + + f = result.Links.Find(l => l.Url == "http://google.com"); + Assert.AreEqual(97, f.Index); + Assert.AreEqual(4, f.Length); + + f = result.Links.Find(l => l.Url == "https://osu.ppy.sh"); + Assert.AreEqual(78, f.Index); + Assert.AreEqual(18, f.Length); + + f = result.Links.Find(l => l.Url == "\uD83D\uDE12"); + Assert.AreEqual(101, f.Index); + Assert.AreEqual(3, f.Length); + } + + [Test] + public void TestEmoji() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "Hello world\uD83D\uDE12<--This is an emoji,There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20" }); + Assert.AreEqual("Hello world\0\0\0<--This is an emoji,There are more:\0\0\0\0\0\0,\0\0\0", result.DisplayContent); + Assert.AreEqual(result.Links.Count, 4); + Assert.AreEqual(result.Links[0].Index, 11); + Assert.AreEqual(result.Links[1].Index, 49); + Assert.AreEqual(result.Links[2].Index, 52); + Assert.AreEqual(result.Links[3].Index, 56); + Assert.AreEqual(result.Links[0].Url, "\uD83D\uDE12"); + Assert.AreEqual(result.Links[1].Url, "\uD83D\uDE10"); + Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00"); + Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20"); + } + } +} diff --git a/osu.Game.Tests/Resources/Resource.cs b/osu.Game.Tests/Resources/Resource.cs index 39c20e6b19..ef90591561 100644 --- a/osu.Game.Tests/Resources/Resource.cs +++ b/osu.Game.Tests/Resources/Resource.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using System.Reflection; - -namespace osu.Game.Tests.Resources -{ - public static class Resource - { - public static Stream OpenResource(string name) - { - var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)); - - return Assembly.GetExecutingAssembly().GetManifestResourceStream($@"osu.Game.Tests.Resources.{name}") ?? - Assembly.LoadFrom(Path.Combine(localPath, @"osu.Game.Resources.dll")).GetManifestResourceStream($@"osu.Game.Resources.{name}"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using System.Reflection; + +namespace osu.Game.Tests.Resources +{ + public static class Resource + { + public static Stream OpenResource(string name) + { + var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)); + + return Assembly.GetExecutingAssembly().GetManifestResourceStream($@"osu.Game.Tests.Resources.{name}") ?? + Assembly.LoadFrom(Path.Combine(localPath, @"osu.Game.Resources.dll")).GetManifestResourceStream($@"osu.Game.Resources.{name}"); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseAllPlayers.cs b/osu.Game.Tests/Visual/TestCaseAllPlayers.cs index e633d121ca..4134ce3634 100644 --- a/osu.Game.Tests/Visual/TestCaseAllPlayers.cs +++ b/osu.Game.Tests/Visual/TestCaseAllPlayers.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseAllPlayers : TestCasePlayer - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseAllPlayers : TestCasePlayer + { + } +} diff --git a/osu.Game.Tests/Visual/TestCaseAutoplay.cs b/osu.Game.Tests/Visual/TestCaseAutoplay.cs index d954d0543c..cecb327b6c 100644 --- a/osu.Game.Tests/Visual/TestCaseAutoplay.cs +++ b/osu.Game.Tests/Visual/TestCaseAutoplay.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Screens.Play; - -namespace osu.Game.Tests.Visual -{ - [Description("Player instantiated with an autoplay mod.")] - public class TestCaseAutoplay : TestCasePlayer - { - protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) - { - beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); - return base.CreatePlayer(beatmap, ruleset); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + [Description("Player instantiated with an autoplay mod.")] + public class TestCaseAutoplay : TestCasePlayer + { + protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) + { + beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + return base.CreatePlayer(beatmap, ruleset); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs index 04a662426f..1effa14e76 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Screens.Edit.Screens.Compose; -using OpenTK; - -namespace osu.Game.Tests.Visual -{ - public class TestCaseBeatDivisorControl : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(BindableBeatDivisor) }; - - [BackgroundDependencyLoader] - private void load() - { - Child = new BeatDivisorControl(new BindableBeatDivisor()) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(90, 90) - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Screens.Edit.Screens.Compose; +using OpenTK; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseBeatDivisorControl : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(BindableBeatDivisor) }; + + [BackgroundDependencyLoader] + private void load() + { + Child = new BeatDivisorControl(new BindableBeatDivisor()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(90, 90) + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs index 66cee634f5..596b7839e0 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs @@ -1,205 +1,205 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using NUnit.Framework; -using osu.Framework.Audio.Track; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; -using OpenTK.Graphics; -using osu.Framework.Lists; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseBeatSyncedContainer : OsuTestCase - { - private readonly MusicController mc; - - public TestCaseBeatSyncedContainer() - { - Clock = new FramedClock(); - Clock.ProcessFrame(); - - Add(new BeatContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }); - - Add(mc = new MusicController - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - mc.ToggleVisibility(); - } - - private class BeatContainer : BeatSyncedContainer - { - private const int flash_layer_heigth = 150; - - private readonly InfoString timingPointCount; - private readonly InfoString currentTimingPoint; - private readonly InfoString beatCount; - private readonly InfoString currentBeat; - private readonly InfoString beatsPerMinute; - private readonly InfoString adjustedBeatLength; - private readonly InfoString timeUntilNextBeat; - private readonly InfoString timeSinceLastBeat; - - private readonly Box flashLayer; - - public BeatContainer() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Children = new Drawable[] - { - new Container - { - Name = @"Info Layer", - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Bottom = flash_layer_heigth }, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(150), - }, - new FillFlowContainer - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - timingPointCount = new InfoString(@"Timing points amount"), - currentTimingPoint = new InfoString(@"Current timing point"), - beatCount = new InfoString(@"Beats amount (in the current timing point)"), - currentBeat = new InfoString(@"Current beat"), - beatsPerMinute = new InfoString(@"BPM"), - adjustedBeatLength = new InfoString(@"Adjusted beat length"), - timeUntilNextBeat = new InfoString(@"Time until next beat"), - timeSinceLastBeat = new InfoString(@"Time since last beat"), - } - } - } - }, - new Container - { - Name = @"Color indicator", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = flash_layer_heigth, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - flashLayer = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - Alpha = 0, - } - } - } - }; - - Beatmap.ValueChanged += delegate - { - timingPointCount.Value = 0; - currentTimingPoint.Value = 0; - beatCount.Value = 0; - currentBeat.Value = 0; - beatsPerMinute.Value = 0; - adjustedBeatLength.Value = 0; - timeUntilNextBeat.Value = 0; - timeSinceLastBeat.Value = 0; - }; - } - - private SortedList timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints; - private TimingControlPoint getNextTimingPoint(TimingControlPoint current) - { - if (timingPoints[timingPoints.Count - 1] == current) - return current; - - return timingPoints[timingPoints.IndexOf(current) + 1]; - } - - private int calculateBeatCount(TimingControlPoint current) - { - if (timingPoints.Count == 0) return 0; - - if (timingPoints[timingPoints.Count - 1] == current) - return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength); - - return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength); - } - - protected override void Update() - { - base.Update(); - timeUntilNextBeat.Value = TimeUntilNextBeat; - timeSinceLastBeat.Value = TimeSinceLastBeat; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - timingPointCount.Value = timingPoints.Count; - currentTimingPoint.Value = timingPoints.IndexOf(timingPoint); - beatCount.Value = calculateBeatCount(timingPoint); - currentBeat.Value = beatIndex; - beatsPerMinute.Value = 60000 / timingPoint.BeatLength; - adjustedBeatLength.Value = timingPoint.BeatLength; - - flashLayer.FadeOutFromOne(timingPoint.BeatLength); - } - } - - private class InfoString : FillFlowContainer - { - private const int text_size = 20; - private const int margin = 7; - - private readonly OsuSpriteText valueText; - - public double Value - { - set { valueText.Text = $"{value:G}"; } - } - - public InfoString(string header) - { - AutoSizeAxes = Axes.Both; - Direction = FillDirection.Horizontal; - Add(new OsuSpriteText { Text = header + @": ", TextSize = text_size }); - Add(valueText = new OsuSpriteText { TextSize = text_size }); - Margin = new MarginPadding(margin); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using NUnit.Framework; +using osu.Framework.Audio.Track; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using OpenTK.Graphics; +using osu.Framework.Lists; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseBeatSyncedContainer : OsuTestCase + { + private readonly MusicController mc; + + public TestCaseBeatSyncedContainer() + { + Clock = new FramedClock(); + Clock.ProcessFrame(); + + Add(new BeatContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }); + + Add(mc = new MusicController + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + mc.ToggleVisibility(); + } + + private class BeatContainer : BeatSyncedContainer + { + private const int flash_layer_heigth = 150; + + private readonly InfoString timingPointCount; + private readonly InfoString currentTimingPoint; + private readonly InfoString beatCount; + private readonly InfoString currentBeat; + private readonly InfoString beatsPerMinute; + private readonly InfoString adjustedBeatLength; + private readonly InfoString timeUntilNextBeat; + private readonly InfoString timeSinceLastBeat; + + private readonly Box flashLayer; + + public BeatContainer() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Children = new Drawable[] + { + new Container + { + Name = @"Info Layer", + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Bottom = flash_layer_heigth }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(150), + }, + new FillFlowContainer + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + timingPointCount = new InfoString(@"Timing points amount"), + currentTimingPoint = new InfoString(@"Current timing point"), + beatCount = new InfoString(@"Beats amount (in the current timing point)"), + currentBeat = new InfoString(@"Current beat"), + beatsPerMinute = new InfoString(@"BPM"), + adjustedBeatLength = new InfoString(@"Adjusted beat length"), + timeUntilNextBeat = new InfoString(@"Time until next beat"), + timeSinceLastBeat = new InfoString(@"Time since last beat"), + } + } + } + }, + new Container + { + Name = @"Color indicator", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = flash_layer_heigth, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + flashLayer = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + Alpha = 0, + } + } + } + }; + + Beatmap.ValueChanged += delegate + { + timingPointCount.Value = 0; + currentTimingPoint.Value = 0; + beatCount.Value = 0; + currentBeat.Value = 0; + beatsPerMinute.Value = 0; + adjustedBeatLength.Value = 0; + timeUntilNextBeat.Value = 0; + timeSinceLastBeat.Value = 0; + }; + } + + private SortedList timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints; + private TimingControlPoint getNextTimingPoint(TimingControlPoint current) + { + if (timingPoints[timingPoints.Count - 1] == current) + return current; + + return timingPoints[timingPoints.IndexOf(current) + 1]; + } + + private int calculateBeatCount(TimingControlPoint current) + { + if (timingPoints.Count == 0) return 0; + + if (timingPoints[timingPoints.Count - 1] == current) + return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength); + + return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength); + } + + protected override void Update() + { + base.Update(); + timeUntilNextBeat.Value = TimeUntilNextBeat; + timeSinceLastBeat.Value = TimeSinceLastBeat; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + timingPointCount.Value = timingPoints.Count; + currentTimingPoint.Value = timingPoints.IndexOf(timingPoint); + beatCount.Value = calculateBeatCount(timingPoint); + currentBeat.Value = beatIndex; + beatsPerMinute.Value = 60000 / timingPoint.BeatLength; + adjustedBeatLength.Value = timingPoint.BeatLength; + + flashLayer.FadeOutFromOne(timingPoint.BeatLength); + } + } + + private class InfoString : FillFlowContainer + { + private const int text_size = 20; + private const int margin = 7; + + private readonly OsuSpriteText valueText; + + public double Value + { + set { valueText.Text = $"{value:G}"; } + } + + public InfoString(string header) + { + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Horizontal; + Add(new OsuSpriteText { Text = header + @": ", TextSize = text_size }); + Add(valueText = new OsuSpriteText { TextSize = text_size }); + Margin = new MarginPadding(margin); + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index 9af4f15d1f..f819e882d9 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -1,531 +1,531 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Rulesets; -using osu.Game.Screens.Select; -using osu.Game.Screens.Select.Carousel; -using osu.Game.Screens.Select.Filter; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseBeatmapCarousel : OsuTestCase - { - private TestBeatmapCarousel carousel; - private RulesetStore rulesets; - - public override IReadOnlyList RequiredTypes => new[] - { - typeof(CarouselItem), - typeof(CarouselGroup), - typeof(CarouselGroupEagerSelect), - typeof(CarouselBeatmap), - typeof(CarouselBeatmapSet), - - typeof(DrawableCarouselItem), - typeof(CarouselItemState), - - typeof(DrawableCarouselBeatmap), - typeof(DrawableCarouselBeatmapSet), - }; - - - private readonly Stack selectedSets = new Stack(); - private readonly HashSet eagerSelectedIDs = new HashSet(); - - private BeatmapInfo currentSelection; - - private const int set_count = 5; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - - Add(carousel = new TestBeatmapCarousel - { - RelativeSizeAxes = Axes.Both, - }); - - List beatmapSets = new List(); - - for (int i = 1; i <= set_count; i++) - beatmapSets.Add(createTestBeatmapSet(i)); - - carousel.SelectionChanged = s => currentSelection = s; - - AddStep("Load Beatmaps", () => { carousel.BeatmapSets = beatmapSets; }); - - bool changed = false; - carousel.BeatmapSetsChanged = () => changed = true; - AddUntilStep(() => changed, "Wait for load"); - - testTraversal(); - testFiltering(); - testRandom(); - testAddRemove(); - testSorting(); - - testRemoveAll(); - testEmptyTraversal(); - testHiding(); - testSelectingFilteredRuleset(); - testCarouselRootIsRandom(); - } - - private void ensureRandomFetchSuccess() => - AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet); - - private void checkSelected(int set, int? diff = null) => - AddAssert($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () => - { - if (diff != null) - return carousel.SelectedBeatmap == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First(); - - return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmap); - }); - - private void setSelected(int set, int diff) => - AddStep($"select set{set} diff{diff}", () => - carousel.SelectBeatmap(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First())); - - private void advanceSelection(bool diff, int direction = 1, int count = 1) - { - if (count == 1) - AddStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () => - carousel.SelectNext(direction, !diff)); - else - { - AddRepeatStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () => - carousel.SelectNext(direction, !diff), count); - } - } - - private void checkVisibleItemCount(bool diff, int count) => - AddAssert($"{count} {(diff ? "diffs" : "sets")} visible", () => - carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count); - - private void checkNoSelection() => AddAssert("Selection is null", () => currentSelection == null); - - private void nextRandom() => - AddStep("select random next", () => - { - carousel.RandomAlgorithm.Value = RandomSelectAlgorithm.RandomPermutation; - - if (!selectedSets.Any() && carousel.SelectedBeatmap != null) - selectedSets.Push(carousel.SelectedBeatmapSet); - - carousel.SelectNextRandom(); - selectedSets.Push(carousel.SelectedBeatmapSet); - }); - - private void ensureRandomDidntRepeat() => - AddAssert("ensure no repeats", () => selectedSets.Distinct().Count() == selectedSets.Count); - - private void prevRandom() => AddStep("select random last", () => - { - carousel.SelectPreviousRandom(); - selectedSets.Pop(); - }); - - private bool selectedBeatmapVisible() - { - var currentlySelected = carousel.Items.FirstOrDefault(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected); - if (currentlySelected == null) - return true; - return currentlySelected.Item.Visible; - } - - private void checkInvisibleDifficultiesUnselectable() - { - nextRandom(); - AddAssert("Selection is visible", selectedBeatmapVisible); - } - - private void checkNonmatchingFilter() - { - AddStep("Toggle non-matching filter", () => - { - carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false); - carousel.Filter(new FilterCriteria(), false); - eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID); - } - ); - } - - /// - /// Test keyboard traversal - /// - private void testTraversal() - { - advanceSelection(direction: 1, diff: false); - checkSelected(1, 1); - - advanceSelection(direction: 1, diff: true); - checkSelected(1, 2); - - advanceSelection(direction: -1, diff: false); - checkSelected(set_count, 1); - - advanceSelection(direction: -1, diff: true); - checkSelected(set_count - 1, 3); - - advanceSelection(diff: false); - advanceSelection(diff: false); - checkSelected(1, 2); - - advanceSelection(direction: -1, diff: true); - advanceSelection(direction: -1, diff: true); - checkSelected(set_count, 3); - } - - /// - /// Test filtering - /// - private void testFiltering() - { - // basic filtering - - setSelected(1, 1); - - AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = "set #3!" }, false)); - checkVisibleItemCount(diff: false, count: 1); - checkVisibleItemCount(diff: true, count: 3); - checkSelected(3, 1); - - advanceSelection(diff: true, count: 4); - checkSelected(3, 2); - - AddStep("Un-filter (debounce)", () => carousel.Filter(new FilterCriteria())); - AddUntilStep(() => !carousel.PendingFilterTask, "Wait for debounce"); - checkVisibleItemCount(diff: false, count: set_count); - checkVisibleItemCount(diff: true, count: 3); - - // test filtering some difficulties (and keeping current beatmap set selected). - - setSelected(1, 2); - AddStep("Filter some difficulties", () => carousel.Filter(new FilterCriteria { SearchText = "Normal" }, false)); - checkSelected(1, 1); - - AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); - checkSelected(1, 1); - - AddStep("Filter all", () => carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false)); - - checkVisibleItemCount(false, 0); - checkVisibleItemCount(true, 0); - AddAssert("Selection is null", () => currentSelection == null); - - advanceSelection(true); - AddAssert("Selection is null", () => currentSelection == null); - - advanceSelection(false); - AddAssert("Selection is null", () => currentSelection == null); - - AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); - - AddAssert("Selection is non-null", () => currentSelection != null); - } - - /// - /// Test random non-repeating algorithm - /// - private void testRandom() - { - setSelected(1, 1); - - nextRandom(); - ensureRandomDidntRepeat(); - nextRandom(); - ensureRandomDidntRepeat(); - nextRandom(); - ensureRandomDidntRepeat(); - - prevRandom(); - ensureRandomFetchSuccess(); - prevRandom(); - ensureRandomFetchSuccess(); - - nextRandom(); - ensureRandomDidntRepeat(); - nextRandom(); - ensureRandomDidntRepeat(); - - nextRandom(); - AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet)); - - AddStep("Add set with 100 difficulties", () => carousel.UpdateBeatmapSet(createTestBeatmapSetWithManyDifficulties(set_count + 1))); - AddStep("Filter Extra", () => carousel.Filter(new FilterCriteria { SearchText = "Extra 10" }, false)); - checkInvisibleDifficultiesUnselectable(); - checkInvisibleDifficultiesUnselectable(); - checkInvisibleDifficultiesUnselectable(); - checkInvisibleDifficultiesUnselectable(); - checkInvisibleDifficultiesUnselectable(); - AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); - } - - /// - /// Test adding and removing beatmap sets - /// - private void testAddRemove() - { - AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 1))); - AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 2))); - - checkVisibleItemCount(false, set_count + 2); - - AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 2))); - - checkVisibleItemCount(false, set_count + 1); - - setSelected(set_count + 1, 1); - - AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 1))); - - checkVisibleItemCount(false, set_count); - - checkSelected(set_count); - } - - /// - /// Test sorting - /// - private void testSorting() - { - AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false)); - AddAssert("Check zzzzz is at bottom", () => carousel.BeatmapSets.Last().Metadata.AuthorString == "zzzzz"); - AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false)); - AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!")); - } - - private void testRemoveAll() - { - setSelected(2, 1); - AddAssert("Selection is non-null", () => currentSelection != null); - - AddStep("Remove selected", () => carousel.RemoveBeatmapSet(carousel.SelectedBeatmapSet)); - checkSelected(2); - - AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First())); - AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First())); - checkSelected(1); - - AddUntilStep(() => - { - if (!carousel.BeatmapSets.Any()) return true; - - carousel.RemoveBeatmapSet(carousel.BeatmapSets.Last()); - return false; - }, "Remove all"); - - checkNoSelection(); - } - - private void testEmptyTraversal() - { - advanceSelection(direction: 1, diff: false); - checkNoSelection(); - - advanceSelection(direction: 1, diff: true); - checkNoSelection(); - - advanceSelection(direction: -1, diff: false); - checkNoSelection(); - - advanceSelection(direction: -1, diff: true); - checkNoSelection(); - } - - private void testHiding() - { - var hidingSet = createTestBeatmapSet(1); - hidingSet.Beatmaps[1].Hidden = true; - AddStep("Add set with diff 2 hidden", () => carousel.UpdateBeatmapSet(hidingSet)); - setSelected(1, 1); - - checkVisibleItemCount(true, 2); - advanceSelection(true); - checkSelected(1, 3); - - setHidden(3); - checkSelected(1, 1); - - setHidden(2, false); - advanceSelection(true); - checkSelected(1, 2); - - setHidden(1); - checkSelected(1, 2); - - setHidden(2); - checkNoSelection(); - - void setHidden(int diff, bool hidden = true) - { - AddStep((hidden ? "" : "un") + $"hide diff {diff}", () => - { - hidingSet.Beatmaps[diff - 1].Hidden = hidden; - carousel.UpdateBeatmapSet(hidingSet); - }); - } - } - - private void testSelectingFilteredRuleset() - { - var testMixed = createTestBeatmapSet(set_count + 1); - AddStep("add mixed ruleset beatmapset", () => - { - for (int i = 0; i <= 2; i++) - { - testMixed.Beatmaps[i].Ruleset = rulesets.AvailableRulesets.ElementAt(i); - testMixed.Beatmaps[i].RulesetID = i; - } - - carousel.UpdateBeatmapSet(testMixed); - }); - AddStep("filter to ruleset 0", () => - carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false)); - AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false)); - AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0])); - - AddStep("remove mixed set", () => - { - carousel.RemoveBeatmapSet(testMixed); - testMixed = null; - }); - var testSingle = createTestBeatmapSet(set_count + 2); - testSingle.Beatmaps.ForEach(b => - { - b.Ruleset = rulesets.AvailableRulesets.ElementAt(1); - b.RulesetID = b.Ruleset.ID ?? 1; - }); - AddStep("add single ruleset beatmapset", () => carousel.UpdateBeatmapSet(testSingle)); - AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testSingle.Beatmaps[0], false)); - checkNoSelection(); - AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle)); - } - - private void testCarouselRootIsRandom() - { - List beatmapSets = new List(); - - for (int i = 1; i <= 50; i++) - beatmapSets.Add(createTestBeatmapSet(i)); - - AddStep("Load 50 Beatmaps", () => { carousel.BeatmapSets = beatmapSets; }); - advanceSelection(direction: 1, diff: false); - checkNonmatchingFilter(); - checkNonmatchingFilter(); - checkNonmatchingFilter(); - checkNonmatchingFilter(); - checkNonmatchingFilter(); - AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1); - } - - private BeatmapSetInfo createTestBeatmapSet(int id) - { - return new BeatmapSetInfo - { - ID = id, - OnlineBeatmapSetID = id, - Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(), - Metadata = new BeatmapMetadata - { - OnlineBeatmapSetID = id, - // Create random metadata, then we can check if sorting works based on these - Artist = $"peppy{id.ToString().PadLeft(6, '0')}", - Title = $"test set #{id}!", - AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, id - 1)), 5)) - }, - Beatmaps = new List(new[] - { - new BeatmapInfo - { - OnlineBeatmapID = id * 10, - Path = "normal.osu", - Version = "Normal", - StarDifficulty = 2, - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 3.5f, - } - }, - new BeatmapInfo - { - OnlineBeatmapID = id * 10 + 1, - Path = "hard.osu", - Version = "Hard", - StarDifficulty = 5, - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 5, - } - }, - new BeatmapInfo - { - OnlineBeatmapID = id * 10 + 2, - Path = "insane.osu", - Version = "Insane", - StarDifficulty = 6, - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 7, - } - }, - }), - }; - } - - private BeatmapSetInfo createTestBeatmapSetWithManyDifficulties(int id) - { - var toReturn = new BeatmapSetInfo - { - ID = id, - OnlineBeatmapSetID = id, - Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(), - Metadata = new BeatmapMetadata - { - OnlineBeatmapSetID = id, - // Create random metadata, then we can check if sorting works based on these - Artist = $"peppy{id.ToString().PadLeft(6, '0')}", - Title = $"test set #{id}!", - AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, id - 1)), 5)) - }, - Beatmaps = new List(), - }; - for (int b = 1; b < 101; b++) - { - toReturn.Beatmaps.Add(new BeatmapInfo - { - OnlineBeatmapID = b * 10, - Path = $"extra{b}.osu", - Version = $"Extra {b}", - StarDifficulty = 2, - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 3.5f, - } - }); - } - return toReturn; - } - - private class TestBeatmapCarousel : BeatmapCarousel - { - public new List Items => base.Items; - - public bool PendingFilterTask => FilterTask != null; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Screens.Select; +using osu.Game.Screens.Select.Carousel; +using osu.Game.Screens.Select.Filter; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseBeatmapCarousel : OsuTestCase + { + private TestBeatmapCarousel carousel; + private RulesetStore rulesets; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(CarouselItem), + typeof(CarouselGroup), + typeof(CarouselGroupEagerSelect), + typeof(CarouselBeatmap), + typeof(CarouselBeatmapSet), + + typeof(DrawableCarouselItem), + typeof(CarouselItemState), + + typeof(DrawableCarouselBeatmap), + typeof(DrawableCarouselBeatmapSet), + }; + + + private readonly Stack selectedSets = new Stack(); + private readonly HashSet eagerSelectedIDs = new HashSet(); + + private BeatmapInfo currentSelection; + + private const int set_count = 5; + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + this.rulesets = rulesets; + + Add(carousel = new TestBeatmapCarousel + { + RelativeSizeAxes = Axes.Both, + }); + + List beatmapSets = new List(); + + for (int i = 1; i <= set_count; i++) + beatmapSets.Add(createTestBeatmapSet(i)); + + carousel.SelectionChanged = s => currentSelection = s; + + AddStep("Load Beatmaps", () => { carousel.BeatmapSets = beatmapSets; }); + + bool changed = false; + carousel.BeatmapSetsChanged = () => changed = true; + AddUntilStep(() => changed, "Wait for load"); + + testTraversal(); + testFiltering(); + testRandom(); + testAddRemove(); + testSorting(); + + testRemoveAll(); + testEmptyTraversal(); + testHiding(); + testSelectingFilteredRuleset(); + testCarouselRootIsRandom(); + } + + private void ensureRandomFetchSuccess() => + AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet); + + private void checkSelected(int set, int? diff = null) => + AddAssert($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () => + { + if (diff != null) + return carousel.SelectedBeatmap == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First(); + + return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmap); + }); + + private void setSelected(int set, int diff) => + AddStep($"select set{set} diff{diff}", () => + carousel.SelectBeatmap(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First())); + + private void advanceSelection(bool diff, int direction = 1, int count = 1) + { + if (count == 1) + AddStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () => + carousel.SelectNext(direction, !diff)); + else + { + AddRepeatStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () => + carousel.SelectNext(direction, !diff), count); + } + } + + private void checkVisibleItemCount(bool diff, int count) => + AddAssert($"{count} {(diff ? "diffs" : "sets")} visible", () => + carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count); + + private void checkNoSelection() => AddAssert("Selection is null", () => currentSelection == null); + + private void nextRandom() => + AddStep("select random next", () => + { + carousel.RandomAlgorithm.Value = RandomSelectAlgorithm.RandomPermutation; + + if (!selectedSets.Any() && carousel.SelectedBeatmap != null) + selectedSets.Push(carousel.SelectedBeatmapSet); + + carousel.SelectNextRandom(); + selectedSets.Push(carousel.SelectedBeatmapSet); + }); + + private void ensureRandomDidntRepeat() => + AddAssert("ensure no repeats", () => selectedSets.Distinct().Count() == selectedSets.Count); + + private void prevRandom() => AddStep("select random last", () => + { + carousel.SelectPreviousRandom(); + selectedSets.Pop(); + }); + + private bool selectedBeatmapVisible() + { + var currentlySelected = carousel.Items.FirstOrDefault(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected); + if (currentlySelected == null) + return true; + return currentlySelected.Item.Visible; + } + + private void checkInvisibleDifficultiesUnselectable() + { + nextRandom(); + AddAssert("Selection is visible", selectedBeatmapVisible); + } + + private void checkNonmatchingFilter() + { + AddStep("Toggle non-matching filter", () => + { + carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false); + carousel.Filter(new FilterCriteria(), false); + eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID); + } + ); + } + + /// + /// Test keyboard traversal + /// + private void testTraversal() + { + advanceSelection(direction: 1, diff: false); + checkSelected(1, 1); + + advanceSelection(direction: 1, diff: true); + checkSelected(1, 2); + + advanceSelection(direction: -1, diff: false); + checkSelected(set_count, 1); + + advanceSelection(direction: -1, diff: true); + checkSelected(set_count - 1, 3); + + advanceSelection(diff: false); + advanceSelection(diff: false); + checkSelected(1, 2); + + advanceSelection(direction: -1, diff: true); + advanceSelection(direction: -1, diff: true); + checkSelected(set_count, 3); + } + + /// + /// Test filtering + /// + private void testFiltering() + { + // basic filtering + + setSelected(1, 1); + + AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = "set #3!" }, false)); + checkVisibleItemCount(diff: false, count: 1); + checkVisibleItemCount(diff: true, count: 3); + checkSelected(3, 1); + + advanceSelection(diff: true, count: 4); + checkSelected(3, 2); + + AddStep("Un-filter (debounce)", () => carousel.Filter(new FilterCriteria())); + AddUntilStep(() => !carousel.PendingFilterTask, "Wait for debounce"); + checkVisibleItemCount(diff: false, count: set_count); + checkVisibleItemCount(diff: true, count: 3); + + // test filtering some difficulties (and keeping current beatmap set selected). + + setSelected(1, 2); + AddStep("Filter some difficulties", () => carousel.Filter(new FilterCriteria { SearchText = "Normal" }, false)); + checkSelected(1, 1); + + AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + checkSelected(1, 1); + + AddStep("Filter all", () => carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false)); + + checkVisibleItemCount(false, 0); + checkVisibleItemCount(true, 0); + AddAssert("Selection is null", () => currentSelection == null); + + advanceSelection(true); + AddAssert("Selection is null", () => currentSelection == null); + + advanceSelection(false); + AddAssert("Selection is null", () => currentSelection == null); + + AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + + AddAssert("Selection is non-null", () => currentSelection != null); + } + + /// + /// Test random non-repeating algorithm + /// + private void testRandom() + { + setSelected(1, 1); + + nextRandom(); + ensureRandomDidntRepeat(); + nextRandom(); + ensureRandomDidntRepeat(); + nextRandom(); + ensureRandomDidntRepeat(); + + prevRandom(); + ensureRandomFetchSuccess(); + prevRandom(); + ensureRandomFetchSuccess(); + + nextRandom(); + ensureRandomDidntRepeat(); + nextRandom(); + ensureRandomDidntRepeat(); + + nextRandom(); + AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet)); + + AddStep("Add set with 100 difficulties", () => carousel.UpdateBeatmapSet(createTestBeatmapSetWithManyDifficulties(set_count + 1))); + AddStep("Filter Extra", () => carousel.Filter(new FilterCriteria { SearchText = "Extra 10" }, false)); + checkInvisibleDifficultiesUnselectable(); + checkInvisibleDifficultiesUnselectable(); + checkInvisibleDifficultiesUnselectable(); + checkInvisibleDifficultiesUnselectable(); + checkInvisibleDifficultiesUnselectable(); + AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); + } + + /// + /// Test adding and removing beatmap sets + /// + private void testAddRemove() + { + AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 1))); + AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 2))); + + checkVisibleItemCount(false, set_count + 2); + + AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 2))); + + checkVisibleItemCount(false, set_count + 1); + + setSelected(set_count + 1, 1); + + AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 1))); + + checkVisibleItemCount(false, set_count); + + checkSelected(set_count); + } + + /// + /// Test sorting + /// + private void testSorting() + { + AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false)); + AddAssert("Check zzzzz is at bottom", () => carousel.BeatmapSets.Last().Metadata.AuthorString == "zzzzz"); + AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false)); + AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!")); + } + + private void testRemoveAll() + { + setSelected(2, 1); + AddAssert("Selection is non-null", () => currentSelection != null); + + AddStep("Remove selected", () => carousel.RemoveBeatmapSet(carousel.SelectedBeatmapSet)); + checkSelected(2); + + AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First())); + AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First())); + checkSelected(1); + + AddUntilStep(() => + { + if (!carousel.BeatmapSets.Any()) return true; + + carousel.RemoveBeatmapSet(carousel.BeatmapSets.Last()); + return false; + }, "Remove all"); + + checkNoSelection(); + } + + private void testEmptyTraversal() + { + advanceSelection(direction: 1, diff: false); + checkNoSelection(); + + advanceSelection(direction: 1, diff: true); + checkNoSelection(); + + advanceSelection(direction: -1, diff: false); + checkNoSelection(); + + advanceSelection(direction: -1, diff: true); + checkNoSelection(); + } + + private void testHiding() + { + var hidingSet = createTestBeatmapSet(1); + hidingSet.Beatmaps[1].Hidden = true; + AddStep("Add set with diff 2 hidden", () => carousel.UpdateBeatmapSet(hidingSet)); + setSelected(1, 1); + + checkVisibleItemCount(true, 2); + advanceSelection(true); + checkSelected(1, 3); + + setHidden(3); + checkSelected(1, 1); + + setHidden(2, false); + advanceSelection(true); + checkSelected(1, 2); + + setHidden(1); + checkSelected(1, 2); + + setHidden(2); + checkNoSelection(); + + void setHidden(int diff, bool hidden = true) + { + AddStep((hidden ? "" : "un") + $"hide diff {diff}", () => + { + hidingSet.Beatmaps[diff - 1].Hidden = hidden; + carousel.UpdateBeatmapSet(hidingSet); + }); + } + } + + private void testSelectingFilteredRuleset() + { + var testMixed = createTestBeatmapSet(set_count + 1); + AddStep("add mixed ruleset beatmapset", () => + { + for (int i = 0; i <= 2; i++) + { + testMixed.Beatmaps[i].Ruleset = rulesets.AvailableRulesets.ElementAt(i); + testMixed.Beatmaps[i].RulesetID = i; + } + + carousel.UpdateBeatmapSet(testMixed); + }); + AddStep("filter to ruleset 0", () => + carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false)); + AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false)); + AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0])); + + AddStep("remove mixed set", () => + { + carousel.RemoveBeatmapSet(testMixed); + testMixed = null; + }); + var testSingle = createTestBeatmapSet(set_count + 2); + testSingle.Beatmaps.ForEach(b => + { + b.Ruleset = rulesets.AvailableRulesets.ElementAt(1); + b.RulesetID = b.Ruleset.ID ?? 1; + }); + AddStep("add single ruleset beatmapset", () => carousel.UpdateBeatmapSet(testSingle)); + AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testSingle.Beatmaps[0], false)); + checkNoSelection(); + AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle)); + } + + private void testCarouselRootIsRandom() + { + List beatmapSets = new List(); + + for (int i = 1; i <= 50; i++) + beatmapSets.Add(createTestBeatmapSet(i)); + + AddStep("Load 50 Beatmaps", () => { carousel.BeatmapSets = beatmapSets; }); + advanceSelection(direction: 1, diff: false); + checkNonmatchingFilter(); + checkNonmatchingFilter(); + checkNonmatchingFilter(); + checkNonmatchingFilter(); + checkNonmatchingFilter(); + AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1); + } + + private BeatmapSetInfo createTestBeatmapSet(int id) + { + return new BeatmapSetInfo + { + ID = id, + OnlineBeatmapSetID = id, + Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(), + Metadata = new BeatmapMetadata + { + OnlineBeatmapSetID = id, + // Create random metadata, then we can check if sorting works based on these + Artist = $"peppy{id.ToString().PadLeft(6, '0')}", + Title = $"test set #{id}!", + AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, id - 1)), 5)) + }, + Beatmaps = new List(new[] + { + new BeatmapInfo + { + OnlineBeatmapID = id * 10, + Path = "normal.osu", + Version = "Normal", + StarDifficulty = 2, + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 3.5f, + } + }, + new BeatmapInfo + { + OnlineBeatmapID = id * 10 + 1, + Path = "hard.osu", + Version = "Hard", + StarDifficulty = 5, + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 5, + } + }, + new BeatmapInfo + { + OnlineBeatmapID = id * 10 + 2, + Path = "insane.osu", + Version = "Insane", + StarDifficulty = 6, + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 7, + } + }, + }), + }; + } + + private BeatmapSetInfo createTestBeatmapSetWithManyDifficulties(int id) + { + var toReturn = new BeatmapSetInfo + { + ID = id, + OnlineBeatmapSetID = id, + Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(), + Metadata = new BeatmapMetadata + { + OnlineBeatmapSetID = id, + // Create random metadata, then we can check if sorting works based on these + Artist = $"peppy{id.ToString().PadLeft(6, '0')}", + Title = $"test set #{id}!", + AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, id - 1)), 5)) + }, + Beatmaps = new List(), + }; + for (int b = 1; b < 101; b++) + { + toReturn.Beatmaps.Add(new BeatmapInfo + { + OnlineBeatmapID = b * 10, + Path = $"extra{b}.osu", + Version = $"Extra {b}", + StarDifficulty = 2, + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 3.5f, + } + }); + } + return toReturn; + } + + private class TestBeatmapCarousel : BeatmapCarousel + { + public new List Items => base.Items; + + public bool PendingFilterTask => FilterTask != null; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs b/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs index 3e94de0f3e..41d85673f8 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Screens.Select; -using OpenTK; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - [System.ComponentModel.Description("PlaySongSelect leaderboard/details area")] - public class TestCaseBeatmapDetailArea : OsuTestCase - { - public TestCaseBeatmapDetailArea() - { - Add(new BeatmapDetailArea - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(550f, 450f), - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Screens.Select; +using OpenTK; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + [System.ComponentModel.Description("PlaySongSelect leaderboard/details area")] + public class TestCaseBeatmapDetailArea : OsuTestCase + { + public TestCaseBeatmapDetailArea() + { + Add(new BeatmapDetailArea + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(550f, 450f), + }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs b/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs index e5924fccf8..0f7d871e5e 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs @@ -1,115 +1,115 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using System.Linq; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Screens.Select; - -namespace osu.Game.Tests.Visual -{ - [Description("PlaySongSelect beatmap details")] - public class TestCaseBeatmapDetails : OsuTestCase - { - public TestCaseBeatmapDetails() - { - BeatmapDetails details; - Add(details = new BeatmapDetails - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(150), - }); - - AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo - { - Version = "All Metrics", - Metadata = new BeatmapMetadata - { - Source = "osu!lazer", - Tags = "this beatmap has all the metrics", - }, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 7, - DrainRate = 1, - OverallDifficulty = 5.7f, - ApproachRate = 3.5f, - }, - StarDifficulty = 5.3f, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }); - - AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo - { - Version = "Only Ratings", - Metadata = new BeatmapMetadata - { - Source = "osu!lazer", - Tags = "this beatmap has ratings metrics but not retries or fails", - }, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 6, - DrainRate = 9, - OverallDifficulty = 6, - ApproachRate = 6, - }, - StarDifficulty = 4.8f, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - }, - }); - - AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo - { - Version = "Only Retries and Fails", - Metadata = new BeatmapMetadata - { - Source = "osu!lazer", - Tags = "this beatmap has retries and fails but no ratings", - }, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 3.7f, - DrainRate = 6, - OverallDifficulty = 6, - ApproachRate = 7, - }, - StarDifficulty = 2.91f, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }); - - AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo - { - Version = "No Metrics", - Metadata = new BeatmapMetadata - { - Source = "osu!lazer", - Tags = "this beatmap has no metrics", - }, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 5, - DrainRate = 5, - OverallDifficulty = 5.5f, - ApproachRate = 6.5f, - }, - StarDifficulty = 1.97f, - Metrics = new BeatmapMetrics(), - }); - - AddStep("null beatmap", () => details.Beatmap = null); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Screens.Select; + +namespace osu.Game.Tests.Visual +{ + [Description("PlaySongSelect beatmap details")] + public class TestCaseBeatmapDetails : OsuTestCase + { + public TestCaseBeatmapDetails() + { + BeatmapDetails details; + Add(details = new BeatmapDetails + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(150), + }); + + AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo + { + Version = "All Metrics", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has all the metrics", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 7, + DrainRate = 1, + OverallDifficulty = 5.7f, + ApproachRate = 3.5f, + }, + StarDifficulty = 5.3f, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }); + + AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo + { + Version = "Only Ratings", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has ratings metrics but not retries or fails", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 6, + DrainRate = 9, + OverallDifficulty = 6, + ApproachRate = 6, + }, + StarDifficulty = 4.8f, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + }, + }); + + AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo + { + Version = "Only Retries and Fails", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has retries and fails but no ratings", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 3.7f, + DrainRate = 6, + OverallDifficulty = 6, + ApproachRate = 7, + }, + StarDifficulty = 2.91f, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }); + + AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo + { + Version = "No Metrics", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has no metrics", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 5, + DrainRate = 5, + OverallDifficulty = 5.5f, + ApproachRate = 6.5f, + }, + StarDifficulty = 1.97f, + Metrics = new BeatmapMetrics(), + }); + + AddStep("null beatmap", () => details.Beatmap = null); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index 3ccdaa90d9..790c4cedc3 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -1,163 +1,163 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Select; -using osu.Game.Tests.Beatmaps; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseBeatmapInfoWedge : OsuTestCase - { - private RulesetStore rulesets; - private TestBeatmapInfoWedge infoWedge; - private readonly List beatmaps = new List(); - private readonly Bindable beatmap = new Bindable(); - - [BackgroundDependencyLoader] - private void load(OsuGameBase game, RulesetStore rulesets) - { - this.rulesets = rulesets; - - beatmap.BindTo(game.Beatmap); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(infoWedge = new TestBeatmapInfoWedge - { - Size = new Vector2(0.5f, 245), - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 20 } - }); - - AddStep("show", () => - { - infoWedge.State = Visibility.Visible; - infoWedge.UpdateBeatmap(beatmap); - }); - - AddWaitStep(3); - - AddStep("hide", () => { infoWedge.State = Visibility.Hidden; }); - - AddWaitStep(3); - - AddStep("show", () => { infoWedge.State = Visibility.Visible; }); - - foreach (var rulesetInfo in rulesets.AvailableRulesets) - { - var ruleset = rulesetInfo.CreateInstance(); - beatmaps.Add(createTestBeatmap(rulesetInfo)); - - var name = rulesetInfo.ShortName; - selectBeatmap(name); - - // TODO: adjust cases once more info is shown for other gamemodes - switch (ruleset) - { - case OsuRuleset osu: - testOsuBeatmap(osu); - testInfoLabels(5); - break; - default: - testInfoLabels(2); - break; - } - } - - testNullBeatmap(); - } - - private void testOsuBeatmap(OsuRuleset ruleset) - { - AddAssert("check version", () => infoWedge.Info.VersionLabel.Text == $"{ruleset.ShortName}Version"); - AddAssert("check title", () => infoWedge.Info.TitleLabel.Text == $"{ruleset.ShortName}Source — {ruleset.ShortName}Title"); - AddAssert("check artist", () => infoWedge.Info.ArtistLabel.Text == $"{ruleset.ShortName}Artist"); - AddAssert("check author", () => infoWedge.Info.MapperContainer.Children.OfType().Any(s => s.Text == $"{ruleset.ShortName}Author")); - } - - private void testInfoLabels(int expectedCount) - { - AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any()); - AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount); - } - - private void testNullBeatmap() - { - selectNullBeatmap(); - AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text)); - AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Title); - AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Artist); - AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any()); - AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); - } - - private void selectBeatmap(string name) - { - var infoBefore = infoWedge.Info; - - AddStep($"select {name} beatmap", () => - { - beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name)); - infoWedge.UpdateBeatmap(beatmap); - }); - - AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load"); - } - - private void selectNullBeatmap() - { - AddStep("select null beatmap", () => - { - beatmap.Value = beatmap.Default; - infoWedge.UpdateBeatmap(beatmap); - }); - } - - private Beatmap createTestBeatmap(RulesetInfo ruleset) - { - List objects = new List(); - for (double i = 0; i < 50000; i += 1000) - objects.Add(new HitObject { StartTime = i }); - - return new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - AuthorString = $"{ruleset.ShortName}Author", - Artist = $"{ruleset.ShortName}Artist", - Source = $"{ruleset.ShortName}Source", - Title = $"{ruleset.ShortName}Title" - }, - Ruleset = ruleset, - StarDifficulty = 6, - Version = $"{ruleset.ShortName}Version" - }, - HitObjects = objects - }; - } - - private class TestBeatmapInfoWedge : BeatmapInfoWedge - { - public new BufferedWedgeInfo Info => base.Info; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Select; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseBeatmapInfoWedge : OsuTestCase + { + private RulesetStore rulesets; + private TestBeatmapInfoWedge infoWedge; + private readonly List beatmaps = new List(); + private readonly Bindable beatmap = new Bindable(); + + [BackgroundDependencyLoader] + private void load(OsuGameBase game, RulesetStore rulesets) + { + this.rulesets = rulesets; + + beatmap.BindTo(game.Beatmap); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(infoWedge = new TestBeatmapInfoWedge + { + Size = new Vector2(0.5f, 245), + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 20 } + }); + + AddStep("show", () => + { + infoWedge.State = Visibility.Visible; + infoWedge.UpdateBeatmap(beatmap); + }); + + AddWaitStep(3); + + AddStep("hide", () => { infoWedge.State = Visibility.Hidden; }); + + AddWaitStep(3); + + AddStep("show", () => { infoWedge.State = Visibility.Visible; }); + + foreach (var rulesetInfo in rulesets.AvailableRulesets) + { + var ruleset = rulesetInfo.CreateInstance(); + beatmaps.Add(createTestBeatmap(rulesetInfo)); + + var name = rulesetInfo.ShortName; + selectBeatmap(name); + + // TODO: adjust cases once more info is shown for other gamemodes + switch (ruleset) + { + case OsuRuleset osu: + testOsuBeatmap(osu); + testInfoLabels(5); + break; + default: + testInfoLabels(2); + break; + } + } + + testNullBeatmap(); + } + + private void testOsuBeatmap(OsuRuleset ruleset) + { + AddAssert("check version", () => infoWedge.Info.VersionLabel.Text == $"{ruleset.ShortName}Version"); + AddAssert("check title", () => infoWedge.Info.TitleLabel.Text == $"{ruleset.ShortName}Source — {ruleset.ShortName}Title"); + AddAssert("check artist", () => infoWedge.Info.ArtistLabel.Text == $"{ruleset.ShortName}Artist"); + AddAssert("check author", () => infoWedge.Info.MapperContainer.Children.OfType().Any(s => s.Text == $"{ruleset.ShortName}Author")); + } + + private void testInfoLabels(int expectedCount) + { + AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any()); + AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount); + } + + private void testNullBeatmap() + { + selectNullBeatmap(); + AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text)); + AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Title); + AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Artist); + AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any()); + AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); + } + + private void selectBeatmap(string name) + { + var infoBefore = infoWedge.Info; + + AddStep($"select {name} beatmap", () => + { + beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name)); + infoWedge.UpdateBeatmap(beatmap); + }); + + AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load"); + } + + private void selectNullBeatmap() + { + AddStep("select null beatmap", () => + { + beatmap.Value = beatmap.Default; + infoWedge.UpdateBeatmap(beatmap); + }); + } + + private Beatmap createTestBeatmap(RulesetInfo ruleset) + { + List objects = new List(); + for (double i = 0; i < 50000; i += 1000) + objects.Add(new HitObject { StartTime = i }); + + return new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + AuthorString = $"{ruleset.ShortName}Author", + Artist = $"{ruleset.ShortName}Artist", + Source = $"{ruleset.ShortName}Source", + Title = $"{ruleset.ShortName}Title" + }, + Ruleset = ruleset, + StarDifficulty = 6, + Version = $"{ruleset.ShortName}Version" + }, + HitObjects = objects + }; + } + + private class TestBeatmapInfoWedge : BeatmapInfoWedge + { + public new BufferedWedgeInfo Info => base.Info; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs index cfeed4b1f0..ffa433ef11 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Game.Graphics; -using osu.Game.Screens.Select.Options; -using OpenTK.Graphics; -using OpenTK.Input; - -namespace osu.Game.Tests.Visual -{ - [Description("bottom beatmap details")] - public class TestCaseBeatmapOptionsOverlay : OsuTestCase - { - public TestCaseBeatmapOptionsOverlay() - { - var overlay = new BeatmapOptionsOverlay(); - - overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, Color4.Purple, null, Key.Number1); - overlay.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, Color4.Purple, null, Key.Number2); - overlay.AddButton(@"Edit", @"Beatmap", FontAwesome.fa_pencil, Color4.Yellow, null, Key.Number3); - overlay.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, Color4.Pink, null, Key.Number4, float.MaxValue); - - Add(overlay); - - AddStep(@"Toggle", overlay.ToggleVisibility); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Game.Graphics; +using osu.Game.Screens.Select.Options; +using OpenTK.Graphics; +using OpenTK.Input; + +namespace osu.Game.Tests.Visual +{ + [Description("bottom beatmap details")] + public class TestCaseBeatmapOptionsOverlay : OsuTestCase + { + public TestCaseBeatmapOptionsOverlay() + { + var overlay = new BeatmapOptionsOverlay(); + + overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, Color4.Purple, null, Key.Number1); + overlay.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, Color4.Purple, null, Key.Number2); + overlay.AddButton(@"Edit", @"Beatmap", FontAwesome.fa_pencil, Color4.Yellow, null, Key.Number3); + overlay.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, Color4.Pink, null, Key.Number4, float.MaxValue); + + Add(overlay); + + AddStep(@"Toggle", overlay.ToggleVisibility); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs index 5ce5eb222e..6cb6a342a8 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs @@ -1,313 +1,313 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.MathUtils; -using osu.Game.Graphics; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.BeatmapSet.Scores; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Scoring; -using osu.Game.Users; -using System.Collections.Generic; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Tests.Visual -{ - [System.ComponentModel.Description("in BeatmapOverlay")] - public class TestCaseBeatmapScoresContainer : OsuTestCase - { - private readonly IEnumerable scores; - private readonly IEnumerable anotherScores; - private readonly OnlineScore topScore; - private readonly Box background; - - public TestCaseBeatmapScoresContainer() - { - Container container; - ScoresContainer scoresContainer; - - Child = container = new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Width = 0.8f, - Children = new Drawable[] - { - background = new Box { RelativeSizeAxes = Axes.Both }, - scoresContainer = new ScoresContainer(), - } - }; - - AddStep("scores pack 1", () => scoresContainer.Scores = scores); - AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); - AddStep("only top score", () => scoresContainer.Scores = new[] { topScore }); - AddStep("remove scores", scoresContainer.CleanAllScores); - AddStep("turn on loading", () => scoresContainer.IsLoading = true); - AddStep("turn off loading", () => scoresContainer.IsLoading = false); - AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); - AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); - - scores = new[] - { - new OnlineScore - { - User = new User - { - Id = 6602580, - Username = @"waaiiru", - Country = new Country - { - FullName = @"Spain", - FlagName = @"ES", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), - new OsuModHardRock(), - }, - Rank = ScoreRank.XH, - TotalScore = 1234567890, - Accuracy = 1, - }, - new OnlineScore - { - User = new User - { - Id = 4608074, - Username = @"Skycries", - Country = new Country - { - FullName = @"Brazil", - FlagName = @"BR", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), - }, - Rank = ScoreRank.S, - TotalScore = 1234789, - Accuracy = 0.9997, - }, - new OnlineScore - { - User = new User - { - Id = 1014222, - Username = @"eLy", - Country = new Country - { - FullName = @"Japan", - FlagName = @"JP", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModHidden(), - }, - Rank = ScoreRank.B, - TotalScore = 12345678, - Accuracy = 0.9854, - }, - new OnlineScore - { - User = new User - { - Id = 1541390, - Username = @"Toukai", - Country = new Country - { - FullName = @"Canada", - FlagName = @"CA", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - }, - Rank = ScoreRank.C, - TotalScore = 1234567, - Accuracy = 0.8765, - }, - new OnlineScore - { - User = new User - { - Id = 7151382, - Username = @"Mayuri Hana", - Country = new Country - { - FullName = @"Thailand", - FlagName = @"TH", - }, - }, - Rank = ScoreRank.F, - TotalScore = 123456, - Accuracy = 0.6543, - }, - }; - foreach(var s in scores) - { - s.Statistics.Add(HitResult.Great, RNG.Next(2000)); - s.Statistics.Add(HitResult.Good, RNG.Next(2000)); - s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); - } - - anotherScores = new[] - { - new OnlineScore - { - User = new User - { - Id = 4608074, - Username = @"Skycries", - Country = new Country - { - FullName = @"Brazil", - FlagName = @"BR", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), - }, - Rank = ScoreRank.S, - TotalScore = 1234789, - Accuracy = 0.9997, - }, - new OnlineScore - { - User = new User - { - Id = 6602580, - Username = @"waaiiru", - Country = new Country - { - FullName = @"Spain", - FlagName = @"ES", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), - new OsuModHardRock(), - }, - Rank = ScoreRank.XH, - TotalScore = 1234567890, - Accuracy = 1, - }, - new OnlineScore - { - User = new User - { - Id = 7151382, - Username = @"Mayuri Hana", - Country = new Country - { - FullName = @"Thailand", - FlagName = @"TH", - }, - }, - Rank = ScoreRank.F, - TotalScore = 123456, - Accuracy = 0.6543, - }, - new OnlineScore - { - User = new User - { - Id = 1014222, - Username = @"eLy", - Country = new Country - { - FullName = @"Japan", - FlagName = @"JP", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModHidden(), - }, - Rank = ScoreRank.B, - TotalScore = 12345678, - Accuracy = 0.9854, - }, - new OnlineScore - { - User = new User - { - Id = 1541390, - Username = @"Toukai", - Country = new Country - { - FullName = @"Canada", - FlagName = @"CA", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - }, - Rank = ScoreRank.C, - TotalScore = 1234567, - Accuracy = 0.8765, - }, - }; - foreach (var s in anotherScores) - { - s.Statistics.Add(HitResult.Great, RNG.Next(2000)); - s.Statistics.Add(HitResult.Good, RNG.Next(2000)); - s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); - } - - topScore = new OnlineScore - { - User = new User - { - Id = 2705430, - Username = @"Mooha", - Country = new Country - { - FullName = @"France", - FlagName = @"FR", - }, - }, - Mods = new Mod[] - { - new OsuModDoubleTime(), - new OsuModFlashlight(), - new OsuModHardRock(), - }, - Rank = ScoreRank.B, - TotalScore = 987654321, - Accuracy = 0.8487, - }; - topScore.Statistics.Add(HitResult.Great, RNG.Next(2000)); - topScore.Statistics.Add(HitResult.Good, RNG.Next(2000)); - topScore.Statistics.Add(HitResult.Meh, RNG.Next(2000)); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.Gray2; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.MathUtils; +using osu.Game.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.BeatmapSet.Scores; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Users; +using System.Collections.Generic; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Tests.Visual +{ + [System.ComponentModel.Description("in BeatmapOverlay")] + public class TestCaseBeatmapScoresContainer : OsuTestCase + { + private readonly IEnumerable scores; + private readonly IEnumerable anotherScores; + private readonly OnlineScore topScore; + private readonly Box background; + + public TestCaseBeatmapScoresContainer() + { + Container container; + ScoresContainer scoresContainer; + + Child = container = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Width = 0.8f, + Children = new Drawable[] + { + background = new Box { RelativeSizeAxes = Axes.Both }, + scoresContainer = new ScoresContainer(), + } + }; + + AddStep("scores pack 1", () => scoresContainer.Scores = scores); + AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); + AddStep("only top score", () => scoresContainer.Scores = new[] { topScore }); + AddStep("remove scores", scoresContainer.CleanAllScores); + AddStep("turn on loading", () => scoresContainer.IsLoading = true); + AddStep("turn off loading", () => scoresContainer.IsLoading = false); + AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); + AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); + + scores = new[] + { + new OnlineScore + { + User = new User + { + Id = 6602580, + Username = @"waaiiru", + Country = new Country + { + FullName = @"Spain", + FlagName = @"ES", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModHidden(), + new OsuModFlashlight(), + new OsuModHardRock(), + }, + Rank = ScoreRank.XH, + TotalScore = 1234567890, + Accuracy = 1, + }, + new OnlineScore + { + User = new User + { + Id = 4608074, + Username = @"Skycries", + Country = new Country + { + FullName = @"Brazil", + FlagName = @"BR", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModHidden(), + new OsuModFlashlight(), + }, + Rank = ScoreRank.S, + TotalScore = 1234789, + Accuracy = 0.9997, + }, + new OnlineScore + { + User = new User + { + Id = 1014222, + Username = @"eLy", + Country = new Country + { + FullName = @"Japan", + FlagName = @"JP", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModHidden(), + }, + Rank = ScoreRank.B, + TotalScore = 12345678, + Accuracy = 0.9854, + }, + new OnlineScore + { + User = new User + { + Id = 1541390, + Username = @"Toukai", + Country = new Country + { + FullName = @"Canada", + FlagName = @"CA", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + }, + Rank = ScoreRank.C, + TotalScore = 1234567, + Accuracy = 0.8765, + }, + new OnlineScore + { + User = new User + { + Id = 7151382, + Username = @"Mayuri Hana", + Country = new Country + { + FullName = @"Thailand", + FlagName = @"TH", + }, + }, + Rank = ScoreRank.F, + TotalScore = 123456, + Accuracy = 0.6543, + }, + }; + foreach(var s in scores) + { + s.Statistics.Add(HitResult.Great, RNG.Next(2000)); + s.Statistics.Add(HitResult.Good, RNG.Next(2000)); + s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); + } + + anotherScores = new[] + { + new OnlineScore + { + User = new User + { + Id = 4608074, + Username = @"Skycries", + Country = new Country + { + FullName = @"Brazil", + FlagName = @"BR", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModHidden(), + new OsuModFlashlight(), + }, + Rank = ScoreRank.S, + TotalScore = 1234789, + Accuracy = 0.9997, + }, + new OnlineScore + { + User = new User + { + Id = 6602580, + Username = @"waaiiru", + Country = new Country + { + FullName = @"Spain", + FlagName = @"ES", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModHidden(), + new OsuModFlashlight(), + new OsuModHardRock(), + }, + Rank = ScoreRank.XH, + TotalScore = 1234567890, + Accuracy = 1, + }, + new OnlineScore + { + User = new User + { + Id = 7151382, + Username = @"Mayuri Hana", + Country = new Country + { + FullName = @"Thailand", + FlagName = @"TH", + }, + }, + Rank = ScoreRank.F, + TotalScore = 123456, + Accuracy = 0.6543, + }, + new OnlineScore + { + User = new User + { + Id = 1014222, + Username = @"eLy", + Country = new Country + { + FullName = @"Japan", + FlagName = @"JP", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModHidden(), + }, + Rank = ScoreRank.B, + TotalScore = 12345678, + Accuracy = 0.9854, + }, + new OnlineScore + { + User = new User + { + Id = 1541390, + Username = @"Toukai", + Country = new Country + { + FullName = @"Canada", + FlagName = @"CA", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + }, + Rank = ScoreRank.C, + TotalScore = 1234567, + Accuracy = 0.8765, + }, + }; + foreach (var s in anotherScores) + { + s.Statistics.Add(HitResult.Great, RNG.Next(2000)); + s.Statistics.Add(HitResult.Good, RNG.Next(2000)); + s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); + } + + topScore = new OnlineScore + { + User = new User + { + Id = 2705430, + Username = @"Mooha", + Country = new Country + { + FullName = @"France", + FlagName = @"FR", + }, + }, + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModFlashlight(), + new OsuModHardRock(), + }, + Rank = ScoreRank.B, + TotalScore = 987654321, + Accuracy = 0.8487, + }; + topScore.Statistics.Add(HitResult.Great, RNG.Next(2000)); + topScore.Statistics.Add(HitResult.Good, RNG.Next(2000)); + topScore.Statistics.Add(HitResult.Meh, RNG.Next(2000)); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.Gray2; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs index 6605c61026..69955a90c4 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs @@ -1,379 +1,379 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Overlays; -using osu.Game.Rulesets; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseBeatmapSetOverlay : OsuTestCase - { - private readonly BeatmapSetOverlay overlay; - - public TestCaseBeatmapSetOverlay() - { - Add(overlay = new BeatmapSetOverlay()); - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - var mania = rulesets.GetRuleset(3); - var taiko = rulesets.GetRuleset(1); - - AddStep(@"show first", () => - { - overlay.ShowBeatmapSet(new BeatmapSetInfo - { - Metadata = new BeatmapMetadata - { - Title = @"Lachryma ", - Artist = @"Kaneko Chiharu", - Source = @"SOUND VOLTEX III GRAVITY WARS", - Tags = @"sdvx grace the 5th kac original song contest konami bemani", - Author = new User - { - Username = @"Fresh Chicken", - Id = 3984370, - }, - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Preview = @"https://b.ppy.sh/preview/415886.mp3", - PlayCount = 681380, - FavouriteCount = 356, - Submitted = new DateTime(2016, 2, 10), - Ranked = new DateTime(2016, 6, 19), - Status = BeatmapSetOnlineStatus.Ranked, - BPM = 236, - HasVideo = true, - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778", - }, - }, - Beatmaps = new List - { - new BeatmapInfo - { - StarDifficulty = 1.36, - Version = @"BASIC", - Ruleset = mania, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 6.5f, - OverallDifficulty = 6.5f, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 115000, - CircleCount = 265, - SliderCount = 71, - PlayCount = 47906, - PassCount = 19899, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - new BeatmapInfo - { - StarDifficulty = 2.22, - Version = @"NOVICE", - Ruleset = mania, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 7, - OverallDifficulty = 7, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 118000, - CircleCount = 592, - SliderCount = 62, - PlayCount = 162021, - PassCount = 72116, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - new BeatmapInfo - { - StarDifficulty = 3.49, - Version = @"ADVANCED", - Ruleset = mania, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 7.5f, - OverallDifficulty = 7.5f, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 118000, - CircleCount = 1042, - SliderCount = 79, - PlayCount = 225178, - PassCount = 73001, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - new BeatmapInfo - { - StarDifficulty = 4.24, - Version = @"EXHAUST", - Ruleset = mania, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 8, - OverallDifficulty = 8, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 118000, - CircleCount = 1352, - SliderCount = 69, - PlayCount = 131545, - PassCount = 42703, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - new BeatmapInfo - { - StarDifficulty = 5.26, - Version = @"GRAVITY", - Ruleset = mania, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 8.5f, - OverallDifficulty = 8.5f, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 118000, - CircleCount = 1730, - SliderCount = 115, - PlayCount = 117673, - PassCount = 24241, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - }, - }); - }); - - AddStep(@"show second", () => - { - overlay.ShowBeatmapSet(new BeatmapSetInfo - { - Metadata = new BeatmapMetadata - { - Title = @"Soumatou Labyrinth", - Artist = @"Yunomi with Momobako&miko", - Tags = @"mmbk.com yuzu__rinrin charlotte", - Author = new User - { - Username = @"komasy", - Id = 1980256, - }, - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Preview = @"https://b.ppy.sh/preview/625493.mp3", - PlayCount = 22996, - FavouriteCount = 58, - Submitted = new DateTime(2016, 6, 11), - Ranked = new DateTime(2016, 7, 12), - Status = BeatmapSetOnlineStatus.Pending, - BPM = 160, - HasVideo = false, - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472", - }, - }, - Beatmaps = new List - { - new BeatmapInfo - { - StarDifficulty = 1.40, - Version = @"yzrin's Kantan", - Ruleset = taiko, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 2, - DrainRate = 7, - OverallDifficulty = 3, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 262, - SliderCount = 0, - PlayCount = 3952, - PassCount = 1373, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - new BeatmapInfo - { - StarDifficulty = 2.23, - Version = @"Futsuu", - Ruleset = taiko, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 2, - DrainRate = 6, - OverallDifficulty = 4, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 464, - SliderCount = 0, - PlayCount = 4833, - PassCount = 920, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - new BeatmapInfo - { - StarDifficulty = 3.19, - Version = @"Muzukashii", - Ruleset = taiko, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 2, - DrainRate = 6, - OverallDifficulty = 5, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 712, - SliderCount = 0, - PlayCount = 4405, - PassCount = 854, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - new BeatmapInfo - { - StarDifficulty = 3.97, - Version = @"Charlotte's Oni", - Ruleset = taiko, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 5, - DrainRate = 6, - OverallDifficulty = 5.5f, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 943, - SliderCount = 0, - PlayCount = 3950, - PassCount = 693, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - new BeatmapInfo - { - StarDifficulty = 5.08, - Version = @"Labyrinth Oni", - Ruleset = taiko, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 5, - DrainRate = 5, - OverallDifficulty = 6, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 1068, - SliderCount = 0, - PlayCount = 5856, - PassCount = 1207, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }, - }, - }); - }); - - AddStep(@"hide", overlay.Hide); - AddStep(@"show without reload", overlay.Show); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Overlays; +using osu.Game.Rulesets; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseBeatmapSetOverlay : OsuTestCase + { + private readonly BeatmapSetOverlay overlay; + + public TestCaseBeatmapSetOverlay() + { + Add(overlay = new BeatmapSetOverlay()); + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + var mania = rulesets.GetRuleset(3); + var taiko = rulesets.GetRuleset(1); + + AddStep(@"show first", () => + { + overlay.ShowBeatmapSet(new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = @"Lachryma ", + Artist = @"Kaneko Chiharu", + Source = @"SOUND VOLTEX III GRAVITY WARS", + Tags = @"sdvx grace the 5th kac original song contest konami bemani", + Author = new User + { + Username = @"Fresh Chicken", + Id = 3984370, + }, + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Preview = @"https://b.ppy.sh/preview/415886.mp3", + PlayCount = 681380, + FavouriteCount = 356, + Submitted = new DateTime(2016, 2, 10), + Ranked = new DateTime(2016, 6, 19), + Status = BeatmapSetOnlineStatus.Ranked, + BPM = 236, + HasVideo = true, + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778", + }, + }, + Beatmaps = new List + { + new BeatmapInfo + { + StarDifficulty = 1.36, + Version = @"BASIC", + Ruleset = mania, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 6.5f, + OverallDifficulty = 6.5f, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 115000, + CircleCount = 265, + SliderCount = 71, + PlayCount = 47906, + PassCount = 19899, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 2.22, + Version = @"NOVICE", + Ruleset = mania, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 7, + OverallDifficulty = 7, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 118000, + CircleCount = 592, + SliderCount = 62, + PlayCount = 162021, + PassCount = 72116, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 3.49, + Version = @"ADVANCED", + Ruleset = mania, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 7.5f, + OverallDifficulty = 7.5f, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 118000, + CircleCount = 1042, + SliderCount = 79, + PlayCount = 225178, + PassCount = 73001, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 4.24, + Version = @"EXHAUST", + Ruleset = mania, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 8, + OverallDifficulty = 8, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 118000, + CircleCount = 1352, + SliderCount = 69, + PlayCount = 131545, + PassCount = 42703, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 5.26, + Version = @"GRAVITY", + Ruleset = mania, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 8.5f, + OverallDifficulty = 8.5f, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 118000, + CircleCount = 1730, + SliderCount = 115, + PlayCount = 117673, + PassCount = 24241, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + }, + }); + }); + + AddStep(@"show second", () => + { + overlay.ShowBeatmapSet(new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = @"Soumatou Labyrinth", + Artist = @"Yunomi with Momobako&miko", + Tags = @"mmbk.com yuzu__rinrin charlotte", + Author = new User + { + Username = @"komasy", + Id = 1980256, + }, + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Preview = @"https://b.ppy.sh/preview/625493.mp3", + PlayCount = 22996, + FavouriteCount = 58, + Submitted = new DateTime(2016, 6, 11), + Ranked = new DateTime(2016, 7, 12), + Status = BeatmapSetOnlineStatus.Pending, + BPM = 160, + HasVideo = false, + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472", + }, + }, + Beatmaps = new List + { + new BeatmapInfo + { + StarDifficulty = 1.40, + Version = @"yzrin's Kantan", + Ruleset = taiko, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 2, + DrainRate = 7, + OverallDifficulty = 3, + ApproachRate = 10, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 193000, + CircleCount = 262, + SliderCount = 0, + PlayCount = 3952, + PassCount = 1373, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 2.23, + Version = @"Futsuu", + Ruleset = taiko, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 2, + DrainRate = 6, + OverallDifficulty = 4, + ApproachRate = 10, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 193000, + CircleCount = 464, + SliderCount = 0, + PlayCount = 4833, + PassCount = 920, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 3.19, + Version = @"Muzukashii", + Ruleset = taiko, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 2, + DrainRate = 6, + OverallDifficulty = 5, + ApproachRate = 10, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 193000, + CircleCount = 712, + SliderCount = 0, + PlayCount = 4405, + PassCount = 854, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 3.97, + Version = @"Charlotte's Oni", + Ruleset = taiko, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 5, + DrainRate = 6, + OverallDifficulty = 5.5f, + ApproachRate = 10, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 193000, + CircleCount = 943, + SliderCount = 0, + PlayCount = 3950, + PassCount = 693, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + new BeatmapInfo + { + StarDifficulty = 5.08, + Version = @"Labyrinth Oni", + Ruleset = taiko, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 5, + DrainRate = 5, + OverallDifficulty = 6, + ApproachRate = 10, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 193000, + CircleCount = 1068, + SliderCount = 0, + PlayCount = 5856, + PassCount = 1207, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }, + }, + }); + }); + + AddStep(@"hide", overlay.Hide); + AddStep(@"show without reload", overlay.Show); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs b/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs index e3cef06f2f..73a97c6269 100644 --- a/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs +++ b/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseBreadcrumbs : OsuTestCase - { - private readonly BreadcrumbControl breadcrumbs; - - public TestCaseBreadcrumbs() - { - - Add(breadcrumbs = new BreadcrumbControl - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Width = 0.5f, - }); - - AddStep(@"first", () => breadcrumbs.Current.Value = BreadcrumbTab.Click); - AddStep(@"second", () => breadcrumbs.Current.Value = BreadcrumbTab.The); - AddStep(@"third", () => breadcrumbs.Current.Value = BreadcrumbTab.Circles); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - breadcrumbs.StripColour = colours.Blue; - } - - private enum BreadcrumbTab - { - Click, - The, - Circles, - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseBreadcrumbs : OsuTestCase + { + private readonly BreadcrumbControl breadcrumbs; + + public TestCaseBreadcrumbs() + { + + Add(breadcrumbs = new BreadcrumbControl + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Width = 0.5f, + }); + + AddStep(@"first", () => breadcrumbs.Current.Value = BreadcrumbTab.Click); + AddStep(@"second", () => breadcrumbs.Current.Value = BreadcrumbTab.The); + AddStep(@"third", () => breadcrumbs.Current.Value = BreadcrumbTab.Circles); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + breadcrumbs.StripColour = colours.Blue; + } + + private enum BreadcrumbTab + { + Click, + The, + Circles, + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs index 51b8c61963..a6a6130a8f 100644 --- a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs @@ -1,91 +1,91 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Timing; -using osu.Game.Beatmaps.Timing; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Game.Screens.Play; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseBreakOverlay : OsuTestCase - { - private readonly BreakOverlay breakOverlay; - - public TestCaseBreakOverlay() - { - Clock = new FramedClock(); - - Child = breakOverlay = new BreakOverlay(true); - - AddStep("2s break", () => startBreak(2000)); - AddStep("5s break", () => startBreak(5000)); - AddStep("10s break", () => startBreak(10000)); - AddStep("15s break", () => startBreak(15000)); - AddStep("2s, 2s", startMultipleBreaks); - AddStep("0.5s, 0.7s, 1s, 2s", startAnotherMultipleBreaks); - } - - private void startBreak(double duration) - { - breakOverlay.Breaks = new List - { - new BreakPeriod - { - StartTime = Clock.CurrentTime, - EndTime = Clock.CurrentTime + duration, - } - }; - } - - private void startMultipleBreaks() - { - double currentTime = Clock.CurrentTime; - - breakOverlay.Breaks = new List - { - new BreakPeriod - { - StartTime = currentTime, - EndTime = currentTime + 2000, - }, - new BreakPeriod - { - StartTime = currentTime + 4000, - EndTime = currentTime + 6000, - } - }; - } - - private void startAnotherMultipleBreaks() - { - double currentTime = Clock.CurrentTime; - - breakOverlay.Breaks = new List - { - new BreakPeriod // Duration is less than 650 - too short to appear - { - StartTime = currentTime, - EndTime = currentTime + 500, - }, - new BreakPeriod - { - StartTime = currentTime + 1500, - EndTime = currentTime + 2200, - }, - new BreakPeriod - { - StartTime = currentTime + 3200, - EndTime = currentTime + 4200, - }, - new BreakPeriod - { - StartTime = currentTime + 5200, - EndTime = currentTime + 7200, - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Timing; +using osu.Game.Beatmaps.Timing; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseBreakOverlay : OsuTestCase + { + private readonly BreakOverlay breakOverlay; + + public TestCaseBreakOverlay() + { + Clock = new FramedClock(); + + Child = breakOverlay = new BreakOverlay(true); + + AddStep("2s break", () => startBreak(2000)); + AddStep("5s break", () => startBreak(5000)); + AddStep("10s break", () => startBreak(10000)); + AddStep("15s break", () => startBreak(15000)); + AddStep("2s, 2s", startMultipleBreaks); + AddStep("0.5s, 0.7s, 1s, 2s", startAnotherMultipleBreaks); + } + + private void startBreak(double duration) + { + breakOverlay.Breaks = new List + { + new BreakPeriod + { + StartTime = Clock.CurrentTime, + EndTime = Clock.CurrentTime + duration, + } + }; + } + + private void startMultipleBreaks() + { + double currentTime = Clock.CurrentTime; + + breakOverlay.Breaks = new List + { + new BreakPeriod + { + StartTime = currentTime, + EndTime = currentTime + 2000, + }, + new BreakPeriod + { + StartTime = currentTime + 4000, + EndTime = currentTime + 6000, + } + }; + } + + private void startAnotherMultipleBreaks() + { + double currentTime = Clock.CurrentTime; + + breakOverlay.Breaks = new List + { + new BreakPeriod // Duration is less than 650 - too short to appear + { + StartTime = currentTime, + EndTime = currentTime + 500, + }, + new BreakPeriod + { + StartTime = currentTime + 1500, + EndTime = currentTime + 2200, + }, + new BreakPeriod + { + StartTime = currentTime + 3200, + EndTime = currentTime + 4200, + }, + new BreakPeriod + { + StartTime = currentTime + 5200, + EndTime = currentTime + 7200, + } + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs index 93740593cb..5eb81cdf9f 100644 --- a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs +++ b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Shapes; -using osu.Game.Screens.Menu; -using OpenTK.Graphics; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseButtonSystem : OsuTestCase - { - public TestCaseButtonSystem() - { - OsuLogo logo; - ButtonSystem buttons; - - Children = new Drawable[] - { - new Box - { - Colour = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke), - RelativeSizeAxes = Axes.Both, - }, - buttons = new ButtonSystem(), - logo = new OsuLogo() - }; - - buttons.SetOsuLogo(logo); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Game.Screens.Menu; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseButtonSystem : OsuTestCase + { + public TestCaseButtonSystem() + { + OsuLogo logo; + ButtonSystem buttons; + + Children = new Drawable[] + { + new Box + { + Colour = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke), + RelativeSizeAxes = Axes.Both, + }, + buttons = new ButtonSystem(), + logo = new OsuLogo() + }; + + buttons.SetOsuLogo(logo); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs index 048106da26..c03b12bdc1 100644 --- a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Framework.Graphics.Containers; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual -{ - [Description("Testing chat api and overlay")] - public class TestCaseChatDisplay : OsuTestCase - { - public TestCaseChatDisplay() - { - Add(new ChatOverlay - { - State = Visibility.Visible - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual +{ + [Description("Testing chat api and overlay")] + public class TestCaseChatDisplay : OsuTestCase + { + public TestCaseChatDisplay() + { + Add(new ChatOverlay + { + State = Visibility.Visible + }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index 786fcb64ab..89b1c52010 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -1,219 +1,219 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Online.Chat; -using osu.Game.Overlays.Chat; -using osu.Game.Users; -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseChatLink : OsuTestCase - { - private readonly TestChatLineContainer textContainer; - private Color4 linkColour; - - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ChatLine), - typeof(Message), - typeof(LinkFlowContainer), - typeof(DummyEchoMessage), - typeof(LocalEchoMessage), - typeof(MessageFormatter) - }; - - private DependencyContainer dependencies; - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); - - public TestCaseChatLink() - { - Add(textContainer = new TestChatLineContainer - { - Padding = new MarginPadding { Left = 20, Right = 20 }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - linkColour = colours.Blue; - dependencies.Cache(new ChatOverlay - { - AvailableChannels = - { - new Channel { Name = "#english" }, - new Channel { Name = "#japanese" } - } - }); - - testLinksGeneral(); - testEcho(); - } - - private void clear() => AddStep("clear messages", textContainer.Clear); - - private void addMessageWithChecks(string text, int linkAmount = 0, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) - { - int index = textContainer.Count + 1; - var newLine = new ChatLine(new DummyMessage(text, isAction, isImportant, index)); - textContainer.Add(newLine); - - AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links.Count == linkAmount); - AddAssert($"msg #{index} has the right action", hasExpectedActions); - AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); - AddAssert($"msg #{index} shows {linkAmount} link(s)", isShowingLinks); - - bool hasExpectedActions() - { - var expectedActionsList = expectedActions.ToList(); - - if (expectedActionsList.Count != newLine.Message.Links.Count) - return false; - - for (int i = 0; i < newLine.Message.Links.Count; i++) - { - var action = newLine.Message.Links[i].Action; - if (action != expectedActions[i]) return false; - } - - return true; - } - - bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font == "Exo2.0-MediumItalic"); - - bool isShowingLinks() - { - bool hasBackground = !string.IsNullOrEmpty(newLine.Message.Sender.Colour); - - Color4 textColour = isAction && hasBackground ? OsuColour.FromHex(newLine.Message.Sender.Colour) : Color4.White; - - var linkCompilers = newLine.ContentFlow.Where(d => d is DrawableLinkCompiler).ToList(); - var linkSprites = linkCompilers.SelectMany(comp => ((DrawableLinkCompiler)comp).Parts); - - return linkSprites.All(d => d.Colour == linkColour) - && newLine.ContentFlow.Except(linkSprites.Concat(linkCompilers)).All(d => d.Colour == textColour); - } - } - - private void testLinksGeneral() - { - addMessageWithChecks("test!"); - addMessageWithChecks("osu.ppy.sh!"); - addMessageWithChecks("https://osu.ppy.sh!", 1, expectedActions: LinkAction.External); - addMessageWithChecks("00:12:345 (1,2) - Test?", 1, expectedActions: LinkAction.OpenEditorTimestamp); - addMessageWithChecks("Wiki link for tasty [[Performance Points]]", 1, expectedActions: LinkAction.External); - addMessageWithChecks("(osu forums)[https://osu.ppy.sh/forum] (old link format)", 1, expectedActions: LinkAction.External); - addMessageWithChecks("[https://osu.ppy.sh/home New site] (new link format)", 1, expectedActions: LinkAction.External); - addMessageWithChecks("[osu forums](https://osu.ppy.sh/forum) (new link format 2)", 1, expectedActions: LinkAction.External); - addMessageWithChecks("[https://osu.ppy.sh/home This is only a link to the new osu webpage but this is supposed to test word wrap.]", 1, expectedActions: LinkAction.External); - addMessageWithChecks("is now listening to [https://osu.ppy.sh/s/93523 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmapSet); - addMessageWithChecks("is now playing [https://osu.ppy.sh/b/252238 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmap); - addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3, - expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); - // note that there's 0 links here (they get removed if a channel is not found) - addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); - addMessageWithChecks("I am important!", 0, false, true); - addMessageWithChecks("feels important", 0, true, true); - addMessageWithChecks("likes to post this [https://osu.ppy.sh/home link].", 1, true, true, expectedActions: LinkAction.External); - addMessageWithChecks("Join my multiplayer game osump://12346.", 1, expectedActions: LinkAction.JoinMultiplayerMatch); - addMessageWithChecks("Join my [multiplayer game](osump://12346).", 1, expectedActions: LinkAction.JoinMultiplayerMatch); - addMessageWithChecks("Join my [#english](osu://chan/#english).", 1, expectedActions: LinkAction.OpenChannel); - addMessageWithChecks("Join my osu://chan/#english.", 1, expectedActions: LinkAction.OpenChannel); - addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel }); - addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel); - } - - private void testEcho() - { - int echoCounter = 0; - - addEchoWithWait("sent!", "received!"); - addEchoWithWait("https://osu.ppy.sh/home", null, 500); - addEchoWithWait("[https://osu.ppy.sh/forum let's try multiple words too!]"); - addEchoWithWait("(long loading times! clickable while loading?)[https://osu.ppy.sh/home]", null, 5000); - - void addEchoWithWait(string text, string completeText = null, double delay = 250) - { - var newLine = new ChatLine(new DummyEchoMessage(text)); - - AddStep($"send msg #{++echoCounter} after {delay}ms", () => - { - textContainer.Add(newLine); - Scheduler.AddDelayed(() => newLine.Message = new DummyMessage(completeText ?? text), delay); - }); - - AddUntilStep(() => textContainer.All(line => line.Message is DummyMessage), $"wait for msg #{echoCounter}"); - } - } - - private class DummyEchoMessage : LocalEchoMessage - { - public DummyEchoMessage(string text) - { - Content = text; - Timestamp = DateTimeOffset.Now; - Sender = DummyMessage.TEST_SENDER; - } - } - - private class DummyMessage : Message - { - private static long messageCounter; - - internal static readonly User TEST_SENDER_BACKGROUND = new User - { - Username = @"i-am-important", - Id = 42, - Colour = "#250cc9", - }; - - internal static readonly User TEST_SENDER = new User - { - Username = @"Somebody", - Id = 1, - }; - - public new DateTimeOffset Timestamp = DateTimeOffset.Now; - - public DummyMessage(string text, bool isAction = false, bool isImportant = false, int number = 0) - : base(messageCounter++) - { - Content = text; - IsAction = isAction; - Sender = new User - { - Username = $"User {number}", - Id = number, - Colour = isImportant ? "#250cc9" : null, - }; - } - } - - private class TestChatLineContainer : FillFlowContainer - { - protected override int Compare(Drawable x, Drawable y) - { - var xC = (ChatLine)x; - var yC = (ChatLine)y; - - return xC.Message.CompareTo(yC.Message); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat; +using osu.Game.Users; +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseChatLink : OsuTestCase + { + private readonly TestChatLineContainer textContainer; + private Color4 linkColour; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ChatLine), + typeof(Message), + typeof(LinkFlowContainer), + typeof(DummyEchoMessage), + typeof(LocalEchoMessage), + typeof(MessageFormatter) + }; + + private DependencyContainer dependencies; + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); + + public TestCaseChatLink() + { + Add(textContainer = new TestChatLineContainer + { + Padding = new MarginPadding { Left = 20, Right = 20 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + linkColour = colours.Blue; + dependencies.Cache(new ChatOverlay + { + AvailableChannels = + { + new Channel { Name = "#english" }, + new Channel { Name = "#japanese" } + } + }); + + testLinksGeneral(); + testEcho(); + } + + private void clear() => AddStep("clear messages", textContainer.Clear); + + private void addMessageWithChecks(string text, int linkAmount = 0, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) + { + int index = textContainer.Count + 1; + var newLine = new ChatLine(new DummyMessage(text, isAction, isImportant, index)); + textContainer.Add(newLine); + + AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links.Count == linkAmount); + AddAssert($"msg #{index} has the right action", hasExpectedActions); + AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); + AddAssert($"msg #{index} shows {linkAmount} link(s)", isShowingLinks); + + bool hasExpectedActions() + { + var expectedActionsList = expectedActions.ToList(); + + if (expectedActionsList.Count != newLine.Message.Links.Count) + return false; + + for (int i = 0; i < newLine.Message.Links.Count; i++) + { + var action = newLine.Message.Links[i].Action; + if (action != expectedActions[i]) return false; + } + + return true; + } + + bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font == "Exo2.0-MediumItalic"); + + bool isShowingLinks() + { + bool hasBackground = !string.IsNullOrEmpty(newLine.Message.Sender.Colour); + + Color4 textColour = isAction && hasBackground ? OsuColour.FromHex(newLine.Message.Sender.Colour) : Color4.White; + + var linkCompilers = newLine.ContentFlow.Where(d => d is DrawableLinkCompiler).ToList(); + var linkSprites = linkCompilers.SelectMany(comp => ((DrawableLinkCompiler)comp).Parts); + + return linkSprites.All(d => d.Colour == linkColour) + && newLine.ContentFlow.Except(linkSprites.Concat(linkCompilers)).All(d => d.Colour == textColour); + } + } + + private void testLinksGeneral() + { + addMessageWithChecks("test!"); + addMessageWithChecks("osu.ppy.sh!"); + addMessageWithChecks("https://osu.ppy.sh!", 1, expectedActions: LinkAction.External); + addMessageWithChecks("00:12:345 (1,2) - Test?", 1, expectedActions: LinkAction.OpenEditorTimestamp); + addMessageWithChecks("Wiki link for tasty [[Performance Points]]", 1, expectedActions: LinkAction.External); + addMessageWithChecks("(osu forums)[https://osu.ppy.sh/forum] (old link format)", 1, expectedActions: LinkAction.External); + addMessageWithChecks("[https://osu.ppy.sh/home New site] (new link format)", 1, expectedActions: LinkAction.External); + addMessageWithChecks("[osu forums](https://osu.ppy.sh/forum) (new link format 2)", 1, expectedActions: LinkAction.External); + addMessageWithChecks("[https://osu.ppy.sh/home This is only a link to the new osu webpage but this is supposed to test word wrap.]", 1, expectedActions: LinkAction.External); + addMessageWithChecks("is now listening to [https://osu.ppy.sh/s/93523 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmapSet); + addMessageWithChecks("is now playing [https://osu.ppy.sh/b/252238 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmap); + addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3, + expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); + // note that there's 0 links here (they get removed if a channel is not found) + addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); + addMessageWithChecks("I am important!", 0, false, true); + addMessageWithChecks("feels important", 0, true, true); + addMessageWithChecks("likes to post this [https://osu.ppy.sh/home link].", 1, true, true, expectedActions: LinkAction.External); + addMessageWithChecks("Join my multiplayer game osump://12346.", 1, expectedActions: LinkAction.JoinMultiplayerMatch); + addMessageWithChecks("Join my [multiplayer game](osump://12346).", 1, expectedActions: LinkAction.JoinMultiplayerMatch); + addMessageWithChecks("Join my [#english](osu://chan/#english).", 1, expectedActions: LinkAction.OpenChannel); + addMessageWithChecks("Join my osu://chan/#english.", 1, expectedActions: LinkAction.OpenChannel); + addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel }); + addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel); + } + + private void testEcho() + { + int echoCounter = 0; + + addEchoWithWait("sent!", "received!"); + addEchoWithWait("https://osu.ppy.sh/home", null, 500); + addEchoWithWait("[https://osu.ppy.sh/forum let's try multiple words too!]"); + addEchoWithWait("(long loading times! clickable while loading?)[https://osu.ppy.sh/home]", null, 5000); + + void addEchoWithWait(string text, string completeText = null, double delay = 250) + { + var newLine = new ChatLine(new DummyEchoMessage(text)); + + AddStep($"send msg #{++echoCounter} after {delay}ms", () => + { + textContainer.Add(newLine); + Scheduler.AddDelayed(() => newLine.Message = new DummyMessage(completeText ?? text), delay); + }); + + AddUntilStep(() => textContainer.All(line => line.Message is DummyMessage), $"wait for msg #{echoCounter}"); + } + } + + private class DummyEchoMessage : LocalEchoMessage + { + public DummyEchoMessage(string text) + { + Content = text; + Timestamp = DateTimeOffset.Now; + Sender = DummyMessage.TEST_SENDER; + } + } + + private class DummyMessage : Message + { + private static long messageCounter; + + internal static readonly User TEST_SENDER_BACKGROUND = new User + { + Username = @"i-am-important", + Id = 42, + Colour = "#250cc9", + }; + + internal static readonly User TEST_SENDER = new User + { + Username = @"Somebody", + Id = 1, + }; + + public new DateTimeOffset Timestamp = DateTimeOffset.Now; + + public DummyMessage(string text, bool isAction = false, bool isImportant = false, int number = 0) + : base(messageCounter++) + { + Content = text; + IsAction = isAction; + Sender = new User + { + Username = $"User {number}", + Id = number, + Colour = isImportant ? "#250cc9" : null, + }; + } + } + + private class TestChatLineContainer : FillFlowContainer + { + protected override int Compare(Drawable x, Drawable y) + { + var xC = (ChatLine)x; + var yC = (ChatLine)y; + + return xC.Message.CompareTo(yC.Message); + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseContextMenu.cs b/osu.Game.Tests/Visual/TestCaseContextMenu.cs index 45c12cf4af..80505b219b 100644 --- a/osu.Game.Tests/Visual/TestCaseContextMenu.cs +++ b/osu.Game.Tests/Visual/TestCaseContextMenu.cs @@ -1,98 +1,98 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Graphics.Cursor; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseContextMenu : OsuTestCase - { - private const int start_time = 0; - private const int duration = 1000; - - private readonly Container container; - - public TestCaseContextMenu() - { - Add(new OsuContextMenuContainer - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - container = new MyContextMenuContainer - { - Size = new Vector2(200), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Green, - } - }, - new AnotherContextMenuContainer - { - Size = new Vector2(200), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Red, - } - } - } - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // Move box along a square trajectory - container.Loop(c => c - .MoveTo(new Vector2(0, 100), duration).Then() - .MoveTo(new Vector2(100, 100), duration).Then() - .MoveTo(new Vector2(100, 0), duration).Then() - .MoveTo(Vector2.Zero, duration) - ); - } - - private class MyContextMenuContainer : Container, IHasContextMenu - { - public MenuItem[] ContextMenuItems => new MenuItem[] - { - new OsuMenuItem(@"Some option"), - new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted), - new OsuMenuItem(@"Another option"), - new OsuMenuItem(@"Choose me please"), - new OsuMenuItem(@"And me too"), - new OsuMenuItem(@"Trying to fill"), - new OsuMenuItem(@"Destructive option", MenuItemType.Destructive), - }; - } - - private class AnotherContextMenuContainer : Container, IHasContextMenu - { - public MenuItem[] ContextMenuItems => new MenuItem[] - { - new OsuMenuItem(@"Simple option"), - new OsuMenuItem(@"Simple very very long option"), - new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Graphics.Cursor; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseContextMenu : OsuTestCase + { + private const int start_time = 0; + private const int duration = 1000; + + private readonly Container container; + + public TestCaseContextMenu() + { + Add(new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + container = new MyContextMenuContainer + { + Size = new Vector2(200), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Green, + } + }, + new AnotherContextMenuContainer + { + Size = new Vector2(200), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Red, + } + } + } + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // Move box along a square trajectory + container.Loop(c => c + .MoveTo(new Vector2(0, 100), duration).Then() + .MoveTo(new Vector2(100, 100), duration).Then() + .MoveTo(new Vector2(100, 0), duration).Then() + .MoveTo(Vector2.Zero, duration) + ); + } + + private class MyContextMenuContainer : Container, IHasContextMenu + { + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem(@"Some option"), + new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted), + new OsuMenuItem(@"Another option"), + new OsuMenuItem(@"Choose me please"), + new OsuMenuItem(@"And me too"), + new OsuMenuItem(@"Trying to fill"), + new OsuMenuItem(@"Destructive option", MenuItemType.Destructive), + }; + } + + private class AnotherContextMenuContainer : Container, IHasContextMenu + { + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem(@"Simple option"), + new OsuMenuItem(@"Simple very very long option"), + new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)), + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs index 4f4fdbeb5b..977f241f7a 100644 --- a/osu.Game.Tests/Visual/TestCaseCursors.cs +++ b/osu.Game.Tests/Visual/TestCaseCursors.cs @@ -1,261 +1,261 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Framework.MathUtils; -using osu.Game.Graphics.Cursor; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseCursors : ManualInputManagerTestCase - { - private readonly CursorOverrideContainer cursorOverrideContainer; - private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; - - public TestCaseCursors() - { - Child = cursorOverrideContainer = new CursorOverrideContainer - { - RelativeSizeAxes = Axes.Both, - Children = new[] - { - // Middle user - cursorBoxes[0] = new CustomCursorBox(Color4.Green) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - }, - // Top-left user - cursorBoxes[1] = new CustomCursorBox(Color4.Blue) - { - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Bottom-right user - cursorBoxes[2] = new CustomCursorBox(Color4.Red) - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Bottom-left local - cursorBoxes[3] = new CustomCursorBox(Color4.Magenta, false) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Top-right local - cursorBoxes[4] = new CustomCursorBox(Color4.Cyan, false) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.4f) - }, - // Left-local - cursorBoxes[5] = new CustomCursorBox(Color4.Yellow, false) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.2f, 1), - }, - } - }; - - AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b)); - - testUserCursor(); - testLocalCursor(); - testUserCursorOverride(); - testMultipleLocalCursors(); - ReturnUserInput(); - } - - /// - /// -- Green Box -- - /// Tests whether hovering in and out of a drawable that provides the user cursor (green) - /// results in the correct visibility state for that cursor. - /// - private void testUserCursor() - { - AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); - AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); - AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); - AddStep("Move out", moveOut); - AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); - AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); - } - - /// - /// -- Purple Box -- - /// Tests whether hovering in and out of a drawable that provides a local cursor (purple) - /// results in the correct visibility and state for that cursor. - /// - private void testLocalCursor() - { - AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3])); - AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); - AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); - AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); - AddAssert("Check global cursor at mouse", () => checkAtMouse(cursorOverrideContainer.Cursor)); - AddStep("Move out", moveOut); - AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); - AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); - } - - /// - /// -- Blue-Green Box Boundary -- - /// Tests whether overriding a user cursor (green) with another user cursor (blue) - /// results in the correct visibility and states for the cursors. - /// - private void testUserCursorOverride() - { - AddStep("Move to blue-green boundary", () => InputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); - AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); - AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); - AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); - AddStep("Move out", moveOut); - AddAssert("Check blue cursor not visible", () => !checkVisible(cursorBoxes[1].Cursor)); - AddAssert("Check green cursor not visible", () => !checkVisible(cursorBoxes[0].Cursor)); - } - - /// - /// -- Yellow-Purple Box Boundary -- - /// Tests whether multiple local cursors (purple + yellow) may be visible and at the mouse position at the same time. - /// - private void testMultipleLocalCursors() - { - AddStep("Move to yellow-purple boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); - AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); - AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); - AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); - AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); - AddStep("Move out", moveOut); - AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); - AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); - } - - /// - /// -- Yellow-Blue Box Boundary -- - /// Tests whether a local cursor (yellow) may be displayed along with a user cursor override (blue). - /// - private void testUserOverrideWithLocal() - { - AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); - AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); - AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); - AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); - AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); - AddStep("Move out", moveOut); - AddAssert("Check blue cursor invisible", () => !checkVisible(cursorBoxes[1].Cursor)); - AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); - } - - /// - /// Moves the cursor to a point not covered by any cursor containers. - /// - private void moveOut() - => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.Centre.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y)); - - /// - /// Checks if a cursor is visible. - /// - /// The cursor to check. - private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State == Visibility.Visible; - - /// - /// Checks if a cursor is at the current inputmanager screen position. - /// - /// The cursor to check. - private bool checkAtMouse(CursorContainer cursorContainer) - => Precision.AlmostEquals(InputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); - - private class CustomCursorBox : Container, IProvideCursor - { - public bool SmoothTransition; - - public CursorContainer Cursor { get; } - public bool ProvidingUserCursor { get; } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor; - - private readonly Box background; - - public CustomCursorBox(Color4 cursorColour, bool providesUserCursor = true) - { - ProvidingUserCursor = providesUserCursor; - - Colour = cursorColour; - Masking = true; - - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.1f - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = providesUserCursor ? "User cursor" : "Local cursor" - }, - Cursor = new TestCursorContainer - { - State = providesUserCursor ? Visibility.Hidden : Visibility.Visible, - } - }; - } - - protected override bool OnHover(InputState state) - { - background.FadeTo(0.4f, 250, Easing.OutQuint); - return false; - } - - protected override void OnHoverLost(InputState state) - { - background.FadeTo(0.1f, 250); - base.OnHoverLost(state); - } - } - - private class TestCursorContainer : CursorContainer - { - protected override Drawable CreateCursor() => new TestCursor(); - - private class TestCursor : CircularContainer - { - public TestCursor() - { - Origin = Anchor.Centre; - - Size = new Vector2(50); - Masking = true; - - Blending = BlendingMode.Additive; - Alpha = 0.5f; - - Child = new Box { RelativeSizeAxes = Axes.Both }; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseCursors : ManualInputManagerTestCase + { + private readonly CursorOverrideContainer cursorOverrideContainer; + private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; + + public TestCaseCursors() + { + Child = cursorOverrideContainer = new CursorOverrideContainer + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + // Middle user + cursorBoxes[0] = new CustomCursorBox(Color4.Green) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + // Top-left user + cursorBoxes[1] = new CustomCursorBox(Color4.Blue) + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-right user + cursorBoxes[2] = new CustomCursorBox(Color4.Red) + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-left local + cursorBoxes[3] = new CustomCursorBox(Color4.Magenta, false) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Top-right local + cursorBoxes[4] = new CustomCursorBox(Color4.Cyan, false) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Left-local + cursorBoxes[5] = new CustomCursorBox(Color4.Yellow, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.2f, 1), + }, + } + }; + + AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b)); + + testUserCursor(); + testLocalCursor(); + testUserCursorOverride(); + testMultipleLocalCursors(); + ReturnUserInput(); + } + + /// + /// -- Green Box -- + /// Tests whether hovering in and out of a drawable that provides the user cursor (green) + /// results in the correct visibility state for that cursor. + /// + private void testUserCursor() + { + AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); + AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + } + + /// + /// -- Purple Box -- + /// Tests whether hovering in and out of a drawable that provides a local cursor (purple) + /// results in the correct visibility and state for that cursor. + /// + private void testLocalCursor() + { + AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3])); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + AddAssert("Check global cursor at mouse", () => checkAtMouse(cursorOverrideContainer.Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + } + + /// + /// -- Blue-Green Box Boundary -- + /// Tests whether overriding a user cursor (green) with another user cursor (blue) + /// results in the correct visibility and states for the cursors. + /// + private void testUserCursorOverride() + { + AddStep("Move to blue-green boundary", () => InputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check blue cursor not visible", () => !checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check green cursor not visible", () => !checkVisible(cursorBoxes[0].Cursor)); + } + + /// + /// -- Yellow-Purple Box Boundary -- + /// Tests whether multiple local cursors (purple + yellow) may be visible and at the mouse position at the same time. + /// + private void testMultipleLocalCursors() + { + AddStep("Move to yellow-purple boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + } + + /// + /// -- Yellow-Blue Box Boundary -- + /// Tests whether a local cursor (yellow) may be displayed along with a user cursor override (blue). + /// + private void testUserOverrideWithLocal() + { + AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); + AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check blue cursor invisible", () => !checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + } + + /// + /// Moves the cursor to a point not covered by any cursor containers. + /// + private void moveOut() + => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.Centre.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y)); + + /// + /// Checks if a cursor is visible. + /// + /// The cursor to check. + private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State == Visibility.Visible; + + /// + /// Checks if a cursor is at the current inputmanager screen position. + /// + /// The cursor to check. + private bool checkAtMouse(CursorContainer cursorContainer) + => Precision.AlmostEquals(InputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); + + private class CustomCursorBox : Container, IProvideCursor + { + public bool SmoothTransition; + + public CursorContainer Cursor { get; } + public bool ProvidingUserCursor { get; } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor; + + private readonly Box background; + + public CustomCursorBox(Color4 cursorColour, bool providesUserCursor = true) + { + ProvidingUserCursor = providesUserCursor; + + Colour = cursorColour; + Masking = true; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = providesUserCursor ? "User cursor" : "Local cursor" + }, + Cursor = new TestCursorContainer + { + State = providesUserCursor ? Visibility.Hidden : Visibility.Visible, + } + }; + } + + protected override bool OnHover(InputState state) + { + background.FadeTo(0.4f, 250, Easing.OutQuint); + return false; + } + + protected override void OnHoverLost(InputState state) + { + background.FadeTo(0.1f, 250); + base.OnHoverLost(state); + } + } + + private class TestCursorContainer : CursorContainer + { + protected override Drawable CreateCursor() => new TestCursor(); + + private class TestCursor : CircularContainer + { + public TestCursor() + { + Origin = Anchor.Centre; + + Size = new Vector2(50); + Masking = true; + + Blending = BlendingMode.Additive; + Alpha = 0.5f; + + Child = new Box { RelativeSizeAxes = Axes.Both }; + } + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs b/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs index e9512b29f7..f0907ed28c 100644 --- a/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs @@ -1,75 +1,75 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Game.Graphics; -using osu.Game.Overlays; -using osu.Game.Overlays.Dialog; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseDialogOverlay : OsuTestCase - { - public TestCaseDialogOverlay() - { - DialogOverlay overlay; - - Add(overlay = new DialogOverlay()); - - AddStep("dialog #1", () => overlay.Push(new PopupDialog - { - Icon = FontAwesome.fa_trash_o, - HeaderText = @"Confirm deletion of", - BodyText = @"Ayase Rie - Yuima-ru*World TVver.", - Buttons = new PopupDialogButton[] - { - new PopupDialogOkButton - { - Text = @"I never want to see this again.", - Action = () => System.Console.WriteLine(@"OK"), - }, - new PopupDialogCancelButton - { - Text = @"Firetruck, I still want quick ranks!", - Action = () => System.Console.WriteLine(@"Cancel"), - }, - }, - })); - - AddStep("dialog #2", () => overlay.Push(new PopupDialog - { - Icon = FontAwesome.fa_gear, - HeaderText = @"What do you want to do with", - BodyText = "Camellia as \"Bang Riot\" - Blastix Riotz", - Buttons = new PopupDialogButton[] - { - new PopupDialogOkButton - { - Text = @"Manage collections", - }, - new PopupDialogOkButton - { - Text = @"Delete...", - }, - new PopupDialogOkButton - { - Text = @"Remove from unplayed", - }, - new PopupDialogOkButton - { - Text = @"Clear local scores", - }, - new PopupDialogOkButton - { - Text = @"Edit", - }, - new PopupDialogCancelButton - { - Text = @"Cancel", - }, - }, - })); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseDialogOverlay : OsuTestCase + { + public TestCaseDialogOverlay() + { + DialogOverlay overlay; + + Add(overlay = new DialogOverlay()); + + AddStep("dialog #1", () => overlay.Push(new PopupDialog + { + Icon = FontAwesome.fa_trash_o, + HeaderText = @"Confirm deletion of", + BodyText = @"Ayase Rie - Yuima-ru*World TVver.", + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"I never want to see this again.", + Action = () => System.Console.WriteLine(@"OK"), + }, + new PopupDialogCancelButton + { + Text = @"Firetruck, I still want quick ranks!", + Action = () => System.Console.WriteLine(@"Cancel"), + }, + }, + })); + + AddStep("dialog #2", () => overlay.Push(new PopupDialog + { + Icon = FontAwesome.fa_gear, + HeaderText = @"What do you want to do with", + BodyText = "Camellia as \"Bang Riot\" - Blastix Riotz", + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"Manage collections", + }, + new PopupDialogOkButton + { + Text = @"Delete...", + }, + new PopupDialogOkButton + { + Text = @"Remove from unplayed", + }, + new PopupDialogOkButton + { + Text = @"Clear local scores", + }, + new PopupDialogOkButton + { + Text = @"Edit", + }, + new PopupDialogCancelButton + { + Text = @"Cancel", + }, + }, + })); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseDirect.cs b/osu.Game.Tests/Visual/TestCaseDirect.cs index 3f3dbb0bca..2f45c87161 100644 --- a/osu.Game.Tests/Visual/TestCaseDirect.cs +++ b/osu.Game.Tests/Visual/TestCaseDirect.cs @@ -1,223 +1,223 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Overlays; -using osu.Game.Rulesets; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseDirect : OsuTestCase - { - private DirectOverlay direct; - private RulesetStore rulesets; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(direct = new DirectOverlay()); - newBeatmaps(); - - AddStep(@"toggle", direct.ToggleVisibility); - AddStep(@"result counts", () => direct.ResultAmounts = new DirectOverlay.ResultCounts(1, 4, 13)); - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - - private void newBeatmaps() - { - var ruleset = rulesets.GetRuleset(0); - - direct.BeatmapSets = new[] - { - new BeatmapSetInfo - { - OnlineBeatmapSetID = 578332, - Metadata = new BeatmapMetadata - { - Title = @"OrVid", - Artist = @"An", - AuthorString = @"RLC", - Source = @"", - Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/578332/covers/card.jpg?1494591390", - Cover = @"https://assets.ppy.sh/beatmaps/578332/covers/cover.jpg?1494591390", - }, - Preview = @"https://b.ppy.sh/preview/578332.mp3", - PlayCount = 97, - FavouriteCount = 72, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 5.35f, - Metadata = new BeatmapMetadata(), - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 599627, - Metadata = new BeatmapMetadata - { - Title = @"tiny lamp", - Artist = @"fhana", - AuthorString = @"Sotarks", - Source = @"ぎんぎつね", - Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/599627/covers/card.jpg?1494539318", - Cover = @"https://assets.ppy.sh/beatmaps/599627/covers/cover.jpg?1494539318", - }, - Preview = @"https//b.ppy.sh/preview/599627.mp3", - PlayCount = 3082, - FavouriteCount = 14, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 5.81f, - Metadata = new BeatmapMetadata(), - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 513268, - Metadata = new BeatmapMetadata - { - Title = @"At Gwanghwamun", - Artist = @"KYUHYUN", - AuthorString = @"Cerulean Veyron", - Source = @"", - Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/513268/covers/card.jpg?1494502863", - Cover = @"https://assets.ppy.sh/beatmaps/513268/covers/cover.jpg?1494502863", - }, - Preview = @"https//b.ppy.sh/preview/513268.mp3", - PlayCount = 2762, - FavouriteCount = 15, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 0.9f, - Metadata = new BeatmapMetadata(), - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 1.1f, - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 2.02f, - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 3.49f, - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 586841, - Metadata = new BeatmapMetadata - { - Title = @"RHAPSODY OF BLUE SKY", - Artist = @"fhana", - AuthorString = @"[Kamiya]", - Source = @"小林さんちのメイドラゴン", - Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/586841/covers/card.jpg?1494052741", - Cover = @"https://assets.ppy.sh/beatmaps/586841/covers/cover.jpg?1494052741", - }, - Preview = @"https//b.ppy.sh/preview/586841.mp3", - PlayCount = 62317, - FavouriteCount = 161, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 1.26f, - Metadata = new BeatmapMetadata(), - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 2.01f, - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 2.87f, - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 3.76f, - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 3.93f, - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 4.37f, - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 5.13f, - }, - new BeatmapInfo - { - Ruleset = ruleset, - StarDifficulty = 5.42f, - }, - }, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Overlays; +using osu.Game.Rulesets; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseDirect : OsuTestCase + { + private DirectOverlay direct; + private RulesetStore rulesets; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(direct = new DirectOverlay()); + newBeatmaps(); + + AddStep(@"toggle", direct.ToggleVisibility); + AddStep(@"result counts", () => direct.ResultAmounts = new DirectOverlay.ResultCounts(1, 4, 13)); + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + this.rulesets = rulesets; + } + + private void newBeatmaps() + { + var ruleset = rulesets.GetRuleset(0); + + direct.BeatmapSets = new[] + { + new BeatmapSetInfo + { + OnlineBeatmapSetID = 578332, + Metadata = new BeatmapMetadata + { + Title = @"OrVid", + Artist = @"An", + AuthorString = @"RLC", + Source = @"", + Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll", + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Card = @"https://assets.ppy.sh/beatmaps/578332/covers/card.jpg?1494591390", + Cover = @"https://assets.ppy.sh/beatmaps/578332/covers/cover.jpg?1494591390", + }, + Preview = @"https://b.ppy.sh/preview/578332.mp3", + PlayCount = 97, + FavouriteCount = 72, + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 5.35f, + Metadata = new BeatmapMetadata(), + }, + }, + }, + new BeatmapSetInfo + { + OnlineBeatmapSetID = 599627, + Metadata = new BeatmapMetadata + { + Title = @"tiny lamp", + Artist = @"fhana", + AuthorString = @"Sotarks", + Source = @"ぎんぎつね", + Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration", + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Card = @"https://assets.ppy.sh/beatmaps/599627/covers/card.jpg?1494539318", + Cover = @"https://assets.ppy.sh/beatmaps/599627/covers/cover.jpg?1494539318", + }, + Preview = @"https//b.ppy.sh/preview/599627.mp3", + PlayCount = 3082, + FavouriteCount = 14, + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 5.81f, + Metadata = new BeatmapMetadata(), + }, + }, + }, + new BeatmapSetInfo + { + OnlineBeatmapSetID = 513268, + Metadata = new BeatmapMetadata + { + Title = @"At Gwanghwamun", + Artist = @"KYUHYUN", + AuthorString = @"Cerulean Veyron", + Source = @"", + Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep", + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Card = @"https://assets.ppy.sh/beatmaps/513268/covers/card.jpg?1494502863", + Cover = @"https://assets.ppy.sh/beatmaps/513268/covers/cover.jpg?1494502863", + }, + Preview = @"https//b.ppy.sh/preview/513268.mp3", + PlayCount = 2762, + FavouriteCount = 15, + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 0.9f, + Metadata = new BeatmapMetadata(), + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 1.1f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 2.02f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 3.49f, + }, + }, + }, + new BeatmapSetInfo + { + OnlineBeatmapSetID = 586841, + Metadata = new BeatmapMetadata + { + Title = @"RHAPSODY OF BLUE SKY", + Artist = @"fhana", + AuthorString = @"[Kamiya]", + Source = @"小林さんちのメイドラゴン", + Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc", + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Card = @"https://assets.ppy.sh/beatmaps/586841/covers/card.jpg?1494052741", + Cover = @"https://assets.ppy.sh/beatmaps/586841/covers/cover.jpg?1494052741", + }, + Preview = @"https//b.ppy.sh/preview/586841.mp3", + PlayCount = 62317, + FavouriteCount = 161, + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 1.26f, + Metadata = new BeatmapMetadata(), + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 2.01f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 2.87f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 3.76f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 3.93f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 4.37f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 5.13f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 5.42f, + }, + }, + }, + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs index 4268fd305e..25f8ba06c4 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs @@ -1,132 +1,132 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Screens.Multiplayer; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseDrawableRoom : OsuTestCase - { - private RulesetStore rulesets; - - protected override void LoadComplete() - { - base.LoadComplete(); - - DrawableRoom first; - Add(new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Y, - Width = 580f, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - first = new DrawableRoom(new Room - { - Name = { Value = @"Great Room Right Here" }, - Host = { Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } } }, - Status = { Value = new RoomStatusOpen() }, - Type = { Value = new GameTypeTeamVersus() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 4.65, - Ruleset = rulesets.GetRuleset(3), - Metadata = new BeatmapMetadata - { - Title = @"Critical Crystal", - Artist = @"Seiryu", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh//beatmaps/376340/covers/cover.jpg?1456478455", - }, - }, - }, - }, - }, - Participants = - { - Value = new[] - { - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1355 } } }, - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 8756 } } }, - }, - }, - }), - new DrawableRoom(new Room - { - Name = { Value = @"Relax It's The Weekend" }, - Host = { Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } } }, - Status = { Value = new RoomStatusPlaying() }, - Type = { Value = new GameTypeTagTeam() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 1.96, - Ruleset = rulesets.GetRuleset(0), - Metadata = new BeatmapMetadata - { - Title = @"Serendipity", - Artist = @"ZAQ", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh//beatmaps/526839/covers/cover.jpg?1493815706", - }, - }, - }, - }, - }, - Participants = - { - Value = new[] - { - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 578975 } } }, - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24554 } } }, - }, - }, - }), - } - }); - - AddStep(@"change title", () => first.Room.Name.Value = @"I Changed Name"); - AddStep(@"change host", () => first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } }); - AddStep(@"change status", () => first.Room.Status.Value = new RoomStatusPlaying()); - AddStep(@"change type", () => first.Room.Type.Value = new GameTypeVersus()); - AddStep(@"change beatmap", () => first.Room.Beatmap.Value = null); - AddStep(@"change participants", () => first.Room.Participants.Value = new[] - { - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1254 } } }, - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 123189 } } }, - }); - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets; +using osu.Game.Screens.Multiplayer; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseDrawableRoom : OsuTestCase + { + private RulesetStore rulesets; + + protected override void LoadComplete() + { + base.LoadComplete(); + + DrawableRoom first; + Add(new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 580f, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + first = new DrawableRoom(new Room + { + Name = { Value = @"Great Room Right Here" }, + Host = { Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } } }, + Status = { Value = new RoomStatusOpen() }, + Type = { Value = new GameTypeTeamVersus() }, + Beatmap = + { + Value = new BeatmapInfo + { + StarDifficulty = 4.65, + Ruleset = rulesets.GetRuleset(3), + Metadata = new BeatmapMetadata + { + Title = @"Critical Crystal", + Artist = @"Seiryu", + }, + BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh//beatmaps/376340/covers/cover.jpg?1456478455", + }, + }, + }, + }, + }, + Participants = + { + Value = new[] + { + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1355 } } }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 8756 } } }, + }, + }, + }), + new DrawableRoom(new Room + { + Name = { Value = @"Relax It's The Weekend" }, + Host = { Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } } }, + Status = { Value = new RoomStatusPlaying() }, + Type = { Value = new GameTypeTagTeam() }, + Beatmap = + { + Value = new BeatmapInfo + { + StarDifficulty = 1.96, + Ruleset = rulesets.GetRuleset(0), + Metadata = new BeatmapMetadata + { + Title = @"Serendipity", + Artist = @"ZAQ", + }, + BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh//beatmaps/526839/covers/cover.jpg?1493815706", + }, + }, + }, + }, + }, + Participants = + { + Value = new[] + { + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 578975 } } }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24554 } } }, + }, + }, + }), + } + }); + + AddStep(@"change title", () => first.Room.Name.Value = @"I Changed Name"); + AddStep(@"change host", () => first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } }); + AddStep(@"change status", () => first.Room.Status.Value = new RoomStatusPlaying()); + AddStep(@"change type", () => first.Room.Type.Value = new GameTypeVersus()); + AddStep(@"change beatmap", () => first.Room.Beatmap.Value = null); + AddStep(@"change participants", () => first.Room.Participants.Value = new[] + { + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1254 } } }, + new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 123189 } } }, + }); + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + this.rulesets = rulesets; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseDrawings.cs b/osu.Game.Tests/Visual/TestCaseDrawings.cs index 204b4f133e..a6a3ef6747 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawings.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawings.cs @@ -1,83 +1,83 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.ComponentModel; -using osu.Game.Screens.Tournament; -using osu.Game.Screens.Tournament.Teams; - -namespace osu.Game.Tests.Visual -{ - [Description("for tournament use")] - public class TestCaseDrawings : OsuTestCase - { - public TestCaseDrawings() - { - Add(new Drawings - { - TeamList = new TestTeamList(), - }); - } - - private class TestTeamList : ITeamList - { - public IEnumerable Teams { get; } = new[] - { - new DrawingsTeam - { - FlagName = "GB", - FullName = "United Kingdom", - Acronym = "UK" - }, - new DrawingsTeam - { - FlagName = "FR", - FullName = "France", - Acronym = "FRA" - }, - new DrawingsTeam - { - FlagName = "CN", - FullName = "China", - Acronym = "CHN" - }, - new DrawingsTeam - { - FlagName = "AU", - FullName = "Australia", - Acronym = "AUS" - }, - new DrawingsTeam - { - FlagName = "JP", - FullName = "Japan", - Acronym = "JPN" - }, - new DrawingsTeam - { - FlagName = "RO", - FullName = "Romania", - Acronym = "ROM" - }, - new DrawingsTeam - { - FlagName = "IT", - FullName = "Italy", - Acronym = "PIZZA" - }, - new DrawingsTeam - { - FlagName = "VE", - FullName = "Venezuela", - Acronym = "VNZ" - }, - new DrawingsTeam - { - FlagName = "US", - FullName = "United States of America", - Acronym = "USA" - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.ComponentModel; +using osu.Game.Screens.Tournament; +using osu.Game.Screens.Tournament.Teams; + +namespace osu.Game.Tests.Visual +{ + [Description("for tournament use")] + public class TestCaseDrawings : OsuTestCase + { + public TestCaseDrawings() + { + Add(new Drawings + { + TeamList = new TestTeamList(), + }); + } + + private class TestTeamList : ITeamList + { + public IEnumerable Teams { get; } = new[] + { + new DrawingsTeam + { + FlagName = "GB", + FullName = "United Kingdom", + Acronym = "UK" + }, + new DrawingsTeam + { + FlagName = "FR", + FullName = "France", + Acronym = "FRA" + }, + new DrawingsTeam + { + FlagName = "CN", + FullName = "China", + Acronym = "CHN" + }, + new DrawingsTeam + { + FlagName = "AU", + FullName = "Australia", + Acronym = "AUS" + }, + new DrawingsTeam + { + FlagName = "JP", + FullName = "Japan", + Acronym = "JPN" + }, + new DrawingsTeam + { + FlagName = "RO", + FullName = "Romania", + Acronym = "ROM" + }, + new DrawingsTeam + { + FlagName = "IT", + FullName = "Italy", + Acronym = "PIZZA" + }, + new DrawingsTeam + { + FlagName = "VE", + FullName = "Venezuela", + Acronym = "VNZ" + }, + new DrawingsTeam + { + FlagName = "US", + FullName = "United States of America", + Acronym = "USA" + }, + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index 8cc7a01acb..96a754a5ce 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -1,30 +1,30 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Edit.Screens.Compose; -using osu.Game.Tests.Beatmaps; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseEditorCompose : EditorClockTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(Compose) }; - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) - { - osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); - - var compose = new Compose(); - compose.Beatmap.BindTo(osuGame.Beatmap); - - Child = compose; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Edit.Screens.Compose; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseEditorCompose : EditorClockTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(Compose) }; + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); + + var compose = new Compose(); + compose.Beatmap.BindTo(osuGame.Beatmap); + + Child = compose; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs index d9850139cd..09f390ab74 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseEditorComposeRadioButtons : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableRadioButton) }; - - public TestCaseEditorComposeRadioButtons() - { - RadioButtonCollection collection; - Add(collection = new RadioButtonCollection - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 150, - Items = new[] - { - new RadioButton("Item 1", () => { }), - new RadioButton("Item 2", () => { }), - new RadioButton("Item 3", () => { }), - new RadioButton("Item 4", () => { }), - new RadioButton("Item 5", () => { }) - } - }); - - for (int i = 0; i < collection.Items.Count; i++) - { - int l = i; - AddStep($"Select item {l + 1}", () => collection.Items[l].Select()); - AddStep($"Deselect item {l + 1}", () => collection.Items[l].Deselect()); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseEditorComposeRadioButtons : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableRadioButton) }; + + public TestCaseEditorComposeRadioButtons() + { + RadioButtonCollection collection; + Add(collection = new RadioButtonCollection + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 150, + Items = new[] + { + new RadioButton("Item 1", () => { }), + new RadioButton("Item 2", () => { }), + new RadioButton("Item 3", () => { }), + new RadioButton("Item 4", () => { }), + new RadioButton("Item 5", () => { }) + } + }); + + for (int i = 0; i < collection.Items.Count; i++) + { + int l = i; + AddStep($"Select item {l + 1}", () => collection.Items[l].Select()); + AddStep($"Deselect item {l + 1}", () => collection.Items[l].Deselect()); + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs index d15ee32d8d..a5053bafe8 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs @@ -1,48 +1,48 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Overlays; -using osu.Game.Screens.Edit.Screens.Compose.Timeline; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseEditorComposeTimeline : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(ScrollableTimeline), typeof(ScrollingTimelineContainer), typeof(BeatmapWaveformGraph), typeof(TimelineButton) }; - - private readonly ScrollableTimeline timeline; - - public TestCaseEditorComposeTimeline() - { - Children = new Drawable[] - { - new MusicController - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - State = Visibility.Visible - }, - timeline = new ScrollableTimeline - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(1000, 100) - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) - { - timeline.Beatmap.BindTo(osuGame.Beatmap); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; +using osu.Game.Screens.Edit.Screens.Compose.Timeline; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseEditorComposeTimeline : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(ScrollableTimeline), typeof(ScrollingTimelineContainer), typeof(BeatmapWaveformGraph), typeof(TimelineButton) }; + + private readonly ScrollableTimeline timeline; + + public TestCaseEditorComposeTimeline() + { + Children = new Drawable[] + { + new MusicController + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + State = Visibility.Visible + }, + timeline = new ScrollableTimeline + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(1000, 100) + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + timeline.Beatmap.BindTo(osuGame.Beatmap); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs b/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs index ee98fa087a..cb4438b2ba 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs @@ -1,98 +1,98 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Edit.Menus; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseEditorMenuBar : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(EditorMenuBar), typeof(ScreenSelectionTabControl) }; - - public TestCaseEditorMenuBar() - { - Add(new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - Height = 50, - Y = 50, - Child = new EditorMenuBar - { - RelativeSizeAxes = Axes.Both, - Items = new[] - { - new MenuItem("File") - { - Items = new[] - { - new EditorMenuItem("Clear All Notes"), - new EditorMenuItem("Open Difficulty..."), - new EditorMenuItem("Save"), - new EditorMenuItem("Create a new Difficulty..."), - new EditorMenuItemSpacer(), - new EditorMenuItem("Revert to Saved"), - new EditorMenuItem("Revert to Saved (Full)"), - new EditorMenuItemSpacer(), - new EditorMenuItem("Test Beatmap"), - new EditorMenuItem("Open AiMod"), - new EditorMenuItemSpacer(), - new EditorMenuItem("Upload Beatmap..."), - new EditorMenuItem("Export Package"), - new EditorMenuItem("Export Map Package"), - new EditorMenuItem("Import from..."), - new EditorMenuItemSpacer(), - new EditorMenuItem("Open Song Folder"), - new EditorMenuItem("Open .osu in Notepad"), - new EditorMenuItem("Open .osb in Notepad"), - new EditorMenuItemSpacer(), - new EditorMenuItem("Exit"), - } - }, - new MenuItem("Timing") - { - Items = new[] - { - new EditorMenuItem("Time Signature"), - new EditorMenuItem("Metronome Clicks"), - new EditorMenuItemSpacer(), - new EditorMenuItem("Add Timing Section"), - new EditorMenuItem("Add Inheriting Section"), - new EditorMenuItem("Reset Current Section"), - new EditorMenuItem("Delete Timing Section"), - new EditorMenuItem("Resnap Current Section"), - new EditorMenuItemSpacer(), - new EditorMenuItem("Timing Setup"), - new EditorMenuItemSpacer(), - new EditorMenuItem("Resnap All Notes", MenuItemType.Destructive), - new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive), - new EditorMenuItem("Recalculate Slider Lengths", MenuItemType.Destructive), - new EditorMenuItem("Delete All Timing Sections", MenuItemType.Destructive), - new EditorMenuItemSpacer(), - new EditorMenuItem("Set Current Position as Preview Point"), - } - }, - new MenuItem("Testing") - { - Items = new[] - { - new EditorMenuItem("Item 1"), - new EditorMenuItem("Item 2"), - new EditorMenuItem("Item 3"), - } - }, - } - } - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Edit.Menus; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseEditorMenuBar : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(EditorMenuBar), typeof(ScreenSelectionTabControl) }; + + public TestCaseEditorMenuBar() + { + Add(new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = 50, + Y = 50, + Child = new EditorMenuBar + { + RelativeSizeAxes = Axes.Both, + Items = new[] + { + new MenuItem("File") + { + Items = new[] + { + new EditorMenuItem("Clear All Notes"), + new EditorMenuItem("Open Difficulty..."), + new EditorMenuItem("Save"), + new EditorMenuItem("Create a new Difficulty..."), + new EditorMenuItemSpacer(), + new EditorMenuItem("Revert to Saved"), + new EditorMenuItem("Revert to Saved (Full)"), + new EditorMenuItemSpacer(), + new EditorMenuItem("Test Beatmap"), + new EditorMenuItem("Open AiMod"), + new EditorMenuItemSpacer(), + new EditorMenuItem("Upload Beatmap..."), + new EditorMenuItem("Export Package"), + new EditorMenuItem("Export Map Package"), + new EditorMenuItem("Import from..."), + new EditorMenuItemSpacer(), + new EditorMenuItem("Open Song Folder"), + new EditorMenuItem("Open .osu in Notepad"), + new EditorMenuItem("Open .osb in Notepad"), + new EditorMenuItemSpacer(), + new EditorMenuItem("Exit"), + } + }, + new MenuItem("Timing") + { + Items = new[] + { + new EditorMenuItem("Time Signature"), + new EditorMenuItem("Metronome Clicks"), + new EditorMenuItemSpacer(), + new EditorMenuItem("Add Timing Section"), + new EditorMenuItem("Add Inheriting Section"), + new EditorMenuItem("Reset Current Section"), + new EditorMenuItem("Delete Timing Section"), + new EditorMenuItem("Resnap Current Section"), + new EditorMenuItemSpacer(), + new EditorMenuItem("Timing Setup"), + new EditorMenuItemSpacer(), + new EditorMenuItem("Resnap All Notes", MenuItemType.Destructive), + new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive), + new EditorMenuItem("Recalculate Slider Lengths", MenuItemType.Destructive), + new EditorMenuItem("Delete All Timing Sections", MenuItemType.Destructive), + new EditorMenuItemSpacer(), + new EditorMenuItem("Set Current Position as Preview Point"), + } + }, + new MenuItem("Testing") + { + Items = new[] + { + new EditorMenuItem("Item 1"), + new EditorMenuItem("Item 2"), + new EditorMenuItem("Item 3"), + } + }, + } + } + }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index 62c02ee5aa..582ab5ecc9 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -1,437 +1,437 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Tests.Beatmaps; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Tests.Visual -{ - public class TestCaseEditorSeekSnapping : EditorClockTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(HitObjectComposer) }; - - public TestCaseEditorSeekSnapping() - { - BeatDivisor.Value = 4; - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) - { - var testBeatmap = new Beatmap - { - ControlPointInfo = new ControlPointInfo - { - TimingPoints = - { - new TimingControlPoint { Time = 0, BeatLength = 200}, - new TimingControlPoint { Time = 100, BeatLength = 400 }, - new TimingControlPoint { Time = 175, BeatLength = 800 }, - new TimingControlPoint { Time = 350, BeatLength = 200 }, - new TimingControlPoint { Time = 450, BeatLength = 100 }, - new TimingControlPoint { Time = 500, BeatLength = 307.69230769230802 } - } - }, - HitObjects = - { - new HitCircle { StartTime = 0 }, - new HitCircle { StartTime = 5000 } - } - }; - - osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap); - - Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock }; - - testSeekNoSnapping(); - testSeekSnappingOnBeat(); - testSeekSnappingInBetweenBeat(); - testSeekForwardNoSnapping(); - testSeekForwardSnappingOnBeat(); - testSeekForwardSnappingFromInBetweenBeat(); - testSeekBackwardSnappingOnBeat(); - testSeekBackwardSnappingFromInBetweenBeat(); - testSeekingWithFloatingPointBeatLength(); - } - - /// - /// Tests whether time is correctly seeked without snapping. - /// - private void testSeekNoSnapping() - { - reset(); - - // Forwards - AddStep("Seek(0)", () => Clock.Seek(0)); - AddAssert("Time = 0", () => Clock.CurrentTime == 0); - AddStep("Seek(33)", () => Clock.Seek(33)); - AddAssert("Time = 33", () => Clock.CurrentTime == 33); - AddStep("Seek(89)", () => Clock.Seek(89)); - AddAssert("Time = 89", () => Clock.CurrentTime == 89); - - // Backwards - AddStep("Seek(25)", () => Clock.Seek(25)); - AddAssert("Time = 25", () => Clock.CurrentTime == 25); - AddStep("Seek(0)", () => Clock.Seek(0)); - AddAssert("Time = 0", () => Clock.CurrentTime == 0); - } - - /// - /// Tests whether seeking to exact beat times puts us on the beat time. - /// These are the white/yellow ticks on the graph. - /// - private void testSeekSnappingOnBeat() - { - reset(); - - AddStep("Seek(0), Snap", () => Clock.SeekSnapped(0)); - AddAssert("Time = 0", () => Clock.CurrentTime == 0); - AddStep("Seek(50), Snap", () => Clock.SeekSnapped(50)); - AddAssert("Time = 50", () => Clock.CurrentTime == 50); - AddStep("Seek(100), Snap", () => Clock.SeekSnapped(100)); - AddAssert("Time = 100", () => Clock.CurrentTime == 100); - AddStep("Seek(175), Snap", () => Clock.SeekSnapped(175)); - AddAssert("Time = 175", () => Clock.CurrentTime == 175); - AddStep("Seek(350), Snap", () => Clock.SeekSnapped(350)); - AddAssert("Time = 350", () => Clock.CurrentTime == 350); - AddStep("Seek(400), Snap", () => Clock.SeekSnapped(400)); - AddAssert("Time = 400", () => Clock.CurrentTime == 400); - AddStep("Seek(450), Snap", () => Clock.SeekSnapped(450)); - AddAssert("Time = 450", () => Clock.CurrentTime == 450); - } - - /// - /// Tests whether seeking to somewhere in the middle between beats puts us on the expected beats. - /// For example, snapping between a white/yellow beat should put us on either the yellow or white, depending on which one we're closer too. - /// If - /// - private void testSeekSnappingInBetweenBeat() - { - reset(); - - AddStep("Seek(24), Snap", () => Clock.SeekSnapped(24)); - AddAssert("Time = 0", () => Clock.CurrentTime == 0); - AddStep("Seek(26), Snap", () => Clock.SeekSnapped(26)); - AddAssert("Time = 50", () => Clock.CurrentTime == 50); - AddStep("Seek(150), Snap", () => Clock.SeekSnapped(150)); - AddAssert("Time = 100", () => Clock.CurrentTime == 100); - AddStep("Seek(170), Snap", () => Clock.SeekSnapped(170)); - AddAssert("Time = 175", () => Clock.CurrentTime == 175); - AddStep("Seek(274), Snap", () => Clock.SeekSnapped(274)); - AddAssert("Time = 175", () => Clock.CurrentTime == 175); - AddStep("Seek(276), Snap", () => Clock.SeekSnapped(276)); - AddAssert("Time = 350", () => Clock.CurrentTime == 350); - } - - /// - /// Tests that when seeking forward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it). - /// - private void testSeekForwardNoSnapping() - { - reset(); - - AddStep("SeekForward", () => Clock.SeekForward()); - AddAssert("Time = 50", () => Clock.CurrentTime == 50); - AddStep("SeekForward", () => Clock.SeekForward()); - AddAssert("Time = 100", () => Clock.CurrentTime == 100); - AddStep("SeekForward", () => Clock.SeekForward()); - AddAssert("Time = 200", () => Clock.CurrentTime == 200); - AddStep("SeekForward", () => Clock.SeekForward()); - AddAssert("Time = 400", () => Clock.CurrentTime == 400); - AddStep("SeekForward", () => Clock.SeekForward()); - AddAssert("Time = 450", () => Clock.CurrentTime == 450); - } - - /// - /// Tests that when seeking forward with beat snapping, all beats are snapped to and timing points are never skipped. - /// - private void testSeekForwardSnappingOnBeat() - { - reset(); - - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 50", () => Clock.CurrentTime == 50); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 100", () => Clock.CurrentTime == 100); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 175", () => Clock.CurrentTime == 175); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 350", () => Clock.CurrentTime == 350); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 400", () => Clock.CurrentTime == 400); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 450", () => Clock.CurrentTime == 450); - } - - /// - /// Tests that when seeking forward from in-between two beats, the next beat or timing point is snapped to, and no beats are skipped. - /// This will also test being extremely close to the next beat/timing point, to ensure rounding is not an issue. - /// - private void testSeekForwardSnappingFromInBetweenBeat() - { - reset(); - - AddStep("Seek(49)", () => Clock.Seek(49)); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 50", () => Clock.CurrentTime == 50); - AddStep("Seek(49.999)", () => Clock.Seek(49.999)); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 50", () => Clock.CurrentTime == 50); - AddStep("Seek(99)", () => Clock.Seek(99)); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 100", () => Clock.CurrentTime == 100); - AddStep("Seek(99.999)", () => Clock.Seek(99.999)); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 100", () => Clock.CurrentTime == 100); - AddStep("Seek(174)", () => Clock.Seek(174)); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 175", () => Clock.CurrentTime == 175); - AddStep("Seek(349)", () => Clock.Seek(349)); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 350", () => Clock.CurrentTime == 350); - AddStep("Seek(399)", () => Clock.Seek(399)); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 400", () => Clock.CurrentTime == 400); - AddStep("Seek(449)", () => Clock.Seek(449)); - AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); - AddAssert("Time = 450", () => Clock.CurrentTime == 450); - } - - /// - /// Tests that when seeking backward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it). - /// - private void testSeekBackwardNoSnapping() - { - reset(); - - AddStep("Seek(450)", () => Clock.Seek(450)); - AddStep("SeekBackward", () => Clock.SeekBackward()); - AddAssert("Time = 425", () => Clock.CurrentTime == 425); - AddStep("SeekBackward", () => Clock.SeekBackward()); - AddAssert("Time = 375", () => Clock.CurrentTime == 375); - AddStep("SeekBackward", () => Clock.SeekBackward()); - AddAssert("Time = 325", () => Clock.CurrentTime == 325); - AddStep("SeekBackward", () => Clock.SeekBackward()); - AddAssert("Time = 125", () => Clock.CurrentTime == 125); - AddStep("SeekBackward", () => Clock.SeekBackward()); - AddAssert("Time = 25", () => Clock.CurrentTime == 25); - AddStep("SeekBackward", () => Clock.SeekBackward()); - AddAssert("Time = 0", () => Clock.CurrentTime == 0); - } - - /// - /// Tests that when seeking backward with beat snapping, all beats are snapped to and timing points are never skipped. - /// - private void testSeekBackwardSnappingOnBeat() - { - reset(); - - AddStep("Seek(450)", () => Clock.Seek(450)); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 400", () => Clock.CurrentTime == 400); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 350", () => Clock.CurrentTime == 350); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 175", () => Clock.CurrentTime == 175); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 100", () => Clock.CurrentTime == 100); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 50", () => Clock.CurrentTime == 50); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 0", () => Clock.CurrentTime == 0); - } - - /// - /// Tests that when seeking backward from in-between two beats, the previous beat or timing point is snapped to, and no beats are skipped. - /// This will also test being extremely close to the previous beat/timing point, to ensure rounding is not an issue. - /// - private void testSeekBackwardSnappingFromInBetweenBeat() - { - reset(); - - AddStep("Seek(451)", () => Clock.Seek(451)); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 450", () => Clock.CurrentTime == 450); - AddStep("Seek(450.999)", () => Clock.Seek(450.999)); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 450", () => Clock.CurrentTime == 450); - AddStep("Seek(401)", () => Clock.Seek(401)); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 400", () => Clock.CurrentTime == 400); - AddStep("Seek(401.999)", () => Clock.Seek(401.999)); - AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); - AddAssert("Time = 400", () => Clock.CurrentTime == 400); - } - - /// - /// Tests that there are no rounding issues when snapping to beats within a timing point with a floating-point beatlength. - /// - private void testSeekingWithFloatingPointBeatLength() - { - reset(); - - double lastTime = 0; - - AddStep("Seek(0)", () => Clock.Seek(0)); - - for (int i = 0; i < 20; i++) - { - AddStep("SeekForward, Snap", () => - { - lastTime = Clock.CurrentTime; - Clock.SeekForward(true); - }); - AddAssert("Time > lastTime", () => Clock.CurrentTime > lastTime); - } - - for (int i = 0; i < 20; i++) - { - AddStep("SeekBackward, Snap", () => - { - lastTime = Clock.CurrentTime; - Clock.SeekBackward(true); - }); - AddAssert("Time < lastTime", () => Clock.CurrentTime < lastTime); - } - - AddAssert("Time = 0", () => Clock.CurrentTime == 0); - } - - private void reset() - { - AddStep("Reset", () => Clock.Seek(0)); - } - - private class TestHitObjectComposer : HitObjectComposer - { - public TestHitObjectComposer(Ruleset ruleset) - : base(ruleset) - { - } - - protected override IReadOnlyList CompositionTools => new ICompositionTool[0]; - } - - private class TimingPointVisualiser : CompositeDrawable - { - private readonly double length; - - private readonly Drawable tracker; - - public TimingPointVisualiser(Beatmap beatmap, double length) - { - this.length = length; - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Width = 0.75f; - - FillFlowContainer timelineContainer; - - InternalChildren = new Drawable[] - { - new Box - { - Name = "Background", - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(85f) - }, - new Container - { - Name = "Tracks", - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(15), - Children = new[] - { - tracker = new Box - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - RelativePositionAxes = Axes.X, - Width = 2, - Colour = Color4.Red, - }, - timelineContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0, 5) - }, - } - } - }; - - var timingPoints = beatmap.ControlPointInfo.TimingPoints; - - for (int i = 0; i < timingPoints.Count; i++) - { - TimingControlPoint next = i == timingPoints.Count - 1 ? null : timingPoints[i + 1]; - timelineContainer.Add(new TimingPointTimeline(timingPoints[i], next?.Time ?? length, length)); - } - } - - protected override void Update() - { - base.Update(); - - tracker.X = (float)(Time.Current / length); - } - - private class TimingPointTimeline : CompositeDrawable - { - public TimingPointTimeline(TimingControlPoint timingPoint, double endTime, double fullDuration) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Box createMainTick(double time) => new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomCentre, - RelativePositionAxes = Axes.X, - X = (float)(time / fullDuration), - Height = 10, - Width = 2 - }; - - Box createBeatTick(double time) => new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomCentre, - RelativePositionAxes = Axes.X, - X = (float)(time / fullDuration), - Height = 5, - Width = 2, - Colour = time > endTime ? Color4.Gray : Color4.Yellow - }; - - AddInternal(createMainTick(timingPoint.Time)); - AddInternal(createMainTick(endTime)); - - for (double t = timingPoint.Time + timingPoint.BeatLength / 4; t < fullDuration; t += timingPoint.BeatLength / 4) - AddInternal(createBeatTick(t)); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseEditorSeekSnapping : EditorClockTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(HitObjectComposer) }; + + public TestCaseEditorSeekSnapping() + { + BeatDivisor.Value = 4; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + var testBeatmap = new Beatmap + { + ControlPointInfo = new ControlPointInfo + { + TimingPoints = + { + new TimingControlPoint { Time = 0, BeatLength = 200}, + new TimingControlPoint { Time = 100, BeatLength = 400 }, + new TimingControlPoint { Time = 175, BeatLength = 800 }, + new TimingControlPoint { Time = 350, BeatLength = 200 }, + new TimingControlPoint { Time = 450, BeatLength = 100 }, + new TimingControlPoint { Time = 500, BeatLength = 307.69230769230802 } + } + }, + HitObjects = + { + new HitCircle { StartTime = 0 }, + new HitCircle { StartTime = 5000 } + } + }; + + osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap); + + Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock }; + + testSeekNoSnapping(); + testSeekSnappingOnBeat(); + testSeekSnappingInBetweenBeat(); + testSeekForwardNoSnapping(); + testSeekForwardSnappingOnBeat(); + testSeekForwardSnappingFromInBetweenBeat(); + testSeekBackwardSnappingOnBeat(); + testSeekBackwardSnappingFromInBetweenBeat(); + testSeekingWithFloatingPointBeatLength(); + } + + /// + /// Tests whether time is correctly seeked without snapping. + /// + private void testSeekNoSnapping() + { + reset(); + + // Forwards + AddStep("Seek(0)", () => Clock.Seek(0)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + AddStep("Seek(33)", () => Clock.Seek(33)); + AddAssert("Time = 33", () => Clock.CurrentTime == 33); + AddStep("Seek(89)", () => Clock.Seek(89)); + AddAssert("Time = 89", () => Clock.CurrentTime == 89); + + // Backwards + AddStep("Seek(25)", () => Clock.Seek(25)); + AddAssert("Time = 25", () => Clock.CurrentTime == 25); + AddStep("Seek(0)", () => Clock.Seek(0)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + } + + /// + /// Tests whether seeking to exact beat times puts us on the beat time. + /// These are the white/yellow ticks on the graph. + /// + private void testSeekSnappingOnBeat() + { + reset(); + + AddStep("Seek(0), Snap", () => Clock.SeekSnapped(0)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + AddStep("Seek(50), Snap", () => Clock.SeekSnapped(50)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("Seek(100), Snap", () => Clock.SeekSnapped(100)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("Seek(175), Snap", () => Clock.SeekSnapped(175)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("Seek(350), Snap", () => Clock.SeekSnapped(350)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + AddStep("Seek(400), Snap", () => Clock.SeekSnapped(400)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("Seek(450), Snap", () => Clock.SeekSnapped(450)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); + } + + /// + /// Tests whether seeking to somewhere in the middle between beats puts us on the expected beats. + /// For example, snapping between a white/yellow beat should put us on either the yellow or white, depending on which one we're closer too. + /// If + /// + private void testSeekSnappingInBetweenBeat() + { + reset(); + + AddStep("Seek(24), Snap", () => Clock.SeekSnapped(24)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + AddStep("Seek(26), Snap", () => Clock.SeekSnapped(26)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("Seek(150), Snap", () => Clock.SeekSnapped(150)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("Seek(170), Snap", () => Clock.SeekSnapped(170)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("Seek(274), Snap", () => Clock.SeekSnapped(274)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("Seek(276), Snap", () => Clock.SeekSnapped(276)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + } + + /// + /// Tests that when seeking forward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it). + /// + private void testSeekForwardNoSnapping() + { + reset(); + + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 200", () => Clock.CurrentTime == 200); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("SeekForward", () => Clock.SeekForward()); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); + } + + /// + /// Tests that when seeking forward with beat snapping, all beats are snapped to and timing points are never skipped. + /// + private void testSeekForwardSnappingOnBeat() + { + reset(); + + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); + } + + /// + /// Tests that when seeking forward from in-between two beats, the next beat or timing point is snapped to, and no beats are skipped. + /// This will also test being extremely close to the next beat/timing point, to ensure rounding is not an issue. + /// + private void testSeekForwardSnappingFromInBetweenBeat() + { + reset(); + + AddStep("Seek(49)", () => Clock.Seek(49)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("Seek(49.999)", () => Clock.Seek(49.999)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("Seek(99)", () => Clock.Seek(99)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("Seek(99.999)", () => Clock.Seek(99.999)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("Seek(174)", () => Clock.Seek(174)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("Seek(349)", () => Clock.Seek(349)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + AddStep("Seek(399)", () => Clock.Seek(399)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("Seek(449)", () => Clock.Seek(449)); + AddStep("SeekForward, Snap", () => Clock.SeekForward(true)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); + } + + /// + /// Tests that when seeking backward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it). + /// + private void testSeekBackwardNoSnapping() + { + reset(); + + AddStep("Seek(450)", () => Clock.Seek(450)); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 425", () => Clock.CurrentTime == 425); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 375", () => Clock.CurrentTime == 375); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 325", () => Clock.CurrentTime == 325); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 125", () => Clock.CurrentTime == 125); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 25", () => Clock.CurrentTime == 25); + AddStep("SeekBackward", () => Clock.SeekBackward()); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + } + + /// + /// Tests that when seeking backward with beat snapping, all beats are snapped to and timing points are never skipped. + /// + private void testSeekBackwardSnappingOnBeat() + { + reset(); + + AddStep("Seek(450)", () => Clock.Seek(450)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 350", () => Clock.CurrentTime == 350); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 175", () => Clock.CurrentTime == 175); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 100", () => Clock.CurrentTime == 100); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 50", () => Clock.CurrentTime == 50); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + } + + /// + /// Tests that when seeking backward from in-between two beats, the previous beat or timing point is snapped to, and no beats are skipped. + /// This will also test being extremely close to the previous beat/timing point, to ensure rounding is not an issue. + /// + private void testSeekBackwardSnappingFromInBetweenBeat() + { + reset(); + + AddStep("Seek(451)", () => Clock.Seek(451)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); + AddStep("Seek(450.999)", () => Clock.Seek(450.999)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 450", () => Clock.CurrentTime == 450); + AddStep("Seek(401)", () => Clock.Seek(401)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + AddStep("Seek(401.999)", () => Clock.Seek(401.999)); + AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true)); + AddAssert("Time = 400", () => Clock.CurrentTime == 400); + } + + /// + /// Tests that there are no rounding issues when snapping to beats within a timing point with a floating-point beatlength. + /// + private void testSeekingWithFloatingPointBeatLength() + { + reset(); + + double lastTime = 0; + + AddStep("Seek(0)", () => Clock.Seek(0)); + + for (int i = 0; i < 20; i++) + { + AddStep("SeekForward, Snap", () => + { + lastTime = Clock.CurrentTime; + Clock.SeekForward(true); + }); + AddAssert("Time > lastTime", () => Clock.CurrentTime > lastTime); + } + + for (int i = 0; i < 20; i++) + { + AddStep("SeekBackward, Snap", () => + { + lastTime = Clock.CurrentTime; + Clock.SeekBackward(true); + }); + AddAssert("Time < lastTime", () => Clock.CurrentTime < lastTime); + } + + AddAssert("Time = 0", () => Clock.CurrentTime == 0); + } + + private void reset() + { + AddStep("Reset", () => Clock.Seek(0)); + } + + private class TestHitObjectComposer : HitObjectComposer + { + public TestHitObjectComposer(Ruleset ruleset) + : base(ruleset) + { + } + + protected override IReadOnlyList CompositionTools => new ICompositionTool[0]; + } + + private class TimingPointVisualiser : CompositeDrawable + { + private readonly double length; + + private readonly Drawable tracker; + + public TimingPointVisualiser(Beatmap beatmap, double length) + { + this.length = length; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Width = 0.75f; + + FillFlowContainer timelineContainer; + + InternalChildren = new Drawable[] + { + new Box + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(85f) + }, + new Container + { + Name = "Tracks", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(15), + Children = new[] + { + tracker = new Box + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + RelativePositionAxes = Axes.X, + Width = 2, + Colour = Color4.Red, + }, + timelineContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 5) + }, + } + } + }; + + var timingPoints = beatmap.ControlPointInfo.TimingPoints; + + for (int i = 0; i < timingPoints.Count; i++) + { + TimingControlPoint next = i == timingPoints.Count - 1 ? null : timingPoints[i + 1]; + timelineContainer.Add(new TimingPointTimeline(timingPoints[i], next?.Time ?? length, length)); + } + } + + protected override void Update() + { + base.Update(); + + tracker.X = (float)(Time.Current / length); + } + + private class TimingPointTimeline : CompositeDrawable + { + public TimingPointTimeline(TimingControlPoint timingPoint, double endTime, double fullDuration) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Box createMainTick(double time) => new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.X, + X = (float)(time / fullDuration), + Height = 10, + Width = 2 + }; + + Box createBeatTick(double time) => new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.X, + X = (float)(time / fullDuration), + Height = 5, + Width = 2, + Colour = time > endTime ? Color4.Gray : Color4.Yellow + }; + + AddInternal(createMainTick(timingPoint.Time)); + AddInternal(createMainTick(endTime)); + + for (double t = timingPoint.Time + timingPoint.BeatLength / 4; t < fullDuration; t += timingPoint.BeatLength / 4) + AddInternal(createBeatTick(t)); + } + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs index 25ea3443ba..d01c2d2b92 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs @@ -1,37 +1,37 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using OpenTK; -using osu.Game.Screens.Edit.Components.Timelines.Summary; -using osu.Game.Rulesets.Osu; -using osu.Game.Tests.Beatmaps; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseEditorSummaryTimeline : EditorClockTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(SummaryTimeline) }; - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) - { - osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); - - SummaryTimeline summaryTimeline; - Add(summaryTimeline = new SummaryTimeline - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(500, 50) - }); - - summaryTimeline.Beatmap.BindTo(osuGame.Beatmap); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using OpenTK; +using osu.Game.Screens.Edit.Components.Timelines.Summary; +using osu.Game.Rulesets.Osu; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseEditorSummaryTimeline : EditorClockTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(SummaryTimeline) }; + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); + + SummaryTimeline summaryTimeline; + Add(summaryTimeline = new SummaryTimeline + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(500, 50) + }); + + summaryTimeline.Beatmap.BindTo(osuGame.Beatmap); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs index 9aa5607f6d..130685a4cf 100644 --- a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs @@ -1,258 +1,258 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using OpenTK.Input; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.Logging; -using osu.Game.Screens.Play; - -namespace osu.Game.Tests.Visual -{ - [Description("player pause/fail screens")] - public class TestCaseGameplayMenuOverlay : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) }; - - private FailOverlay failOverlay; - private PauseContainer.PauseOverlay pauseOverlay; - - [BackgroundDependencyLoader] - private void load() - { - Add(pauseOverlay = new PauseContainer.PauseOverlay - { - OnResume = () => Logger.Log(@"Resume"), - OnRetry = () => Logger.Log(@"Retry"), - OnQuit = () => Logger.Log(@"Quit"), - }); - - Add(failOverlay = new FailOverlay - { - OnRetry = () => Logger.Log(@"Retry"), - OnQuit = () => Logger.Log(@"Quit"), - }); - - var retryCount = 0; - - AddStep("Add retry", () => - { - retryCount++; - pauseOverlay.Retries = failOverlay.Retries = retryCount; - }); - - AddToggleStep("Toggle pause overlay", t => pauseOverlay.ToggleVisibility()); - AddToggleStep("Toggle fail overlay", t => failOverlay.ToggleVisibility()); - - testHideResets(); - - testEnterWithoutSelection(); - testKeyUpFromInitial(); - testKeyDownFromInitial(); - testKeyUpWrapping(); - testKeyDownWrapping(); - - testMouseSelectionAfterKeySelection(); - testKeySelectionAfterMouseSelection(); - - testMouseDeselectionResets(); - - testClickSelection(); - testEnterKeySelection(); - } - - /// - /// Test that hiding the overlay after hovering a button will reset the overlay to the initial state with no buttons selected. - /// - private void testHideResets() - { - AddStep("Show overlay", () => failOverlay.Show()); - - AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnMouseMove(null)); - AddStep("Hide overlay", () => failOverlay.Hide()); - - AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected)); - } - - /// - /// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred. - /// - private void testEnterWithoutSelection() - { - AddStep("Show overlay", () => pauseOverlay.Show()); - - AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter })); - AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); - } - - /// - /// Tests that pressing the up arrow from the initial state selects the last button. - /// - private void testKeyUpFromInitial() - { - AddStep("Show overlay", () => pauseOverlay.Show()); - - AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); - AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); - } - - /// - /// Tests that pressing the down arrow from the initial state selects the first button. - /// - private void testKeyDownFromInitial() - { - AddStep("Show overlay", () => pauseOverlay.Show()); - - AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); - AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); - } - - /// - /// Tests that pressing the up arrow repeatedly causes the selected button to wrap correctly. - /// - private void testKeyUpWrapping() - { - AddStep("Show overlay", () => failOverlay.Show()); - - AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); - AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); - AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); - - AddStep("Hide overlay", () => failOverlay.Hide()); - } - - /// - /// Tests that pressing the down arrow repeatedly causes the selected button to wrap correctly. - /// - private void testKeyDownWrapping() - { - AddStep("Show overlay", () => failOverlay.Show()); - - AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); - AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); - AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); - - AddStep("Hide overlay", () => failOverlay.Hide()); - } - - /// - /// Tests that hovering a button that was previously selected with the keyboard correctly selects the new button and deselects the previous button. - /// - private void testMouseSelectionAfterKeySelection() - { - AddStep("Show overlay", () => pauseOverlay.Show()); - - var secondButton = pauseOverlay.Buttons.Skip(1).First(); - - AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); - AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); - AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected); - AddAssert("Second button selected", () => secondButton.Selected); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); - } - - /// - /// Tests that pressing a key after selecting a button with a hover event correctly selects a new button and deselects the previous button. - /// - private void testKeySelectionAfterMouseSelection() - { - AddStep("Show overlay", () => pauseOverlay.Show()); - - var secondButton = pauseOverlay.Buttons.Skip(1).First(); - - AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); - AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); - AddAssert("Second button not selected", () => !secondButton.Selected); - AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); - - AddStep("Hide overlay", () => pauseOverlay.Hide()); - } - - /// - /// Tests that deselecting with the mouse by losing hover will reset the overlay to the initial state. - /// - private void testMouseDeselectionResets() - { - AddStep("Show overlay", () => pauseOverlay.Show()); - - var secondButton = pauseOverlay.Buttons.Skip(1).First(); - - AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); - AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null)); - AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); - AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition - - AddStep("Hide overlay", () => pauseOverlay.Hide()); - } - - /// - /// Tests that clicking on a button correctly causes a click event for that button. - /// - private void testClickSelection() - { - AddStep("Show overlay", () => pauseOverlay.Show()); - - var retryButton = pauseOverlay.Buttons.Skip(1).First(); - - bool triggered = false; - AddStep("Click retry button", () => - { - var lastAction = pauseOverlay.OnRetry; - pauseOverlay.OnRetry = () => triggered = true; - - retryButton.TriggerOnClick(); - pauseOverlay.OnRetry = lastAction; - }); - - AddAssert("Action was triggered", () => triggered); - AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); - } - - /// - /// Tests that pressing the enter key with a button selected correctly causes a click event for that button. - /// - private void testEnterKeySelection() - { - AddStep("Show overlay", () => pauseOverlay.Show()); - - AddStep("Select second button", () => - { - pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); - pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); - }); - - var retryButton = pauseOverlay.Buttons.Skip(1).First(); - - bool triggered = false; - AddStep("Press enter", () => - { - var lastAction = pauseOverlay.OnRetry; - pauseOverlay.OnRetry = () => triggered = true; - - retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter }); - pauseOverlay.OnRetry = lastAction; - }); - - AddAssert("Action was triggered", () => triggered); - AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Logging; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + [Description("player pause/fail screens")] + public class TestCaseGameplayMenuOverlay : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) }; + + private FailOverlay failOverlay; + private PauseContainer.PauseOverlay pauseOverlay; + + [BackgroundDependencyLoader] + private void load() + { + Add(pauseOverlay = new PauseContainer.PauseOverlay + { + OnResume = () => Logger.Log(@"Resume"), + OnRetry = () => Logger.Log(@"Retry"), + OnQuit = () => Logger.Log(@"Quit"), + }); + + Add(failOverlay = new FailOverlay + { + OnRetry = () => Logger.Log(@"Retry"), + OnQuit = () => Logger.Log(@"Quit"), + }); + + var retryCount = 0; + + AddStep("Add retry", () => + { + retryCount++; + pauseOverlay.Retries = failOverlay.Retries = retryCount; + }); + + AddToggleStep("Toggle pause overlay", t => pauseOverlay.ToggleVisibility()); + AddToggleStep("Toggle fail overlay", t => failOverlay.ToggleVisibility()); + + testHideResets(); + + testEnterWithoutSelection(); + testKeyUpFromInitial(); + testKeyDownFromInitial(); + testKeyUpWrapping(); + testKeyDownWrapping(); + + testMouseSelectionAfterKeySelection(); + testKeySelectionAfterMouseSelection(); + + testMouseDeselectionResets(); + + testClickSelection(); + testEnterKeySelection(); + } + + /// + /// Test that hiding the overlay after hovering a button will reset the overlay to the initial state with no buttons selected. + /// + private void testHideResets() + { + AddStep("Show overlay", () => failOverlay.Show()); + + AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnMouseMove(null)); + AddStep("Hide overlay", () => failOverlay.Hide()); + + AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected)); + } + + /// + /// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred. + /// + private void testEnterWithoutSelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter })); + AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that pressing the up arrow from the initial state selects the last button. + /// + private void testKeyUpFromInitial() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that pressing the down arrow from the initial state selects the first button. + /// + private void testKeyDownFromInitial() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that pressing the up arrow repeatedly causes the selected button to wrap correctly. + /// + private void testKeyUpWrapping() + { + AddStep("Show overlay", () => failOverlay.Show()); + + AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + + AddStep("Hide overlay", () => failOverlay.Hide()); + } + + /// + /// Tests that pressing the down arrow repeatedly causes the selected button to wrap correctly. + /// + private void testKeyDownWrapping() + { + AddStep("Show overlay", () => failOverlay.Show()); + + AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + + AddStep("Hide overlay", () => failOverlay.Hide()); + } + + /// + /// Tests that hovering a button that was previously selected with the keyboard correctly selects the new button and deselects the previous button. + /// + private void testMouseSelectionAfterKeySelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var secondButton = pauseOverlay.Buttons.Skip(1).First(); + + AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); + AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected); + AddAssert("Second button selected", () => secondButton.Selected); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that pressing a key after selecting a button with a hover event correctly selects a new button and deselects the previous button. + /// + private void testKeySelectionAfterMouseSelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var secondButton = pauseOverlay.Buttons.Skip(1).First(); + + AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); + AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("Second button not selected", () => !secondButton.Selected); + AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that deselecting with the mouse by losing hover will reset the overlay to the initial state. + /// + private void testMouseDeselectionResets() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var secondButton = pauseOverlay.Buttons.Skip(1).First(); + + AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); + AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null)); + AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that clicking on a button correctly causes a click event for that button. + /// + private void testClickSelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var retryButton = pauseOverlay.Buttons.Skip(1).First(); + + bool triggered = false; + AddStep("Click retry button", () => + { + var lastAction = pauseOverlay.OnRetry; + pauseOverlay.OnRetry = () => triggered = true; + + retryButton.TriggerOnClick(); + pauseOverlay.OnRetry = lastAction; + }); + + AddAssert("Action was triggered", () => triggered); + AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); + } + + /// + /// Tests that pressing the enter key with a button selected correctly causes a click event for that button. + /// + private void testEnterKeySelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddStep("Select second button", () => + { + pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); + pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); + }); + + var retryButton = pauseOverlay.Buttons.Skip(1).First(); + + bool triggered = false; + AddStep("Press enter", () => + { + var lastAction = pauseOverlay.OnRetry; + pauseOverlay.OnRetry = () => triggered = true; + + retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter }); + pauseOverlay.OnRetry = lastAction; + }); + + AddAssert("Action was triggered", () => triggered); + AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseGraph.cs b/osu.Game.Tests/Visual/TestCaseGraph.cs index 285a43707a..40b05d51e6 100644 --- a/osu.Game.Tests/Visual/TestCaseGraph.cs +++ b/osu.Game.Tests/Visual/TestCaseGraph.cs @@ -1,39 +1,39 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; -using OpenTK; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseGraph : OsuTestCase - { - public TestCaseGraph() - { - BarGraph graph; - - Children = new[] - { - graph = new BarGraph - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(0.5f), - }, - }; - - AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i)); - AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i)); - AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i)); - AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop); - AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom); - AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight); - AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using OpenTK; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseGraph : OsuTestCase + { + public TestCaseGraph() + { + BarGraph graph; + + Children = new[] + { + graph = new BarGraph + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.5f), + }, + }; + + AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i)); + AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i)); + AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i)); + AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop); + AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom); + AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight); + AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs index 2e94baa9fc..a576ac84e7 100644 --- a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs +++ b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs @@ -1,49 +1,49 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Overlays.Profile.Sections; -using osu.Game.Overlays.Profile.Sections.Historical; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseHistoricalSection : OsuTestCase - { - public override IReadOnlyList RequiredTypes => - new[] - { - typeof(HistoricalSection), - typeof(PaginatedMostPlayedBeatmapContainer), - typeof(DrawableMostPlayedRow), - typeof(DrawableProfileRow) - }; - - public TestCaseHistoricalSection() - { - HistoricalSection section; - - Add(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f) - }); - - Add(new ScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = section = new HistoricalSection(), - }); - - AddStep("Show peppy", () => section.User.Value = new User { Id = 2 }); - AddStep("Show WubWoofWolf", () => section.User.Value = new User { Id = 39828 }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Overlays.Profile.Sections; +using osu.Game.Overlays.Profile.Sections.Historical; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseHistoricalSection : OsuTestCase + { + public override IReadOnlyList RequiredTypes => + new[] + { + typeof(HistoricalSection), + typeof(PaginatedMostPlayedBeatmapContainer), + typeof(DrawableMostPlayedRow), + typeof(DrawableProfileRow) + }; + + public TestCaseHistoricalSection() + { + HistoricalSection section; + + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }); + + Add(new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = section = new HistoricalSection(), + }); + + AddStep("Show peppy", () => section.User.Value = new User { Id = 2 }); + AddStep("Show WubWoofWolf", () => section.User.Value = new User { Id = 39828 }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs index 72d60d8e01..d0c46ecdd7 100644 --- a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs @@ -1,72 +1,72 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using JetBrains.Annotations; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Timing; -using OpenTK; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Edit.Screens.Compose.Layers; -using osu.Game.Tests.Beatmaps; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseHitObjectComposer : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(MaskSelection), - typeof(DragLayer), - typeof(HitObjectComposer), - typeof(OsuHitObjectComposer), - typeof(HitObjectMaskLayer), - typeof(NotNullAttribute) - }; - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) - { - osuGame.Beatmap.Value = new TestWorkingBeatmap(new Beatmap - { - HitObjects = new List - { - new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, - new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, - new Slider - { - Position = new Vector2(128, 256), - ControlPoints = new List - { - Vector2.Zero, - new Vector2(216, 0), - }, - Distance = 400, - Velocity = 1, - TickDistance = 100, - Scale = 0.5f, - } - }, - }); - - var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - dependencies.CacheAs(clock); - dependencies.CacheAs(clock); - - Child = new OsuHitObjectComposer(new OsuRuleset()); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Timing; +using OpenTK; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Screens.Compose.Layers; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseHitObjectComposer : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(MaskSelection), + typeof(DragLayer), + typeof(HitObjectComposer), + typeof(OsuHitObjectComposer), + typeof(HitObjectMaskLayer), + typeof(NotNullAttribute) + }; + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + osuGame.Beatmap.Value = new TestWorkingBeatmap(new Beatmap + { + HitObjects = new List + { + new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, + new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, + new Slider + { + Position = new Vector2(128, 256), + ControlPoints = new List + { + Vector2.Zero, + new Vector2(216, 0), + }, + Distance = 400, + Velocity = 1, + TickDistance = 100, + Scale = 0.5f, + } + }, + }); + + var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); + + Child = new OsuHitObjectComposer(new OsuRuleset()); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseIconButton.cs b/osu.Game.Tests/Visual/TestCaseIconButton.cs index fae79e25bd..16363da527 100644 --- a/osu.Game.Tests/Visual/TestCaseIconButton.cs +++ b/osu.Game.Tests/Visual/TestCaseIconButton.cs @@ -1,113 +1,113 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseIconButton : OsuTestCase - { - public TestCaseIconButton() - { - Child = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Spacing = new Vector2(10, 10), - Children = new[] - { - new NamedIconButton("No change", new IconButton()), - new NamedIconButton("Background colours", new IconButton - { - FlashColour = Color4.DarkGreen, - HoverColour = Color4.Green, - }), - new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }), - new NamedIconButton("Unchanging size", new IconButton(), false), - new NamedIconButton("Icon colours", new IconButton - { - IconColour = Color4.Green, - IconHoverColour = Color4.Red - }) - } - }; - } - - private class NamedIconButton : Container - { - public NamedIconButton(string name, IconButton button, bool allowSizeChange = true) - { - AutoSizeAxes = Axes.Y; - Width = 200; - - Container iconContainer; - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - Children = new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = name - }, - new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.1f, - }, - iconContainer = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Child = button - } - } - } - } - } - }; - - if (allowSizeChange) - iconContainer.AutoSizeAxes = Axes.Both; - else - { - iconContainer.RelativeSizeAxes = Axes.X; - iconContainer.Height = 30; - } - - button.Anchor = Anchor.Centre; - button.Origin = Anchor.Centre; - button.Icon = FontAwesome.fa_osu_osu_o; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseIconButton : OsuTestCase + { + public TestCaseIconButton() + { + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Spacing = new Vector2(10, 10), + Children = new[] + { + new NamedIconButton("No change", new IconButton()), + new NamedIconButton("Background colours", new IconButton + { + FlashColour = Color4.DarkGreen, + HoverColour = Color4.Green, + }), + new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }), + new NamedIconButton("Unchanging size", new IconButton(), false), + new NamedIconButton("Icon colours", new IconButton + { + IconColour = Color4.Green, + IconHoverColour = Color4.Red + }) + } + }; + } + + private class NamedIconButton : Container + { + public NamedIconButton(string name, IconButton button, bool allowSizeChange = true) + { + AutoSizeAxes = Axes.Y; + Width = 200; + + Container iconContainer; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = name + }, + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f, + }, + iconContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Child = button + } + } + } + } + } + }; + + if (allowSizeChange) + iconContainer.AutoSizeAxes = Axes.Both; + else + { + iconContainer.RelativeSizeAxes = Axes.X; + iconContainer.Height = 30; + } + + button.Anchor = Anchor.Centre; + button.Origin = Anchor.Centre; + button.Icon = FontAwesome.fa_osu_osu_o; + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseIntroSequence.cs b/osu.Game.Tests/Visual/TestCaseIntroSequence.cs index 4af6255b48..4982686505 100644 --- a/osu.Game.Tests/Visual/TestCaseIntroSequence.cs +++ b/osu.Game.Tests/Visual/TestCaseIntroSequence.cs @@ -1,54 +1,54 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; -using osu.Game.Screens.Menu; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseIntroSequence : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuLogo), - }; - - public TestCaseIntroSequence() - { - OsuLogo logo; - - var rateAdjustClock = new StopwatchClock(true); - var framedClock = new FramedClock(rateAdjustClock); - framedClock.ProcessFrame(); - - Add(new Container - { - RelativeSizeAxes = Axes.Both, - Clock = framedClock, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - logo = new OsuLogo - { - Anchor = Anchor.Centre, - } - } - }); - - AddStep(@"Restart", logo.PlayIntro); - AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseIntroSequence : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(OsuLogo), + }; + + public TestCaseIntroSequence() + { + OsuLogo logo; + + var rateAdjustClock = new StopwatchClock(true); + var framedClock = new FramedClock(rateAdjustClock); + framedClock.ProcessFrame(); + + Add(new Container + { + RelativeSizeAxes = Axes.Both, + Clock = framedClock, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + logo = new OsuLogo + { + Anchor = Anchor.Centre, + } + } + }); + + AddStep(@"Restart", logo.PlayIntro); + AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs b/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs index e39b9f6683..fe88b46414 100644 --- a/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs +++ b/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseKeyConfiguration : OsuTestCase - { - private readonly KeyBindingOverlay overlay; - - public TestCaseKeyConfiguration() - { - Child = overlay = new KeyBindingOverlay(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - overlay.Show(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseKeyConfiguration : OsuTestCase + { + private readonly KeyBindingOverlay overlay; + + public TestCaseKeyConfiguration() + { + Child = overlay = new KeyBindingOverlay(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + overlay.Show(); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs index bf73c6899b..a20f67e336 100644 --- a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs +++ b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.MathUtils; -using osu.Game.Screens.Play; -using OpenTK.Input; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseKeyCounter : OsuTestCase - { - public TestCaseKeyCounter() - { - KeyCounterCollection kc = new KeyCounterCollection - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - IsCounting = true, - Children = new KeyCounter[] - { - new KeyCounterKeyboard(Key.Z), - new KeyCounterKeyboard(Key.X), - new KeyCounterMouse(MouseButton.Left), - new KeyCounterMouse(MouseButton.Right), - }, - }; - - AddStep("Add random", () => - { - Key key = (Key)((int)Key.A + RNG.Next(26)); - kc.Add(new KeyCounterKeyboard(key)); - }); - AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v); - - Add(kc); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using osu.Game.Screens.Play; +using OpenTK.Input; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseKeyCounter : OsuTestCase + { + public TestCaseKeyCounter() + { + KeyCounterCollection kc = new KeyCounterCollection + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + IsCounting = true, + Children = new KeyCounter[] + { + new KeyCounterKeyboard(Key.Z), + new KeyCounterKeyboard(Key.X), + new KeyCounterMouse(MouseButton.Left), + new KeyCounterMouse(MouseButton.Right), + }, + }; + + AddStep("Add random", () => + { + Key key = (Key)((int)Key.A + RNG.Next(26)); + kc.Add(new KeyCounterKeyboard(key)); + }); + AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v); + + Add(kc); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index 7b00f4a1d5..7f5bce3b84 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -1,278 +1,278 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Select.Leaderboards; -using osu.Game.Users; -using osu.Framework.Allocation; -using OpenTK; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; - -namespace osu.Game.Tests.Visual -{ - [Description("PlaySongSelect leaderboard")] - public class TestCaseLeaderboard : OsuTestCase - { - private RulesetStore rulesets; - - private readonly FailableLeaderboard leaderboard; - - public TestCaseLeaderboard() - { - Add(leaderboard = new FailableLeaderboard - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(550f, 450f), - Scope = LeaderboardScope.Global, - }); - - AddStep(@"New Scores", newScores); - AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores)); - AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure)); - AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter)); - AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn)); - AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable)); - AddStep(@"Real beatmap", realBeatmap); - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - - private void newScores() - { - var scores = new[] - { - new Score - { - Rank = ScoreRank.XH, - Accuracy = 1, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 6602580, - Username = @"waaiiru", - Country = new Country - { - FullName = @"Spain", - FlagName = @"ES", - }, - }, - }, - new Score - { - Rank = ScoreRank.X, - Accuracy = 1, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 4608074, - Username = @"Skycries", - Country = new Country - { - FullName = @"Brazil", - FlagName = @"BR", - }, - }, - }, - new Score - { - Rank = ScoreRank.SH, - Accuracy = 1, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 1014222, - Username = @"eLy", - Country = new Country - { - FullName = @"Japan", - FlagName = @"JP", - }, - }, - }, - new Score - { - Rank = ScoreRank.S, - Accuracy = 1, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 1541390, - Username = @"Toukai", - Country = new Country - { - FullName = @"Canada", - FlagName = @"CA", - }, - }, - }, - new Score - { - Rank = ScoreRank.A, - Accuracy = 1, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 2243452, - Username = @"Satoruu", - Country = new Country - { - FullName = @"Venezuela", - FlagName = @"VE", - }, - }, - }, - new Score - { - Rank = ScoreRank.B, - Accuracy = 0.9826, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 2705430, - Username = @"Mooha", - Country = new Country - { - FullName = @"France", - FlagName = @"FR", - }, - }, - }, - new Score - { - Rank = ScoreRank.C, - Accuracy = 0.9654, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 7151382, - Username = @"Mayuri Hana", - Country = new Country - { - FullName = @"Thailand", - FlagName = @"TH", - }, - }, - }, - new Score - { - Rank = ScoreRank.F, - Accuracy = 0.6025, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 2051389, - Username = @"FunOrange", - Country = new Country - { - FullName = @"Canada", - FlagName = @"CA", - }, - }, - }, - new Score - { - Rank = ScoreRank.F, - Accuracy = 0.5140, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 6169483, - Username = @"-Hebel-", - Country = new Country - { - FullName = @"Mexico", - FlagName = @"MX", - }, - }, - }, - new Score - { - Rank = ScoreRank.F, - Accuracy = 0.4222, - MaxCombo = 244, - TotalScore = 1707827, - //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - User = new User - { - Id = 6702666, - Username = @"prhtnsm", - Country = new Country - { - FullName = @"Germany", - FlagName = @"DE", - }, - }, - }, - }; - - leaderboard.Scores = scores; - } - - private void realBeatmap() - { - leaderboard.Beatmap = new BeatmapInfo - { - StarDifficulty = 1.36, - Version = @"BASIC", - OnlineBeatmapID = 1113057, - Ruleset = rulesets.GetRuleset(0), - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 6.5f, - OverallDifficulty = 6.5f, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 115000, - CircleCount = 265, - SliderCount = 71, - PlayCount = 47906, - PassCount = 19899, - }, - Metrics = new BeatmapMetrics - { - Ratings = Enumerable.Range(0, 11), - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), - }, - }; - } - - private class FailableLeaderboard : Leaderboard - { - public void SetRetrievalState(PlaceholderState state) - { - PlaceholderState = state; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Users; +using osu.Framework.Allocation; +using OpenTK; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; + +namespace osu.Game.Tests.Visual +{ + [Description("PlaySongSelect leaderboard")] + public class TestCaseLeaderboard : OsuTestCase + { + private RulesetStore rulesets; + + private readonly FailableLeaderboard leaderboard; + + public TestCaseLeaderboard() + { + Add(leaderboard = new FailableLeaderboard + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(550f, 450f), + Scope = LeaderboardScope.Global, + }); + + AddStep(@"New Scores", newScores); + AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores)); + AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure)); + AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter)); + AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn)); + AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable)); + AddStep(@"Real beatmap", realBeatmap); + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + this.rulesets = rulesets; + } + + private void newScores() + { + var scores = new[] + { + new Score + { + Rank = ScoreRank.XH, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 6602580, + Username = @"waaiiru", + Country = new Country + { + FullName = @"Spain", + FlagName = @"ES", + }, + }, + }, + new Score + { + Rank = ScoreRank.X, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 4608074, + Username = @"Skycries", + Country = new Country + { + FullName = @"Brazil", + FlagName = @"BR", + }, + }, + }, + new Score + { + Rank = ScoreRank.SH, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 1014222, + Username = @"eLy", + Country = new Country + { + FullName = @"Japan", + FlagName = @"JP", + }, + }, + }, + new Score + { + Rank = ScoreRank.S, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 1541390, + Username = @"Toukai", + Country = new Country + { + FullName = @"Canada", + FlagName = @"CA", + }, + }, + }, + new Score + { + Rank = ScoreRank.A, + Accuracy = 1, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 2243452, + Username = @"Satoruu", + Country = new Country + { + FullName = @"Venezuela", + FlagName = @"VE", + }, + }, + }, + new Score + { + Rank = ScoreRank.B, + Accuracy = 0.9826, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 2705430, + Username = @"Mooha", + Country = new Country + { + FullName = @"France", + FlagName = @"FR", + }, + }, + }, + new Score + { + Rank = ScoreRank.C, + Accuracy = 0.9654, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 7151382, + Username = @"Mayuri Hana", + Country = new Country + { + FullName = @"Thailand", + FlagName = @"TH", + }, + }, + }, + new Score + { + Rank = ScoreRank.F, + Accuracy = 0.6025, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 2051389, + Username = @"FunOrange", + Country = new Country + { + FullName = @"Canada", + FlagName = @"CA", + }, + }, + }, + new Score + { + Rank = ScoreRank.F, + Accuracy = 0.5140, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 6169483, + Username = @"-Hebel-", + Country = new Country + { + FullName = @"Mexico", + FlagName = @"MX", + }, + }, + }, + new Score + { + Rank = ScoreRank.F, + Accuracy = 0.4222, + MaxCombo = 244, + TotalScore = 1707827, + //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + User = new User + { + Id = 6702666, + Username = @"prhtnsm", + Country = new Country + { + FullName = @"Germany", + FlagName = @"DE", + }, + }, + }, + }; + + leaderboard.Scores = scores; + } + + private void realBeatmap() + { + leaderboard.Beatmap = new BeatmapInfo + { + StarDifficulty = 1.36, + Version = @"BASIC", + OnlineBeatmapID = 1113057, + Ruleset = rulesets.GetRuleset(0), + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 4, + DrainRate = 6.5f, + OverallDifficulty = 6.5f, + ApproachRate = 5, + }, + OnlineInfo = new BeatmapOnlineInfo + { + Length = 115000, + CircleCount = 265, + SliderCount = 71, + PlayCount = 47906, + PassCount = 19899, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 11), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, + }; + } + + private class FailableLeaderboard : Leaderboard + { + public void SetRetrievalState(PlaceholderState state) + { + PlaceholderState = state; + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs b/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs index 8d91a0f0dd..c2df8d33ca 100644 --- a/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Game.Overlays; -using osu.Game.Overlays.MedalSplash; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseMedalOverlay : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(MedalOverlay), - typeof(DrawableMedal), - }; - - public TestCaseMedalOverlay() - { - AddStep(@"display", () => - { - LoadComponentAsync(new MedalOverlay(new Medal - { - Name = @"Animations", - InternalName = @"all-intro-doubletime", - Description = @"More complex than you think.", - }), Add); - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Overlays; +using osu.Game.Overlays.MedalSplash; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseMedalOverlay : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(MedalOverlay), + typeof(DrawableMedal), + }; + + public TestCaseMedalOverlay() + { + AddStep(@"display", () => + { + LoadComponentAsync(new MedalOverlay(new Medal + { + Name = @"Animations", + InternalName = @"all-intro-doubletime", + Description = @"More complex than you think.", + }), Add); + }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs index 039d8bfdb6..dad8fb8fed 100644 --- a/osu.Game.Tests/Visual/TestCaseMods.cs +++ b/osu.Game.Tests/Visual/TestCaseMods.cs @@ -1,233 +1,233 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Overlays.Mods; -using osu.Game.Rulesets; -using osu.Game.Screens.Play.HUD; -using OpenTK; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using System.Linq; -using System.Collections.Generic; -using osu.Game.Rulesets.Osu; -using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mania; -using osu.Game.Rulesets.Mania.Mods; -using OpenTK.Graphics; - -namespace osu.Game.Tests.Visual -{ - [Description("mod select and icon display")] - public class TestCaseMods : OsuTestCase - { - private const string unranked_suffix = " (Unranked)"; - - private RulesetStore rulesets; - private ModDisplay modDisplay; - private TestModSelectOverlay modSelect; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(modSelect = new TestModSelectOverlay - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - }); - - Add(modDisplay = new ModDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Position = new Vector2(0, 25), - }); - - modDisplay.Current.BindTo(modSelect.SelectedMods); - - AddStep("Toggle", modSelect.ToggleVisibility); - AddStep("Hide", modSelect.Hide); - AddStep("Show", modSelect.Show); - - foreach (var rulesetInfo in rulesets.AvailableRulesets) - { - Ruleset ruleset = rulesetInfo.CreateInstance(); - AddStep($"switch to {ruleset.Description}", () => modSelect.Ruleset.Value = rulesetInfo); - - switch (ruleset) { - case OsuRuleset or: - testOsuMods(or); - break; - case ManiaRuleset mr: - testManiaMods(mr); - break; - } - } - } - - private void testOsuMods(OsuRuleset ruleset) - { - var easierMods = ruleset.GetModsFor(ModType.DifficultyReduction); - var harderMods = ruleset.GetModsFor(ModType.DifficultyIncrease); - var assistMods = ruleset.GetModsFor(ModType.Special); - - var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail); - var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden); - - var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); - - var autoPilotMod = assistMods.FirstOrDefault(m => m is OsuModAutopilot); - - var easy = easierMods.FirstOrDefault(m => m is OsuModEasy); - var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock); - - testSingleMod(noFailMod); - testMultiMod(doubleTimeMod); - testIncompatibleMods(easy, hardRock); - testDeselectAll(easierMods.Where(m => !(m is MultiMod))); - testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour); - testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour); - - testUnimplmentedMod(autoPilotMod); - } - - private void testManiaMods(ManiaRuleset ruleset) - { - testMultiplierTextUnranked(ruleset.GetModsFor(ModType.Special).First(m => m is ManiaModRandom)); - } - - private void testSingleMod(Mod mod) - { - selectNext(mod); - checkSelected(mod); - - selectPrevious(mod); - checkNotSelected(mod); - - selectNext(mod); - selectNext(mod); - checkNotSelected(mod); - - selectPrevious(mod); - selectPrevious(mod); - checkNotSelected(mod); - } - - private void testMultiMod(MultiMod multiMod) - { - foreach (var mod in multiMod.Mods) - { - selectNext(mod); - checkSelected(mod); - } - - for (int index = multiMod.Mods.Length - 1; index >= 0; index--) - selectPrevious(multiMod.Mods[index]); - - foreach (var mod in multiMod.Mods) - checkNotSelected(mod); - } - - private void testUnimplmentedMod(Mod mod) - { - selectNext(mod); - checkNotSelected(mod); - } - - private void testIncompatibleMods(Mod modA, Mod modB) - { - selectNext(modA); - checkSelected(modA); - checkNotSelected(modB); - - selectNext(modB); - checkSelected(modB); - checkNotSelected(modA); - - selectPrevious(modB); - checkNotSelected(modA); - checkNotSelected(modB); - } - - private void testDeselectAll(IEnumerable mods) - { - foreach (var mod in mods) - selectNext(mod); - - AddAssert("check for any selection", () => modSelect.SelectedMods.Value.Any()); - AddStep("deselect all", modSelect.DeselectAllButton.Action.Invoke); - AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any()); - } - - private void testMultiplierTextColour(Mod mod, Color4 colour) - { - checkLabelColor(Color4.White); - selectNext(mod); - AddWaitStep(1, "wait for changing colour"); - checkLabelColor(colour); - selectPrevious(mod); - AddWaitStep(1, "wait for changing colour"); - checkLabelColor(Color4.White); - } - - private void testMultiplierTextUnranked(Mod mod) - { - AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix)); - selectNext(mod); - AddAssert("check for unranked", () => modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix)); - selectPrevious(mod); - AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix)); - } - - private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1)); - - private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1)); - - private void checkSelected(Mod mod) - { - AddAssert($"check {mod.Name} is selected", () => - { - var button = modSelect.GetModButton(mod); - return modSelect.SelectedMods.Value.Single(m => m.Name == mod.Name) != null && button.SelectedMod.GetType() == mod.GetType() && button.Selected; - }); - } - - private void checkNotSelected(Mod mod) - { - AddAssert($"check {mod.Name} is not selected", () => - { - var button = modSelect.GetModButton(mod); - return modSelect.SelectedMods.Value.All(m => m.GetType() != mod.GetType()) && button.SelectedMod?.GetType() != mod.GetType(); - }); - } - - private void checkLabelColor(Color4 color) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == color); - - private class TestModSelectOverlay : ModSelectOverlay - { - public ModButton GetModButton(Mod mod) - { - var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type); - return section.ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())); - } - - public new OsuSpriteText MultiplierLabel => base.MultiplierLabel; - public new TriangleButton DeselectAllButton => base.DeselectAllButton; - - public new Color4 LowMultiplierColour => base.LowMultiplierColour; - public new Color4 HighMultiplierColour => base.HighMultiplierColour; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets; +using osu.Game.Screens.Play.HUD; +using OpenTK; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using System.Linq; +using System.Collections.Generic; +using osu.Game.Rulesets.Osu; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Mania.Mods; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + [Description("mod select and icon display")] + public class TestCaseMods : OsuTestCase + { + private const string unranked_suffix = " (Unranked)"; + + private RulesetStore rulesets; + private ModDisplay modDisplay; + private TestModSelectOverlay modSelect; + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + this.rulesets = rulesets; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(modSelect = new TestModSelectOverlay + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + }); + + Add(modDisplay = new ModDisplay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Position = new Vector2(0, 25), + }); + + modDisplay.Current.BindTo(modSelect.SelectedMods); + + AddStep("Toggle", modSelect.ToggleVisibility); + AddStep("Hide", modSelect.Hide); + AddStep("Show", modSelect.Show); + + foreach (var rulesetInfo in rulesets.AvailableRulesets) + { + Ruleset ruleset = rulesetInfo.CreateInstance(); + AddStep($"switch to {ruleset.Description}", () => modSelect.Ruleset.Value = rulesetInfo); + + switch (ruleset) { + case OsuRuleset or: + testOsuMods(or); + break; + case ManiaRuleset mr: + testManiaMods(mr); + break; + } + } + } + + private void testOsuMods(OsuRuleset ruleset) + { + var easierMods = ruleset.GetModsFor(ModType.DifficultyReduction); + var harderMods = ruleset.GetModsFor(ModType.DifficultyIncrease); + var assistMods = ruleset.GetModsFor(ModType.Special); + + var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail); + var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden); + + var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); + + var autoPilotMod = assistMods.FirstOrDefault(m => m is OsuModAutopilot); + + var easy = easierMods.FirstOrDefault(m => m is OsuModEasy); + var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock); + + testSingleMod(noFailMod); + testMultiMod(doubleTimeMod); + testIncompatibleMods(easy, hardRock); + testDeselectAll(easierMods.Where(m => !(m is MultiMod))); + testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour); + testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour); + + testUnimplmentedMod(autoPilotMod); + } + + private void testManiaMods(ManiaRuleset ruleset) + { + testMultiplierTextUnranked(ruleset.GetModsFor(ModType.Special).First(m => m is ManiaModRandom)); + } + + private void testSingleMod(Mod mod) + { + selectNext(mod); + checkSelected(mod); + + selectPrevious(mod); + checkNotSelected(mod); + + selectNext(mod); + selectNext(mod); + checkNotSelected(mod); + + selectPrevious(mod); + selectPrevious(mod); + checkNotSelected(mod); + } + + private void testMultiMod(MultiMod multiMod) + { + foreach (var mod in multiMod.Mods) + { + selectNext(mod); + checkSelected(mod); + } + + for (int index = multiMod.Mods.Length - 1; index >= 0; index--) + selectPrevious(multiMod.Mods[index]); + + foreach (var mod in multiMod.Mods) + checkNotSelected(mod); + } + + private void testUnimplmentedMod(Mod mod) + { + selectNext(mod); + checkNotSelected(mod); + } + + private void testIncompatibleMods(Mod modA, Mod modB) + { + selectNext(modA); + checkSelected(modA); + checkNotSelected(modB); + + selectNext(modB); + checkSelected(modB); + checkNotSelected(modA); + + selectPrevious(modB); + checkNotSelected(modA); + checkNotSelected(modB); + } + + private void testDeselectAll(IEnumerable mods) + { + foreach (var mod in mods) + selectNext(mod); + + AddAssert("check for any selection", () => modSelect.SelectedMods.Value.Any()); + AddStep("deselect all", modSelect.DeselectAllButton.Action.Invoke); + AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any()); + } + + private void testMultiplierTextColour(Mod mod, Color4 colour) + { + checkLabelColor(Color4.White); + selectNext(mod); + AddWaitStep(1, "wait for changing colour"); + checkLabelColor(colour); + selectPrevious(mod); + AddWaitStep(1, "wait for changing colour"); + checkLabelColor(Color4.White); + } + + private void testMultiplierTextUnranked(Mod mod) + { + AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix)); + selectNext(mod); + AddAssert("check for unranked", () => modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix)); + selectPrevious(mod); + AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix)); + } + + private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1)); + + private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1)); + + private void checkSelected(Mod mod) + { + AddAssert($"check {mod.Name} is selected", () => + { + var button = modSelect.GetModButton(mod); + return modSelect.SelectedMods.Value.Single(m => m.Name == mod.Name) != null && button.SelectedMod.GetType() == mod.GetType() && button.Selected; + }); + } + + private void checkNotSelected(Mod mod) + { + AddAssert($"check {mod.Name} is not selected", () => + { + var button = modSelect.GetModButton(mod); + return modSelect.SelectedMods.Value.All(m => m.GetType() != mod.GetType()) && button.SelectedMod?.GetType() != mod.GetType(); + }); + } + + private void checkLabelColor(Color4 color) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == color); + + private class TestModSelectOverlay : ModSelectOverlay + { + public ModButton GetModButton(Mod mod) + { + var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type); + return section.ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())); + } + + public new OsuSpriteText MultiplierLabel => base.MultiplierLabel; + public new TriangleButton DeselectAllButton => base.DeselectAllButton; + + public new Color4 LowMultiplierColour => base.LowMultiplierColour; + public new Color4 HighMultiplierColour => base.HighMultiplierColour; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseMusicController.cs b/osu.Game.Tests/Visual/TestCaseMusicController.cs index 2ddc57d7b4..10c813b2f8 100644 --- a/osu.Game.Tests/Visual/TestCaseMusicController.cs +++ b/osu.Game.Tests/Visual/TestCaseMusicController.cs @@ -1,42 +1,42 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Timing; -using osu.Game.Beatmaps; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseMusicController : OsuTestCase - { - private readonly Bindable beatmapBacking = new Bindable(); - - public TestCaseMusicController() - { - Clock = new FramedClock(); - - var mc = new MusicController - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre - }; - Add(mc); - - AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden); - AddStep(@"show", () => mc.State = Visibility.Visible); - AddToggleStep(@"toggle beatmap lock", state => beatmapBacking.Disabled = state); - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase game) - { - beatmapBacking.BindTo(game.Beatmap); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseMusicController : OsuTestCase + { + private readonly Bindable beatmapBacking = new Bindable(); + + public TestCaseMusicController() + { + Clock = new FramedClock(); + + var mc = new MusicController + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre + }; + Add(mc); + + AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden); + AddStep(@"show", () => mc.State = Visibility.Visible); + AddToggleStep(@"toggle beatmap lock", state => beatmapBacking.Disabled = state); + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + beatmapBacking.BindTo(game.Beatmap); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs index 2ba57f2bd2..2c56f08f42 100644 --- a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs @@ -1,166 +1,166 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; -using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseNotificationOverlay : OsuTestCase - { - private readonly NotificationOverlay manager; - private readonly List progressingNotifications = new List(); - - public override IReadOnlyList RequiredTypes => new[] - { - typeof(NotificationSection), - typeof(SimpleNotification), - typeof(ProgressNotification), - typeof(ProgressCompletionNotification), - typeof(IHasCompletionTarget), - typeof(Notification) - }; - - public TestCaseNotificationOverlay() - { - progressingNotifications.Clear(); - - Content.Add(manager = new NotificationOverlay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }); - - SpriteText displayedCount = new SpriteText(); - - Content.Add(displayedCount); - - void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state); - void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected); - - manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; }; - - - setState(Visibility.Visible); - AddStep(@"simple #1", sendHelloNotification); - AddStep(@"simple #2", sendAmazingNotification); - AddStep(@"progress #1", sendUploadProgress); - AddStep(@"progress #2", sendDownloadProgress); - - checkProgressingCount(2); - - setState(Visibility.Hidden); - - AddRepeatStep(@"add many simple", sendManyNotifications, 3); - AddWaitStep(5); - - checkProgressingCount(0); - - AddStep(@"progress #3", sendUploadProgress); - - checkProgressingCount(1); - - AddAssert("Displayed count is 33", () => manager.UnreadCount.Value == 33); - - AddWaitStep(10); - - checkProgressingCount(0); - - - setState(Visibility.Visible); - - //AddStep(@"barrage", () => sendBarrage()); - } - - private void sendBarrage(int remaining = 10) - { - switch (RNG.Next(0, 4)) - { - case 0: - sendHelloNotification(); - break; - case 1: - sendAmazingNotification(); - break; - case 2: - sendUploadProgress(); - break; - case 3: - sendDownloadProgress(); - break; - } - - if (remaining > 0) - Scheduler.AddDelayed(() => sendBarrage(remaining - 1), 80); - } - - protected override void Update() - { - base.Update(); - - progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed); - - if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) - { - var p = progressingNotifications.FirstOrDefault(n => n.State == ProgressNotificationState.Queued); - if (p != null) - p.State = ProgressNotificationState.Active; - } - - foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active)) - { - if (n.Progress < 1) - n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle(); - else - n.State = ProgressNotificationState.Completed; - } - } - - private void sendDownloadProgress() - { - var n = new ProgressNotification - { - Text = @"Downloading Haitai...", - CompletionText = "Downloaded Haitai!", - }; - manager.Post(n); - progressingNotifications.Add(n); - } - - private void sendUploadProgress() - { - var n = new ProgressNotification - { - Text = @"Uploading to BSS...", - CompletionText = "Uploaded to BSS!", - }; - manager.Post(n); - progressingNotifications.Add(n); - } - - private void sendAmazingNotification() - { - manager.Post(new SimpleNotification { Text = @"You are amazing" }); - } - - private void sendHelloNotification() - { - manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" }); - } - - private void sendManyNotifications() - { - for (int i = 0; i < 10; i++) - manager.Post(new SimpleNotification { Text = @"Spam incoming!!" }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.MathUtils; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseNotificationOverlay : OsuTestCase + { + private readonly NotificationOverlay manager; + private readonly List progressingNotifications = new List(); + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(NotificationSection), + typeof(SimpleNotification), + typeof(ProgressNotification), + typeof(ProgressCompletionNotification), + typeof(IHasCompletionTarget), + typeof(Notification) + }; + + public TestCaseNotificationOverlay() + { + progressingNotifications.Clear(); + + Content.Add(manager = new NotificationOverlay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }); + + SpriteText displayedCount = new SpriteText(); + + Content.Add(displayedCount); + + void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state); + void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected); + + manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; }; + + + setState(Visibility.Visible); + AddStep(@"simple #1", sendHelloNotification); + AddStep(@"simple #2", sendAmazingNotification); + AddStep(@"progress #1", sendUploadProgress); + AddStep(@"progress #2", sendDownloadProgress); + + checkProgressingCount(2); + + setState(Visibility.Hidden); + + AddRepeatStep(@"add many simple", sendManyNotifications, 3); + AddWaitStep(5); + + checkProgressingCount(0); + + AddStep(@"progress #3", sendUploadProgress); + + checkProgressingCount(1); + + AddAssert("Displayed count is 33", () => manager.UnreadCount.Value == 33); + + AddWaitStep(10); + + checkProgressingCount(0); + + + setState(Visibility.Visible); + + //AddStep(@"barrage", () => sendBarrage()); + } + + private void sendBarrage(int remaining = 10) + { + switch (RNG.Next(0, 4)) + { + case 0: + sendHelloNotification(); + break; + case 1: + sendAmazingNotification(); + break; + case 2: + sendUploadProgress(); + break; + case 3: + sendDownloadProgress(); + break; + } + + if (remaining > 0) + Scheduler.AddDelayed(() => sendBarrage(remaining - 1), 80); + } + + protected override void Update() + { + base.Update(); + + progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed); + + if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) + { + var p = progressingNotifications.FirstOrDefault(n => n.State == ProgressNotificationState.Queued); + if (p != null) + p.State = ProgressNotificationState.Active; + } + + foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active)) + { + if (n.Progress < 1) + n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle(); + else + n.State = ProgressNotificationState.Completed; + } + } + + private void sendDownloadProgress() + { + var n = new ProgressNotification + { + Text = @"Downloading Haitai...", + CompletionText = "Downloaded Haitai!", + }; + manager.Post(n); + progressingNotifications.Add(n); + } + + private void sendUploadProgress() + { + var n = new ProgressNotification + { + Text = @"Uploading to BSS...", + CompletionText = "Uploaded to BSS!", + }; + manager.Post(n); + progressingNotifications.Add(n); + } + + private void sendAmazingNotification() + { + manager.Post(new SimpleNotification { Text = @"You are amazing" }); + } + + private void sendHelloNotification() + { + manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" }); + } + + private void sendManyNotifications() + { + for (int i = 0; i < 10; i++) + manager.Post(new SimpleNotification { Text = @"Spam incoming!!" }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs index 6fe8bc5a8a..233c418d4a 100644 --- a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseOnScreenDisplay : OsuTestCase - { - private FrameworkConfigManager config; - private Bindable frameSyncMode; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(new OnScreenDisplay()); - - frameSyncMode = config.GetBindable(FrameworkSetting.FrameSync); - - FrameSync initial = frameSyncMode.Value; - - AddRepeatStep(@"Change frame limiter", setNextMode, 3); - - AddStep(@"Restore frame limiter", () => frameSyncMode.Value = initial); - } - - private void setNextMode() - { - var nextMode = frameSyncMode.Value + 1; - if (nextMode > FrameSync.Unlimited) - nextMode = FrameSync.VSync; - frameSyncMode.Value = nextMode; - } - - [BackgroundDependencyLoader] - private void load(FrameworkConfigManager config) - { - this.config = config; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseOnScreenDisplay : OsuTestCase + { + private FrameworkConfigManager config; + private Bindable frameSyncMode; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(new OnScreenDisplay()); + + frameSyncMode = config.GetBindable(FrameworkSetting.FrameSync); + + FrameSync initial = frameSyncMode.Value; + + AddRepeatStep(@"Change frame limiter", setNextMode, 3); + + AddStep(@"Restore frame limiter", () => frameSyncMode.Value = initial); + } + + private void setNextMode() + { + var nextMode = frameSyncMode.Value + 1; + if (nextMode > FrameSync.Unlimited) + nextMode = FrameSync.VSync; + frameSyncMode.Value = nextMode; + } + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager config) + { + this.config = config; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseOsuGame.cs b/osu.Game.Tests/Visual/TestCaseOsuGame.cs index a802db6a10..f1a21a58d5 100644 --- a/osu.Game.Tests/Visual/TestCaseOsuGame.cs +++ b/osu.Game.Tests/Visual/TestCaseOsuGame.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; -using osu.Game.Screens; -using osu.Game.Screens.Menu; -using OpenTK.Graphics; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseOsuGame : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(OsuLogo), - }; - - public TestCaseOsuGame() - { - var rateAdjustClock = new StopwatchClock(true); - var framedClock = new FramedClock(rateAdjustClock); - framedClock.ProcessFrame(); - - Add(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }); - - Add(new Loader()); - - AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; +using osu.Game.Screens; +using osu.Game.Screens.Menu; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseOsuGame : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(OsuLogo), + }; + + public TestCaseOsuGame() + { + var rateAdjustClock = new StopwatchClock(true); + var framedClock = new FramedClock(rateAdjustClock); + framedClock.ProcessFrame(); + + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }); + + Add(new Loader()); + + AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index 5fd8fcc9c3..8c52360db8 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -1,173 +1,173 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.MathUtils; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.Rulesets; -using osu.Game.Screens.Select; -using osu.Game.Screens.Select.Carousel; -using osu.Game.Screens.Select.Filter; -using osu.Game.Tests.Platform; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCasePlaySongSelect : OsuTestCase - { - private BeatmapManager manager; - - private RulesetStore rulesets; - - private DependencyContainer dependencies; - private WorkingBeatmap defaultBeatmap; - - public override IReadOnlyList RequiredTypes => new[] - { - typeof(SongSelect), - typeof(BeatmapCarousel), - - typeof(CarouselItem), - typeof(CarouselGroup), - typeof(CarouselGroupEagerSelect), - typeof(CarouselBeatmap), - typeof(CarouselBeatmapSet), - - typeof(DrawableCarouselItem), - typeof(CarouselItemState), - - typeof(DrawableCarouselBeatmap), - typeof(DrawableCarouselBeatmapSet), - }; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); - - private class TestSongSelect : PlaySongSelect - { - public WorkingBeatmap CurrentBeatmap => Beatmap.Value; - public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; - public new BeatmapCarousel Carousel => base.Carousel; - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase game) - { - TestSongSelect songSelect = null; - - var storage = new TestStorage(@"TestCasePlaySongSelect"); - - // this is by no means clean. should be replacing inside of OsuGameBase somehow. - IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext()); - - dependencies.Cache(rulesets = new RulesetStore(factory)); - dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null) - { - DefaultBeatmap = defaultBeatmap = game.Beatmap.Default - }); - - void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () => - { - if (deleteMaps) - { - manager.Delete(manager.GetAllUsableBeatmapSets()); - game.Beatmap.SetDefault(); - } - - if (songSelect != null) - { - Remove(songSelect); - songSelect.Dispose(); - } - - Add(songSelect = new TestSongSelect()); - }); - - loadNewSongSelect(true); - - AddWaitStep(3); - - AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap); - - AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap); - - AddStep("import test maps", () => - { - for (int i = 0; i < 100; i += 10) - manager.Import(createTestBeatmapSet(i)); - }); - - AddWaitStep(3); - AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); - - loadNewSongSelect(); - AddWaitStep(3); - AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); - - AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; }); - AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); - AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); - AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); - } - - private BeatmapSetInfo createTestBeatmapSet(int i) - { - return new BeatmapSetInfo - { - OnlineBeatmapSetID = 1234 + i, - Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(), - Metadata = new BeatmapMetadata - { - OnlineBeatmapSetID = 1234 + i, - // Create random metadata, then we can check if sorting works based on these - Artist = "MONACA " + RNG.Next(0, 9), - Title = "Black Song " + RNG.Next(0, 9), - AuthorString = "Some Guy " + RNG.Next(0, 9), - }, - Beatmaps = new List(new[] - { - new BeatmapInfo - { - OnlineBeatmapID = 1234 + i, - Ruleset = rulesets.AvailableRulesets.First(), - Path = "normal.osu", - Version = "Normal", - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 3.5f, - } - }, - new BeatmapInfo - { - OnlineBeatmapID = 1235 + i, - Ruleset = rulesets.AvailableRulesets.First(), - Path = "hard.osu", - Version = "Hard", - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 5, - } - }, - new BeatmapInfo - { - OnlineBeatmapID = 1236 + i, - Ruleset = rulesets.AvailableRulesets.First(), - Path = "insane.osu", - Version = "Insane", - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 7, - } - }, - }), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Rulesets; +using osu.Game.Screens.Select; +using osu.Game.Screens.Select.Carousel; +using osu.Game.Screens.Select.Filter; +using osu.Game.Tests.Platform; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCasePlaySongSelect : OsuTestCase + { + private BeatmapManager manager; + + private RulesetStore rulesets; + + private DependencyContainer dependencies; + private WorkingBeatmap defaultBeatmap; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(SongSelect), + typeof(BeatmapCarousel), + + typeof(CarouselItem), + typeof(CarouselGroup), + typeof(CarouselGroupEagerSelect), + typeof(CarouselBeatmap), + typeof(CarouselBeatmapSet), + + typeof(DrawableCarouselItem), + typeof(CarouselItemState), + + typeof(DrawableCarouselBeatmap), + typeof(DrawableCarouselBeatmapSet), + }; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); + + private class TestSongSelect : PlaySongSelect + { + public WorkingBeatmap CurrentBeatmap => Beatmap.Value; + public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; + public new BeatmapCarousel Carousel => base.Carousel; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + TestSongSelect songSelect = null; + + var storage = new TestStorage(@"TestCasePlaySongSelect"); + + // this is by no means clean. should be replacing inside of OsuGameBase somehow. + IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext()); + + dependencies.Cache(rulesets = new RulesetStore(factory)); + dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null) + { + DefaultBeatmap = defaultBeatmap = game.Beatmap.Default + }); + + void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () => + { + if (deleteMaps) + { + manager.Delete(manager.GetAllUsableBeatmapSets()); + game.Beatmap.SetDefault(); + } + + if (songSelect != null) + { + Remove(songSelect); + songSelect.Dispose(); + } + + Add(songSelect = new TestSongSelect()); + }); + + loadNewSongSelect(true); + + AddWaitStep(3); + + AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap); + + AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap); + + AddStep("import test maps", () => + { + for (int i = 0; i < 100; i += 10) + manager.Import(createTestBeatmapSet(i)); + }); + + AddWaitStep(3); + AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); + + loadNewSongSelect(); + AddWaitStep(3); + AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); + + AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; }); + AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); + AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); + AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); + } + + private BeatmapSetInfo createTestBeatmapSet(int i) + { + return new BeatmapSetInfo + { + OnlineBeatmapSetID = 1234 + i, + Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(), + Metadata = new BeatmapMetadata + { + OnlineBeatmapSetID = 1234 + i, + // Create random metadata, then we can check if sorting works based on these + Artist = "MONACA " + RNG.Next(0, 9), + Title = "Black Song " + RNG.Next(0, 9), + AuthorString = "Some Guy " + RNG.Next(0, 9), + }, + Beatmaps = new List(new[] + { + new BeatmapInfo + { + OnlineBeatmapID = 1234 + i, + Ruleset = rulesets.AvailableRulesets.First(), + Path = "normal.osu", + Version = "Normal", + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 3.5f, + } + }, + new BeatmapInfo + { + OnlineBeatmapID = 1235 + i, + Ruleset = rulesets.AvailableRulesets.First(), + Path = "hard.osu", + Version = "Hard", + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 5, + } + }, + new BeatmapInfo + { + OnlineBeatmapID = 1236 + i, + Ruleset = rulesets.AvailableRulesets.First(), + Path = "insane.osu", + Version = "Insane", + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = 7, + } + }, + }), + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs index 9cdb3e36e3..24ebb534c1 100644 --- a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs +++ b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs @@ -1,42 +1,42 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Timing; -using osu.Game.Beatmaps; -using osu.Game.Screens.Edit.Components; -using osu.Game.Tests.Beatmaps; -using OpenTK; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCasePlaybackControl : OsuTestCase - { - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); - - [BackgroundDependencyLoader] - private void load() - { - var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - dependencies.CacheAs(clock); - dependencies.CacheAs(clock); - - var playback = new PlaybackControl - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(200,100) - }; - - playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap()); - - Child = playback; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Screens.Edit.Components; +using osu.Game.Tests.Beatmaps; +using OpenTK; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCasePlaybackControl : OsuTestCase + { + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + + [BackgroundDependencyLoader] + private void load() + { + var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); + + var playback = new PlaybackControl + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200,100) + }; + + playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap()); + + Child = playback; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCasePopupDialog.cs b/osu.Game.Tests/Visual/TestCasePopupDialog.cs index 8d830672b7..d88be1e7c7 100644 --- a/osu.Game.Tests/Visual/TestCasePopupDialog.cs +++ b/osu.Game.Tests/Visual/TestCasePopupDialog.cs @@ -1,39 +1,39 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.Dialog; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCasePopupDialog : OsuTestCase - { - public TestCasePopupDialog() - { - var popup = new PopupDialog - { - RelativeSizeAxes = Axes.Both, - State = Framework.Graphics.Containers.Visibility.Visible, - Icon = FontAwesome.fa_assistive_listening_systems, - HeaderText = @"This is a test popup", - BodyText = "I can say lots of stuff and even wrap my words!", - Buttons = new PopupDialogButton[] - { - new PopupDialogCancelButton - { - Text = @"Yes. That you can.", - }, - new PopupDialogOkButton - { - Text = @"You're a fake!", - }, - } - }; - - Add(popup); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCasePopupDialog : OsuTestCase + { + public TestCasePopupDialog() + { + var popup = new PopupDialog + { + RelativeSizeAxes = Axes.Both, + State = Framework.Graphics.Containers.Visibility.Visible, + Icon = FontAwesome.fa_assistive_listening_systems, + HeaderText = @"This is a test popup", + BodyText = "I can say lots of stuff and even wrap my words!", + Buttons = new PopupDialogButton[] + { + new PopupDialogCancelButton + { + Text = @"Yes. That you can.", + }, + new PopupDialogOkButton + { + Text = @"You're a fake!", + }, + } + }; + + Add(popup); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseRankGraph.cs b/osu.Game.Tests/Visual/TestCaseRankGraph.cs index ad53238e76..45f6651537 100644 --- a/osu.Game.Tests/Visual/TestCaseRankGraph.cs +++ b/osu.Game.Tests/Visual/TestCaseRankGraph.cs @@ -1,125 +1,125 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Overlays.Profile; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using System.Collections.Generic; -using System; -using NUnit.Framework; -using osu.Game.Graphics.UserInterface; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseRankGraph : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(RankGraph), - typeof(LineGraph) - }; - - public TestCaseRankGraph() - { - RankGraph graph; - - var data = new int[89]; - var dataWithZeros = new int[89]; - var smallData = new int[89]; - - for (int i = 0; i < 89; i++) - data[i] = dataWithZeros[i] = (i + 1) * 1000; - - for (int i = 20; i < 60; i++) - dataWithZeros[i] = 0; - - for (int i = 79; i < 89; i++) - smallData[i] = 100000 - i * 1000; - - Add(new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(300, 150), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f) - }, - graph = new RankGraph - { - RelativeSizeAxes = Axes.Both, - } - } - }); - - AddStep("null user", () => graph.User.Value = null); - AddStep("rank only", () => - { - graph.User.Value = new User - { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 123456 }, - PP = 12345, - } - }; - }); - - AddStep("with rank history", () => - { - graph.User.Value = new User - { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 89000 }, - PP = 12345, - }, - RankHistory = new User.RankHistoryData - { - Data = data, - } - }; - }); - - AddStep("with zero values", () => - { - graph.User.Value = new User - { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 89000 }, - PP = 12345, - }, - RankHistory = new User.RankHistoryData - { - Data = dataWithZeros, - } - }; - }); - - AddStep("small amount of data", () => - { - graph.User.Value = new User - { - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 12000 }, - PP = 12345, - }, - RankHistory = new User.RankHistoryData - { - Data = smallData, - } - }; - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Overlays.Profile; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using System.Collections.Generic; +using System; +using NUnit.Framework; +using osu.Game.Graphics.UserInterface; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseRankGraph : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(RankGraph), + typeof(LineGraph) + }; + + public TestCaseRankGraph() + { + RankGraph graph; + + var data = new int[89]; + var dataWithZeros = new int[89]; + var smallData = new int[89]; + + for (int i = 0; i < 89; i++) + data[i] = dataWithZeros[i] = (i + 1) * 1000; + + for (int i = 20; i < 60; i++) + dataWithZeros[i] = 0; + + for (int i = 79; i < 89; i++) + smallData[i] = 100000 - i * 1000; + + Add(new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(300, 150), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }, + graph = new RankGraph + { + RelativeSizeAxes = Axes.Both, + } + } + }); + + AddStep("null user", () => graph.User.Value = null); + AddStep("rank only", () => + { + graph.User.Value = new User + { + Statistics = new UserStatistics + { + Ranks = new UserStatistics.UserRanks { Global = 123456 }, + PP = 12345, + } + }; + }); + + AddStep("with rank history", () => + { + graph.User.Value = new User + { + Statistics = new UserStatistics + { + Ranks = new UserStatistics.UserRanks { Global = 89000 }, + PP = 12345, + }, + RankHistory = new User.RankHistoryData + { + Data = data, + } + }; + }); + + AddStep("with zero values", () => + { + graph.User.Value = new User + { + Statistics = new UserStatistics + { + Ranks = new UserStatistics.UserRanks { Global = 89000 }, + PP = 12345, + }, + RankHistory = new User.RankHistoryData + { + Data = dataWithZeros, + } + }; + }); + + AddStep("small amount of data", () => + { + graph.User.Value = new User + { + Statistics = new UserStatistics + { + Ranks = new UserStatistics.UserRanks { Global = 12000 }, + PP = 12345, + }, + RankHistory = new User.RankHistoryData + { + Data = smallData, + } + }; + }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseReplay.cs b/osu.Game.Tests/Visual/TestCaseReplay.cs index 115ac11919..6ba671c7fc 100644 --- a/osu.Game.Tests/Visual/TestCaseReplay.cs +++ b/osu.Game.Tests/Visual/TestCaseReplay.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Play; - -namespace osu.Game.Tests.Visual -{ - [Description("Player instantiated with a replay.")] - public class TestCaseReplay : TestCasePlayer - { - protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) - { - // We create a dummy RulesetContainer just to get the replay - we don't want to use mods here - // to simulate setting a replay rather than having the replay already set for us - beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); - var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap, beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo)); - - // We have the replay - var replay = dummyRulesetContainer.Replay; - - // Reset the mods - beatmap.Mods.Value = beatmap.Mods.Value.Where(m => !(m is ModAutoplay)); - - return new ReplayPlayer(replay) - { - InitialBeatmap = beatmap - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + [Description("Player instantiated with a replay.")] + public class TestCaseReplay : TestCasePlayer + { + protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) + { + // We create a dummy RulesetContainer just to get the replay - we don't want to use mods here + // to simulate setting a replay rather than having the replay already set for us + beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); + var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap, beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo)); + + // We have the replay + var replay = dummyRulesetContainer.Replay; + + // Reset the mods + beatmap.Mods.Value = beatmap.Mods.Value.Where(m => !(m is ModAutoplay)); + + return new ReplayPlayer(replay) + { + InitialBeatmap = beatmap + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs index a1b683b64c..a57cc5b79d 100644 --- a/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs @@ -1,52 +1,52 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Play.HUD; -using osu.Game.Screens.Play.PlayerSettings; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseReplaySettingsOverlay : OsuTestCase - { - public TestCaseReplaySettingsOverlay() - { - ExampleContainer container; - - Add(new PlayerSettingsOverlay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }); - - Add(container = new ExampleContainer()); - - AddStep(@"Add button", () => container.Add(new TriangleButton - { - RelativeSizeAxes = Axes.X, - Text = @"Button", - })); - - AddStep(@"Add checkbox", () => container.Add(new PlayerCheckbox - { - LabelText = "Checkbox", - })); - - AddStep(@"Add textbox", () => container.Add(new FocusedTextBox - { - RelativeSizeAxes = Axes.X, - Height = 30, - PlaceholderText = "Textbox", - HoldFocus = false, - })); - } - - private class ExampleContainer : PlayerSettingsGroup - { - protected override string Title => @"example"; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Play.PlayerSettings; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseReplaySettingsOverlay : OsuTestCase + { + public TestCaseReplaySettingsOverlay() + { + ExampleContainer container; + + Add(new PlayerSettingsOverlay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }); + + Add(container = new ExampleContainer()); + + AddStep(@"Add button", () => container.Add(new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = @"Button", + })); + + AddStep(@"Add checkbox", () => container.Add(new PlayerCheckbox + { + LabelText = "Checkbox", + })); + + AddStep(@"Add textbox", () => container.Add(new FocusedTextBox + { + RelativeSizeAxes = Axes.X, + Height = 30, + PlaceholderText = "Textbox", + HoldFocus = false, + })); + } + + private class ExampleContainer : PlayerSettingsGroup + { + protected override string Title => @"example"; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseResults.cs b/osu.Game.Tests/Visual/TestCaseResults.cs index 06bdfdb7e1..35e1db7c9e 100644 --- a/osu.Game.Tests/Visual/TestCaseResults.cs +++ b/osu.Game.Tests/Visual/TestCaseResults.cs @@ -1,72 +1,72 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Ranking; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseResults : OsuTestCase - { - private BeatmapManager beatmaps; - - public override IReadOnlyList RequiredTypes => new[] - { - typeof(Score), - typeof(Results), - typeof(ResultsPage), - typeof(ResultsPageScore), - typeof(ResultsPageRanking) - }; - - [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps) - { - this.beatmaps = beatmaps; - } - - private WorkingBeatmap beatmap; - - protected override void LoadComplete() - { - base.LoadComplete(); - - if (beatmap == null) - { - var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0); - if (beatmapInfo != null) - beatmap = beatmaps.GetWorkingBeatmap(beatmapInfo); - } - - Add(new Results(new Score - { - TotalScore = 2845370, - Accuracy = 0.98, - MaxCombo = 123, - Rank = ScoreRank.A, - Date = DateTimeOffset.Now, - Statistics = new Dictionary - { - { HitResult.Great, 50 }, - { HitResult.Good, 20 }, - { HitResult.Meh, 50 }, - { HitResult.Miss, 1 } - }, - User = new User - { - Username = "peppy", - } - }) - { - InitialBeatmap = beatmap - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Ranking; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseResults : OsuTestCase + { + private BeatmapManager beatmaps; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(Score), + typeof(Results), + typeof(ResultsPage), + typeof(ResultsPageScore), + typeof(ResultsPageRanking) + }; + + [BackgroundDependencyLoader] + private void load(BeatmapManager beatmaps) + { + this.beatmaps = beatmaps; + } + + private WorkingBeatmap beatmap; + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (beatmap == null) + { + var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0); + if (beatmapInfo != null) + beatmap = beatmaps.GetWorkingBeatmap(beatmapInfo); + } + + Add(new Results(new Score + { + TotalScore = 2845370, + Accuracy = 0.98, + MaxCombo = 123, + Rank = ScoreRank.A, + Date = DateTimeOffset.Now, + Statistics = new Dictionary + { + { HitResult.Great, 50 }, + { HitResult.Good, 20 }, + { HitResult.Meh, 50 }, + { HitResult.Miss, 1 } + }, + User = new User + { + Username = "peppy", + } + }) + { + InitialBeatmap = beatmap + }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs index c45312392f..88059d2dcf 100644 --- a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs +++ b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs @@ -1,143 +1,143 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Screens.Multiplayer; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseRoomInspector : OsuTestCase - { - private RulesetStore rulesets; - - protected override void LoadComplete() - { - base.LoadComplete(); - - var room = new Room - { - Name = { Value = @"My Awesome Room" }, - Host = { Value = new User { Username = @"flyte", Id = 3103765, Country = new Country { FlagName = @"JP" } } }, - Status = { Value = new RoomStatusOpen() }, - Type = { Value = new GameTypeTeamVersus() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 3.7, - Ruleset = rulesets.GetRuleset(3), - Metadata = new BeatmapMetadata - { - Title = @"Platina", - Artist = @"Maaya Sakamoto", - AuthorString = @"uwutm8", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/560573/covers/cover.jpg?1492722343", - }, - }, - }, - } - }, - MaxParticipants = { Value = 200 }, - Participants = - { - Value = new[] - { - new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } }, - new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } }, - new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } }, - new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } }, - new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } }, - new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } }, - } - } - }; - - RoomInspector inspector; - Add(inspector = new RoomInspector - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Room = room, - }); - - AddStep(@"change title", () => room.Name.Value = @"A Better Room Than The Above"); - AddStep(@"change host", () => room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } }); - AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying()); - AddStep(@"change type", () => room.Type.Value = new GameTypeTag()); - AddStep(@"change beatmap", () => room.Beatmap.Value = null); - AddStep(@"change max participants", () => room.MaxParticipants.Value = null); - AddStep(@"change participants", () => room.Participants.Value = new[] - { - new User { Username = @"filsdelama", Id = 2831793, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 854 } } }, - new User { Username = @"_index", Id = 652457, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 150 } } } - }); - - AddStep(@"change room", () => - { - var newRoom = new Room - { - Name = { Value = @"My New, Better Than Ever Room" }, - Host = { Value = new User { Username = @"Angelsim", Id = 1777162, Country = new Country { FlagName = @"KR" } } }, - Status = { Value = new RoomStatusOpen() }, - Type = { Value = new GameTypeTagTeam() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 7.07, - Ruleset = rulesets.GetRuleset(0), - Metadata = new BeatmapMetadata - { - Title = @"FREEDOM DIVE", - Artist = @"xi", - AuthorString = @"Nakagawa-Kanon", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/39804/covers/cover.jpg?1456506845", - }, - }, - }, - }, - }, - MaxParticipants = { Value = 10 }, - Participants = - { - Value = new[] - { - new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 4 } } }, - new User { Username = @"HappyStick", Id = 256802, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 752 } } }, - new User { Username = @"-Konpaku-", Id = 2258797, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 571 } } } - } - } - }; - - inspector.Room = newRoom; - }); - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets; +using osu.Game.Screens.Multiplayer; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseRoomInspector : OsuTestCase + { + private RulesetStore rulesets; + + protected override void LoadComplete() + { + base.LoadComplete(); + + var room = new Room + { + Name = { Value = @"My Awesome Room" }, + Host = { Value = new User { Username = @"flyte", Id = 3103765, Country = new Country { FlagName = @"JP" } } }, + Status = { Value = new RoomStatusOpen() }, + Type = { Value = new GameTypeTeamVersus() }, + Beatmap = + { + Value = new BeatmapInfo + { + StarDifficulty = 3.7, + Ruleset = rulesets.GetRuleset(3), + Metadata = new BeatmapMetadata + { + Title = @"Platina", + Artist = @"Maaya Sakamoto", + AuthorString = @"uwutm8", + }, + BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh/beatmaps/560573/covers/cover.jpg?1492722343", + }, + }, + }, + } + }, + MaxParticipants = { Value = 200 }, + Participants = + { + Value = new[] + { + new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } }, + new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } }, + new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } }, + new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } }, + new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } }, + new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } }, + } + } + }; + + RoomInspector inspector; + Add(inspector = new RoomInspector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Room = room, + }); + + AddStep(@"change title", () => room.Name.Value = @"A Better Room Than The Above"); + AddStep(@"change host", () => room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } }); + AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying()); + AddStep(@"change type", () => room.Type.Value = new GameTypeTag()); + AddStep(@"change beatmap", () => room.Beatmap.Value = null); + AddStep(@"change max participants", () => room.MaxParticipants.Value = null); + AddStep(@"change participants", () => room.Participants.Value = new[] + { + new User { Username = @"filsdelama", Id = 2831793, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 854 } } }, + new User { Username = @"_index", Id = 652457, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 150 } } } + }); + + AddStep(@"change room", () => + { + var newRoom = new Room + { + Name = { Value = @"My New, Better Than Ever Room" }, + Host = { Value = new User { Username = @"Angelsim", Id = 1777162, Country = new Country { FlagName = @"KR" } } }, + Status = { Value = new RoomStatusOpen() }, + Type = { Value = new GameTypeTagTeam() }, + Beatmap = + { + Value = new BeatmapInfo + { + StarDifficulty = 7.07, + Ruleset = rulesets.GetRuleset(0), + Metadata = new BeatmapMetadata + { + Title = @"FREEDOM DIVE", + Artist = @"xi", + AuthorString = @"Nakagawa-Kanon", + }, + BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh/beatmaps/39804/covers/cover.jpg?1456506845", + }, + }, + }, + }, + }, + MaxParticipants = { Value = 10 }, + Participants = + { + Value = new[] + { + new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 4 } } }, + new User { Username = @"HappyStick", Id = 256802, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 752 } } }, + new User { Username = @"-Konpaku-", Id = 2258797, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 571 } } } + } + } + }; + + inspector.Room = newRoom; + }); + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + this.rulesets = rulesets; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseScoreCounter.cs b/osu.Game.Tests/Visual/TestCaseScoreCounter.cs index e657035355..4dff7547d9 100644 --- a/osu.Game.Tests/Visual/TestCaseScoreCounter.cs +++ b/osu.Game.Tests/Visual/TestCaseScoreCounter.cs @@ -1,106 +1,106 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; -using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Play.HUD; -using OpenTK; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseScoreCounter : OsuTestCase - { - public TestCaseScoreCounter() - { - int numerator = 0, denominator = 0; - - ScoreCounter score = new ScoreCounter(7) - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - TextSize = 40, - Margin = new MarginPadding(20), - }; - Add(score); - - ComboCounter comboCounter = new StandardComboCounter - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Margin = new MarginPadding(10), - TextSize = 40, - }; - Add(comboCounter); - - PercentageCounter accuracyCounter = new PercentageCounter - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Position = new Vector2(-20, 60), - }; - Add(accuracyCounter); - - StarCounter stars = new StarCounter - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Position = new Vector2(20, -160), - CountStars = 5, - }; - Add(stars); - - SpriteText starsLabel = new SpriteText - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Position = new Vector2(20, -190), - Text = stars.CountStars.ToString("0.00"), - }; - Add(starsLabel); - - AddStep(@"Reset all", delegate - { - score.Current.Value = 0; - comboCounter.Current.Value = 0; - numerator = denominator = 0; - accuracyCounter.SetFraction(0, 0); - stars.CountStars = 0; - starsLabel.Text = stars.CountStars.ToString("0.00"); - }); - - AddStep(@"Hit! :D", delegate - { - score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0); - comboCounter.Increment(); - numerator++; - denominator++; - accuracyCounter.SetFraction(numerator, denominator); - }); - - AddStep(@"miss...", delegate - { - comboCounter.Current.Value = 0; - denominator++; - accuracyCounter.SetFraction(numerator, denominator); - }); - - AddStep(@"Alter stars", delegate - { - stars.CountStars = RNG.NextSingle() * (stars.StarCount + 1); - starsLabel.Text = stars.CountStars.ToString("0.00"); - }); - - AddStep(@"Stop counters", delegate - { - score.StopRolling(); - comboCounter.StopRolling(); - accuracyCounter.StopRolling(); - stars.StopAnimation(); - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.MathUtils; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Play.HUD; +using OpenTK; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseScoreCounter : OsuTestCase + { + public TestCaseScoreCounter() + { + int numerator = 0, denominator = 0; + + ScoreCounter score = new ScoreCounter(7) + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + TextSize = 40, + Margin = new MarginPadding(20), + }; + Add(score); + + ComboCounter comboCounter = new StandardComboCounter + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Margin = new MarginPadding(10), + TextSize = 40, + }; + Add(comboCounter); + + PercentageCounter accuracyCounter = new PercentageCounter + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Position = new Vector2(-20, 60), + }; + Add(accuracyCounter); + + StarCounter stars = new StarCounter + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Position = new Vector2(20, -160), + CountStars = 5, + }; + Add(stars); + + SpriteText starsLabel = new SpriteText + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Position = new Vector2(20, -190), + Text = stars.CountStars.ToString("0.00"), + }; + Add(starsLabel); + + AddStep(@"Reset all", delegate + { + score.Current.Value = 0; + comboCounter.Current.Value = 0; + numerator = denominator = 0; + accuracyCounter.SetFraction(0, 0); + stars.CountStars = 0; + starsLabel.Text = stars.CountStars.ToString("0.00"); + }); + + AddStep(@"Hit! :D", delegate + { + score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0); + comboCounter.Increment(); + numerator++; + denominator++; + accuracyCounter.SetFraction(numerator, denominator); + }); + + AddStep(@"miss...", delegate + { + comboCounter.Current.Value = 0; + denominator++; + accuracyCounter.SetFraction(numerator, denominator); + }); + + AddStep(@"Alter stars", delegate + { + stars.CountStars = RNG.NextSingle() * (stars.StarCount + 1); + starsLabel.Text = stars.CountStars.ToString("0.00"); + }); + + AddStep(@"Stop counters", delegate + { + score.StopRolling(); + comboCounter.StopRolling(); + accuracyCounter.StopRolling(); + stars.StopAnimation(); + }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs index 0742dd68eb..8f90b6d87e 100644 --- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs @@ -1,186 +1,186 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Extensions.IEnumerableExtensions; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Timing; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.UI.Scrolling; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseScrollingHitObjects : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(Playfield) }; - - private readonly TestPlayfield[] playfields = new TestPlayfield[4]; - - public TestCaseScrollingHitObjects() - { - Add(new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - playfields[0] = new TestPlayfield(ScrollingDirection.Up), - playfields[1] = new TestPlayfield(ScrollingDirection.Down) - }, - new Drawable[] - { - playfields[2] = new TestPlayfield(ScrollingDirection.Left), - playfields[3] = new TestPlayfield(ScrollingDirection.Right) - } - } - }); - - AddSliderStep("Time range", 100, 10000, 5000, v => playfields.ForEach(p => p.VisibleTimeRange.Value = v)); - AddStep("Add control point", () => addControlPoint(Time.Current + 5000)); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - playfields.ForEach(p => p.HitObjects.AddControlPoint(new MultiplierControlPoint(0))); - - for (int i = 0; i <= 5000; i += 1000) - addHitObject(Time.Current + i); - - Scheduler.AddDelayed(() => addHitObject(Time.Current + 5000), 1000, true); - } - - private void addHitObject(double time) - { - playfields.ForEach(p => - { - var hitObject = new TestDrawableHitObject(time); - setAnchor(hitObject, p); - - p.Add(hitObject); - }); - } - - private void addControlPoint(double time) - { - playfields.ForEach(p => - { - p.HitObjects.AddControlPoint(new MultiplierControlPoint(time) { DifficultyPoint = { SpeedMultiplier = 3 } }); - p.HitObjects.AddControlPoint(new MultiplierControlPoint(time + 2000) { DifficultyPoint = { SpeedMultiplier = 2 } }); - p.HitObjects.AddControlPoint(new MultiplierControlPoint(time + 3000) { DifficultyPoint = { SpeedMultiplier = 1 } }); - - TestDrawableControlPoint createDrawablePoint(double t) - { - var obj = new TestDrawableControlPoint(p.Direction, t); - setAnchor(obj, p); - return obj; - } - - p.Add(createDrawablePoint(time)); - p.Add(createDrawablePoint(time + 2000)); - p.Add(createDrawablePoint(time + 3000)); - }); - } - - private void setAnchor(DrawableHitObject obj, TestPlayfield playfield) - { - switch (playfield.Direction) - { - case ScrollingDirection.Up: - obj.Anchor = Anchor.TopCentre; - break; - case ScrollingDirection.Down: - obj.Anchor = Anchor.BottomCentre; - break; - case ScrollingDirection.Left: - obj.Anchor = Anchor.CentreLeft; - break; - case ScrollingDirection.Right: - obj.Anchor = Anchor.CentreRight; - break; - } - } - - - private class TestPlayfield : ScrollingPlayfield - { - public readonly ScrollingDirection Direction; - - public TestPlayfield(ScrollingDirection direction) - : base(direction) - { - Direction = direction; - - Padding = new MarginPadding(2); - Content.Masking = true; - - AddInternal(new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.5f, - Depth = float.MaxValue - }); - } - } - - private class TestDrawableControlPoint : DrawableHitObject - { - public TestDrawableControlPoint(ScrollingDirection direction, double time) - : base(new HitObject { StartTime = time }) - { - Origin = Anchor.Centre; - - InternalChild = new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both - }; - - switch (direction) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - RelativeSizeAxes = Axes.X; - Height = 2; - break; - case ScrollingDirection.Left: - case ScrollingDirection.Right: - RelativeSizeAxes = Axes.Y; - Width = 2; - break; - } - } - - protected override void UpdateState(ArmedState state) - { - } - } - - private class TestDrawableHitObject : DrawableHitObject - { - public TestDrawableHitObject(double time) - : base(new HitObject { StartTime = time }) - { - Origin = Anchor.Centre; - AutoSizeAxes = Axes.Both; - - InternalChild = new Box { Size = new Vector2(75) }; - } - - protected override void UpdateState(ArmedState state) - { - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Timing; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseScrollingHitObjects : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(Playfield) }; + + private readonly TestPlayfield[] playfields = new TestPlayfield[4]; + + public TestCaseScrollingHitObjects() + { + Add(new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + playfields[0] = new TestPlayfield(ScrollingDirection.Up), + playfields[1] = new TestPlayfield(ScrollingDirection.Down) + }, + new Drawable[] + { + playfields[2] = new TestPlayfield(ScrollingDirection.Left), + playfields[3] = new TestPlayfield(ScrollingDirection.Right) + } + } + }); + + AddSliderStep("Time range", 100, 10000, 5000, v => playfields.ForEach(p => p.VisibleTimeRange.Value = v)); + AddStep("Add control point", () => addControlPoint(Time.Current + 5000)); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + playfields.ForEach(p => p.HitObjects.AddControlPoint(new MultiplierControlPoint(0))); + + for (int i = 0; i <= 5000; i += 1000) + addHitObject(Time.Current + i); + + Scheduler.AddDelayed(() => addHitObject(Time.Current + 5000), 1000, true); + } + + private void addHitObject(double time) + { + playfields.ForEach(p => + { + var hitObject = new TestDrawableHitObject(time); + setAnchor(hitObject, p); + + p.Add(hitObject); + }); + } + + private void addControlPoint(double time) + { + playfields.ForEach(p => + { + p.HitObjects.AddControlPoint(new MultiplierControlPoint(time) { DifficultyPoint = { SpeedMultiplier = 3 } }); + p.HitObjects.AddControlPoint(new MultiplierControlPoint(time + 2000) { DifficultyPoint = { SpeedMultiplier = 2 } }); + p.HitObjects.AddControlPoint(new MultiplierControlPoint(time + 3000) { DifficultyPoint = { SpeedMultiplier = 1 } }); + + TestDrawableControlPoint createDrawablePoint(double t) + { + var obj = new TestDrawableControlPoint(p.Direction, t); + setAnchor(obj, p); + return obj; + } + + p.Add(createDrawablePoint(time)); + p.Add(createDrawablePoint(time + 2000)); + p.Add(createDrawablePoint(time + 3000)); + }); + } + + private void setAnchor(DrawableHitObject obj, TestPlayfield playfield) + { + switch (playfield.Direction) + { + case ScrollingDirection.Up: + obj.Anchor = Anchor.TopCentre; + break; + case ScrollingDirection.Down: + obj.Anchor = Anchor.BottomCentre; + break; + case ScrollingDirection.Left: + obj.Anchor = Anchor.CentreLeft; + break; + case ScrollingDirection.Right: + obj.Anchor = Anchor.CentreRight; + break; + } + } + + + private class TestPlayfield : ScrollingPlayfield + { + public readonly ScrollingDirection Direction; + + public TestPlayfield(ScrollingDirection direction) + : base(direction) + { + Direction = direction; + + Padding = new MarginPadding(2); + Content.Masking = true; + + AddInternal(new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.5f, + Depth = float.MaxValue + }); + } + } + + private class TestDrawableControlPoint : DrawableHitObject + { + public TestDrawableControlPoint(ScrollingDirection direction, double time) + : base(new HitObject { StartTime = time }) + { + Origin = Anchor.Centre; + + InternalChild = new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both + }; + + switch (direction) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + RelativeSizeAxes = Axes.X; + Height = 2; + break; + case ScrollingDirection.Left: + case ScrollingDirection.Right: + RelativeSizeAxes = Axes.Y; + Width = 2; + break; + } + } + + protected override void UpdateState(ArmedState state) + { + } + } + + private class TestDrawableHitObject : DrawableHitObject + { + public TestDrawableHitObject(double time) + : base(new HitObject { StartTime = time }) + { + Origin = Anchor.Centre; + AutoSizeAxes = Axes.Both; + + InternalChild = new Box { Size = new Vector2(75) }; + } + + protected override void UpdateState(ArmedState state) + { + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseSettings.cs b/osu.Game.Tests/Visual/TestCaseSettings.cs index 3f42f2e863..5dad48c6d7 100644 --- a/osu.Game.Tests/Visual/TestCaseSettings.cs +++ b/osu.Game.Tests/Visual/TestCaseSettings.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseSettings : OsuTestCase - { - private readonly SettingsOverlay settings; - private readonly DialogOverlay dialogOverlay; - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); - - public TestCaseSettings() - { - settings = new MainSettings - { - State = Visibility.Visible - }; - Add(dialogOverlay = new DialogOverlay - { - Depth = -1 - }); - } - - [BackgroundDependencyLoader] - private void load() - { - dependencies.Cache(dialogOverlay); - - Add(settings); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseSettings : OsuTestCase + { + private readonly SettingsOverlay settings; + private readonly DialogOverlay dialogOverlay; + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); + + public TestCaseSettings() + { + settings = new MainSettings + { + State = Visibility.Visible + }; + Add(dialogOverlay = new DialogOverlay + { + Depth = -1 + }); + } + + [BackgroundDependencyLoader] + private void load() + { + dependencies.Cache(dialogOverlay); + + Add(settings); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseSkipButton.cs b/osu.Game.Tests/Visual/TestCaseSkipButton.cs index df94d5147f..4f381fd7a8 100644 --- a/osu.Game.Tests/Visual/TestCaseSkipButton.cs +++ b/osu.Game.Tests/Visual/TestCaseSkipButton.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Game.Screens.Play; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseSkipButton : OsuTestCase - { - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(new SkipOverlay(Clock.CurrentTime + 5000)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseSkipButton : OsuTestCase + { + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(new SkipOverlay(Clock.CurrentTime + 5000)); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseSocial.cs b/osu.Game.Tests/Visual/TestCaseSocial.cs index 4003d834d5..03f8fccae6 100644 --- a/osu.Game.Tests/Visual/TestCaseSocial.cs +++ b/osu.Game.Tests/Visual/TestCaseSocial.cs @@ -1,95 +1,95 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Game.Overlays; -using osu.Game.Overlays.Social; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseSocial : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(UserPanel), - typeof(SocialPanel), - typeof(FilterControl), - typeof(SocialOverlay), - typeof(SocialGridPanel), - typeof(SocialListPanel) - }; - - public TestCaseSocial() - { - SocialOverlay s = new SocialOverlay - { - Users = new[] - { - new User - { - Username = @"flyte", - Id = 3103765, - Country = new Country { FlagName = @"JP" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", - }, - new User - { - Username = @"Cookiezi", - Id = 124493, - Country = new Country { FlagName = @"KR" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg", - }, - new User - { - Username = @"Angelsim", - Id = 1777162, - Country = new Country { FlagName = @"KR" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", - }, - new User - { - Username = @"Rafis", - Id = 2558286, - Country = new Country { FlagName = @"PL" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg", - }, - new User - { - Username = @"hvick225", - Id = 50265, - Country = new Country { FlagName = @"TW" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c5.jpg", - }, - new User - { - Username = @"peppy", - Id = 2, - Country = new Country { FlagName = @"AU" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - }, - new User - { - Username = @"filsdelama", - Id = 2831793, - Country = new Country { FlagName = @"FR" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c7.jpg" - }, - new User - { - Username = @"_index", - Id = 652457, - Country = new Country { FlagName = @"RU" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c8.jpg" - }, - }, - }; - Add(s); - - AddStep(@"toggle", s.ToggleVisibility); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Overlays; +using osu.Game.Overlays.Social; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseSocial : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(UserPanel), + typeof(SocialPanel), + typeof(FilterControl), + typeof(SocialOverlay), + typeof(SocialGridPanel), + typeof(SocialListPanel) + }; + + public TestCaseSocial() + { + SocialOverlay s = new SocialOverlay + { + Users = new[] + { + new User + { + Username = @"flyte", + Id = 3103765, + Country = new Country { FlagName = @"JP" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", + }, + new User + { + Username = @"Cookiezi", + Id = 124493, + Country = new Country { FlagName = @"KR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg", + }, + new User + { + Username = @"Angelsim", + Id = 1777162, + Country = new Country { FlagName = @"KR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }, + new User + { + Username = @"Rafis", + Id = 2558286, + Country = new Country { FlagName = @"PL" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg", + }, + new User + { + Username = @"hvick225", + Id = 50265, + Country = new Country { FlagName = @"TW" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c5.jpg", + }, + new User + { + Username = @"peppy", + Id = 2, + Country = new Country { FlagName = @"AU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + }, + new User + { + Username = @"filsdelama", + Id = 2831793, + Country = new Country { FlagName = @"FR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c7.jpg" + }, + new User + { + Username = @"_index", + Id = 652457, + Country = new Country { FlagName = @"RU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c8.jpg" + }, + }, + }; + Add(s); + + AddStep(@"toggle", s.ToggleVisibility); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseSongProgress.cs b/osu.Game.Tests/Visual/TestCaseSongProgress.cs index 857fd6c902..1eb40a9486 100644 --- a/osu.Game.Tests/Visual/TestCaseSongProgress.cs +++ b/osu.Game.Tests/Visual/TestCaseSongProgress.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.MathUtils; -using osu.Framework.Timing; -using osu.Game.Rulesets.Objects; -using osu.Game.Screens.Play; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseSongProgress : OsuTestCase - { - private readonly SongProgress progress; - private readonly SongProgressGraph graph; - - private readonly StopwatchClock clock; - - public TestCaseSongProgress() - { - clock = new StopwatchClock(true); - - Add(progress = new SongProgress - { - RelativeSizeAxes = Axes.X, - AudioClock = new StopwatchClock(true), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - }); - - Add(graph = new SongProgressGraph - { - RelativeSizeAxes = Axes.X, - Height = 200, - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - }); - - AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking); - AddWaitStep(5); - AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking); - AddWaitStep(2); - AddRepeatStep("New Values", displayNewValues, 5); - - displayNewValues(); - } - - private void displayNewValues() - { - List objects = new List(); - for (double i = 0; i < 5000; i += RNG.NextDouble() * 10 + i / 1000) - objects.Add(new HitObject { StartTime = i }); - - progress.Objects = objects; - graph.Objects = objects; - - progress.AudioClock = clock; - progress.OnSeek = pos => clock.Seek(pos); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseSongProgress : OsuTestCase + { + private readonly SongProgress progress; + private readonly SongProgressGraph graph; + + private readonly StopwatchClock clock; + + public TestCaseSongProgress() + { + clock = new StopwatchClock(true); + + Add(progress = new SongProgress + { + RelativeSizeAxes = Axes.X, + AudioClock = new StopwatchClock(true), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }); + + Add(graph = new SongProgressGraph + { + RelativeSizeAxes = Axes.X, + Height = 200, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }); + + AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking); + AddWaitStep(5); + AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking); + AddWaitStep(2); + AddRepeatStep("New Values", displayNewValues, 5); + + displayNewValues(); + } + + private void displayNewValues() + { + List objects = new List(); + for (double i = 0; i < 5000; i += RNG.NextDouble() * 10 + i / 1000) + objects.Add(new HitObject { StartTime = i }); + + progress.Objects = objects; + graph.Objects = objects; + + progress.AudioClock = clock; + progress.OnSeek = pos => clock.Seek(pos); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseStoryboard.cs b/osu.Game.Tests/Visual/TestCaseStoryboard.cs index d34a0e0e5f..e721c5ced0 100644 --- a/osu.Game.Tests/Visual/TestCaseStoryboard.cs +++ b/osu.Game.Tests/Visual/TestCaseStoryboard.cs @@ -1,91 +1,91 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; -using osu.Game.Beatmaps; -using osu.Game.Overlays; -using osu.Game.Storyboards.Drawables; -using OpenTK.Graphics; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseStoryboard : OsuTestCase - { - private readonly Bindable beatmapBacking = new Bindable(); - - private readonly Container storyboardContainer; - private DrawableStoryboard storyboard; - - public TestCaseStoryboard() - { - Clock = new FramedClock(); - - Add(new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - storyboardContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }, - }, - }); - Add(new MusicController - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - State = Visibility.Visible, - }); - - AddStep("Restart", restart); - AddToggleStep("Passing", passing => { if (storyboard != null) storyboard.Passing = passing; }); - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase game) - { - beatmapBacking.BindTo(game.Beatmap); - beatmapBacking.ValueChanged += beatmapChanged; - } - - private void beatmapChanged(WorkingBeatmap working) - => loadStoryboard(working); - - private void restart() - { - var track = beatmapBacking.Value.Track; - - track.Reset(); - loadStoryboard(beatmapBacking.Value); - track.Start(); - } - - private void loadStoryboard(WorkingBeatmap working) - { - if (storyboard != null) - storyboardContainer.Remove(storyboard); - - var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true }; - storyboardContainer.Clock = decoupledClock; - - storyboard = working.Storyboard.CreateDrawable(beatmapBacking); - storyboard.Passing = false; - - storyboardContainer.Add(storyboard); - decoupledClock.ChangeSource(working.Track); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Overlays; +using osu.Game.Storyboards.Drawables; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseStoryboard : OsuTestCase + { + private readonly Bindable beatmapBacking = new Bindable(); + + private readonly Container storyboardContainer; + private DrawableStoryboard storyboard; + + public TestCaseStoryboard() + { + Clock = new FramedClock(); + + Add(new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + storyboardContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + }, + }); + Add(new MusicController + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + State = Visibility.Visible, + }); + + AddStep("Restart", restart); + AddToggleStep("Passing", passing => { if (storyboard != null) storyboard.Passing = passing; }); + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + beatmapBacking.BindTo(game.Beatmap); + beatmapBacking.ValueChanged += beatmapChanged; + } + + private void beatmapChanged(WorkingBeatmap working) + => loadStoryboard(working); + + private void restart() + { + var track = beatmapBacking.Value.Track; + + track.Reset(); + loadStoryboard(beatmapBacking.Value); + track.Start(); + } + + private void loadStoryboard(WorkingBeatmap working) + { + if (storyboard != null) + storyboardContainer.Remove(storyboard); + + var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true }; + storyboardContainer.Clock = decoupledClock; + + storyboard = working.Storyboard.CreateDrawable(beatmapBacking); + storyboard.Passing = false; + + storyboardContainer.Add(storyboard); + decoupledClock.ChangeSource(working.Track); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseTabControl.cs b/osu.Game.Tests/Visual/TestCaseTabControl.cs index 5fc774ff42..63708c5662 100644 --- a/osu.Game.Tests/Visual/TestCaseTabControl.cs +++ b/osu.Game.Tests/Visual/TestCaseTabControl.cs @@ -1,42 +1,42 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Framework.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Select.Filter; -using OpenTK; - -namespace osu.Game.Tests.Visual -{ - [Description("SongSelect filter control")] - public class TestCaseTabControl : OsuTestCase - { - public TestCaseTabControl() - { - OsuSpriteText text; - OsuTabControl filter; - Add(filter = new OsuTabControl - { - Margin = new MarginPadding(4), - Size = new Vector2(229, 24), - AutoSort = true - }); - Add(text = new OsuSpriteText - { - Text = "None", - Margin = new MarginPadding(4), - Position = new Vector2(275, 5) - }); - - filter.PinItem(GroupMode.All); - filter.PinItem(GroupMode.RecentlyPlayed); - - filter.Current.ValueChanged += newFilter => - { - text.Text = "Currently Selected: " + newFilter.ToString(); - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Select.Filter; +using OpenTK; + +namespace osu.Game.Tests.Visual +{ + [Description("SongSelect filter control")] + public class TestCaseTabControl : OsuTestCase + { + public TestCaseTabControl() + { + OsuSpriteText text; + OsuTabControl filter; + Add(filter = new OsuTabControl + { + Margin = new MarginPadding(4), + Size = new Vector2(229, 24), + AutoSort = true + }); + Add(text = new OsuSpriteText + { + Text = "None", + Margin = new MarginPadding(4), + Position = new Vector2(275, 5) + }); + + filter.PinItem(GroupMode.All); + filter.PinItem(GroupMode.RecentlyPlayed); + + filter.Current.ValueChanged += newFilter => + { + text.Text = "Currently Selected: " + newFilter.ToString(); + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseTextAwesome.cs b/osu.Game.Tests/Visual/TestCaseTextAwesome.cs index bf7609ff8d..75b149af29 100644 --- a/osu.Game.Tests/Visual/TestCaseTextAwesome.cs +++ b/osu.Game.Tests/Visual/TestCaseTextAwesome.cs @@ -1,55 +1,55 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Game.Graphics; -using OpenTK; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseTextAwesome : OsuTestCase - { - public TestCaseTextAwesome() - { - FillFlowContainer flow; - - Add(new ScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = flow = new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Full, - }, - }); - - foreach (FontAwesome fa in Enum.GetValues(typeof(FontAwesome))) - flow.Add(new Icon(fa)); - } - - private class Icon : Container, IHasTooltip - { - public string TooltipText { get; } - - public Icon(FontAwesome fa) - { - TooltipText = fa.ToString(); - - AutoSizeAxes = Axes.Both; - Child = new SpriteIcon - { - Icon = fa, - Size = new Vector2(60), - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics; +using OpenTK; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseTextAwesome : OsuTestCase + { + public TestCaseTextAwesome() + { + FillFlowContainer flow; + + Add(new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = flow = new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Full, + }, + }); + + foreach (FontAwesome fa in Enum.GetValues(typeof(FontAwesome))) + flow.Add(new Icon(fa)); + } + + private class Icon : Container, IHasTooltip + { + public string TooltipText { get; } + + public Icon(FontAwesome fa) + { + TooltipText = fa.ToString(); + + AutoSizeAxes = Axes.Both; + Child = new SpriteIcon + { + Icon = fa, + Size = new Vector2(60), + }; + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseToolbar.cs b/osu.Game.Tests/Visual/TestCaseToolbar.cs index 94e45fe0c2..fd218af054 100644 --- a/osu.Game.Tests/Visual/TestCaseToolbar.cs +++ b/osu.Game.Tests/Visual/TestCaseToolbar.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Graphics.Containers; -using osu.Game.Overlays.Toolbar; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseToolbar : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ToolbarButton), - typeof(ToolbarModeSelector), - typeof(ToolbarModeButton), - typeof(ToolbarNotificationButton), - }; - - public TestCaseToolbar() - { - var toolbar = new Toolbar { State = Visibility.Visible }; - - Add(toolbar); - - var notificationButton = toolbar.Children.OfType().Last().Children.OfType().First(); - - void setNotifications(int count) => AddStep($"set notification count to {count}", () => notificationButton.NotificationCount.Value = count); - - setNotifications(1); - setNotifications(2); - setNotifications(3); - setNotifications(0); - setNotifications(144); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Toolbar; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseToolbar : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ToolbarButton), + typeof(ToolbarModeSelector), + typeof(ToolbarModeButton), + typeof(ToolbarNotificationButton), + }; + + public TestCaseToolbar() + { + var toolbar = new Toolbar { State = Visibility.Visible }; + + Add(toolbar); + + var notificationButton = toolbar.Children.OfType().Last().Children.OfType().First(); + + void setNotifications(int count) => AddStep($"set notification count to {count}", () => notificationButton.NotificationCount.Value = count); + + setNotifications(1); + setNotifications(2); + setNotifications(3); + setNotifications(0); + setNotifications(144); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseTwoLayerButton.cs b/osu.Game.Tests/Visual/TestCaseTwoLayerButton.cs index cbf3dd150a..8079566947 100644 --- a/osu.Game.Tests/Visual/TestCaseTwoLayerButton.cs +++ b/osu.Game.Tests/Visual/TestCaseTwoLayerButton.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Tests.Visual -{ - [Description("mostly back button")] - public class TestCaseTwoLayerButton : OsuTestCase - { - public TestCaseTwoLayerButton() - { - Add(new BackButton()); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tests.Visual +{ + [Description("mostly back button")] + public class TestCaseTwoLayerButton : OsuTestCase + { + public TestCaseTwoLayerButton() + { + Add(new BackButton()); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseUserPanel.cs b/osu.Game.Tests/Visual/TestCaseUserPanel.cs index ed377dc160..8e2e708cc7 100644 --- a/osu.Game.Tests/Visual/TestCaseUserPanel.cs +++ b/osu.Game.Tests/Visual/TestCaseUserPanel.cs @@ -1,55 +1,55 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Users; -using OpenTK; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseUserPanel : OsuTestCase - { - public TestCaseUserPanel() - { - UserPanel flyte; - UserPanel peppy; - Add(new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10f), - Children = new[] - { - flyte = new UserPanel(new User - { - Username = @"flyte", - Id = 3103765, - Country = new Country { FlagName = @"JP" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - }) { Width = 300 }, - peppy = new UserPanel(new User - { - Username = @"peppy", - Id = 2, - Country = new Country { FlagName = @"AU" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", - IsSupporter = true, - }) { Width = 300 }, - }, - }); - - flyte.Status.Value = new UserStatusOnline(); - peppy.Status.Value = new UserStatusSoloGame(); - - AddStep(@"spectating", () => { flyte.Status.Value = new UserStatusSpectating(); }); - AddStep(@"multiplaying", () => { flyte.Status.Value = new UserStatusMultiplayerGame(); }); - AddStep(@"modding", () => { flyte.Status.Value = new UserStatusModding(); }); - AddStep(@"offline", () => { flyte.Status.Value = new UserStatusOffline(); }); - AddStep(@"null status", () => { flyte.Status.Value = null; }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Users; +using OpenTK; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseUserPanel : OsuTestCase + { + public TestCaseUserPanel() + { + UserPanel flyte; + UserPanel peppy; + Add(new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10f), + Children = new[] + { + flyte = new UserPanel(new User + { + Username = @"flyte", + Id = 3103765, + Country = new Country { FlagName = @"JP" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + }) { Width = 300 }, + peppy = new UserPanel(new User + { + Username = @"peppy", + Id = 2, + Country = new Country { FlagName = @"AU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + IsSupporter = true, + }) { Width = 300 }, + }, + }); + + flyte.Status.Value = new UserStatusOnline(); + peppy.Status.Value = new UserStatusSoloGame(); + + AddStep(@"spectating", () => { flyte.Status.Value = new UserStatusSpectating(); }); + AddStep(@"multiplaying", () => { flyte.Status.Value = new UserStatusMultiplayerGame(); }); + AddStep(@"modding", () => { flyte.Status.Value = new UserStatusModding(); }); + AddStep(@"offline", () => { flyte.Status.Value = new UserStatusOffline(); }); + AddStep(@"null status", () => { flyte.Status.Value = null; }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index 1fc6c6f224..b060b9f2f8 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -1,103 +1,103 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays; -using osu.Game.Overlays.Profile; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseUserProfile : OsuTestCase - { - private readonly TestUserProfileOverlay profile; - - public override IReadOnlyList RequiredTypes => new[] - { - typeof(ProfileHeader), - typeof(UserProfileOverlay), - typeof(RankGraph), - typeof(LineGraph), - }; - - public TestCaseUserProfile() - { - Add(profile = new TestUserProfileOverlay()); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - AddStep("Show offline dummy", () => profile.ShowUser(new User - { - Username = @"Somebody", - Id = 1, - Country = new Country { FullName = @"Alien" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", - JoinDate = DateTimeOffset.Now.AddDays(-1), - LastVisit = DateTimeOffset.Now, - Age = 1, - ProfileOrder = new[] { "me" }, - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, - PP = 4567.89m, - }, - RankHistory = new User.RankHistoryData - { - Mode = @"osu", - Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() - } - }, false)); - - checkSupporterTag(false); - - AddStep("Show null dummy", () => profile.ShowUser(new User - { - Username = @"Null", - Id = 1, - }, false)); - - AddStep("Show ppy", () => profile.ShowUser(new User - { - Username = @"peppy", - Id = 2, - Country = new Country { FullName = @"Australia", FlagName = @"AU" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" - })); - - checkSupporterTag(true); - - AddStep("Show flyte", () => profile.ShowUser(new User - { - Username = @"flyte", - Id = 3103765, - Country = new Country { FullName = @"Japan", FlagName = @"JP" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - })); - - AddStep("Hide", profile.Hide); - AddStep("Show without reload", profile.Show); - } - - private void checkSupporterTag(bool isSupporter) - { - AddUntilStep(() => profile.Header.User != null, "wait for load"); - if (isSupporter) - AddAssert("is supporter", () => profile.Header.SupporterTag.Alpha == 1); - else - AddAssert("no supporter", () => profile.Header.SupporterTag.Alpha == 0); - } - - private class TestUserProfileOverlay : UserProfileOverlay - { - public new ProfileHeader Header => base.Header; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osu.Game.Overlays.Profile; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseUserProfile : OsuTestCase + { + private readonly TestUserProfileOverlay profile; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ProfileHeader), + typeof(UserProfileOverlay), + typeof(RankGraph), + typeof(LineGraph), + }; + + public TestCaseUserProfile() + { + Add(profile = new TestUserProfileOverlay()); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep("Show offline dummy", () => profile.ShowUser(new User + { + Username = @"Somebody", + Id = 1, + Country = new Country { FullName = @"Alien" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", + JoinDate = DateTimeOffset.Now.AddDays(-1), + LastVisit = DateTimeOffset.Now, + Age = 1, + ProfileOrder = new[] { "me" }, + Statistics = new UserStatistics + { + Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, + PP = 4567.89m, + }, + RankHistory = new User.RankHistoryData + { + Mode = @"osu", + Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() + } + }, false)); + + checkSupporterTag(false); + + AddStep("Show null dummy", () => profile.ShowUser(new User + { + Username = @"Null", + Id = 1, + }, false)); + + AddStep("Show ppy", () => profile.ShowUser(new User + { + Username = @"peppy", + Id = 2, + Country = new Country { FullName = @"Australia", FlagName = @"AU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" + })); + + checkSupporterTag(true); + + AddStep("Show flyte", () => profile.ShowUser(new User + { + Username = @"flyte", + Id = 3103765, + Country = new Country { FullName = @"Japan", FlagName = @"JP" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + })); + + AddStep("Hide", profile.Hide); + AddStep("Show without reload", profile.Show); + } + + private void checkSupporterTag(bool isSupporter) + { + AddUntilStep(() => profile.Header.User != null, "wait for load"); + if (isSupporter) + AddAssert("is supporter", () => profile.Header.SupporterTag.Alpha == 1); + else + AddAssert("no supporter", () => profile.Header.SupporterTag.Alpha == 0); + } + + private class TestUserProfileOverlay : UserProfileOverlay + { + public new ProfileHeader Header => base.Header; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseUserProfileRecentSection.cs b/osu.Game.Tests/Visual/TestCaseUserProfileRecentSection.cs index 1f7a7e7165..f625bc0150 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfileRecentSection.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfileRecentSection.cs @@ -1,161 +1,161 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Profile.Sections; -using osu.Game.Overlays.Profile.Sections.Recent; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseUserProfileRecentSection : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(RecentSection), - typeof(DrawableRecentActivity), - typeof(PaginatedRecentActivityContainer), - typeof(MedalIcon) - }; - - public TestCaseUserProfileRecentSection() - { - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f) - }, - new ScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - ChildrenEnumerable = createDummyActivities().Select(a => new DrawableRecentActivity(a)) - }, - } - }; - } - - private IEnumerable createDummyActivities() - { - var dummyBeatmap = new RecentActivity.RecentActivityBeatmap - { - Title = @"Dummy beatmap", - Url = "/b/1337", - }; - - var dummyUser = new RecentActivity.RecentActivityUser - { - Username = "DummyReborn", - Url = "/u/666", - PreviousUsername = "Dummy", - }; - - return new[] - { - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.Achievement, - Achievement = new RecentActivity.RecentActivityAchievement - { - Name = @"Feelin' It", - Slug = @"all-secret-feelinit", - }, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.BeatmapPlaycount, - Count = 1337, - Beatmap = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.BeatmapsetApprove, - Approval = BeatmapApproval.Qualified, - Beatmapset = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.BeatmapsetDelete, - Beatmapset = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.BeatmapsetRevive, - Beatmapset = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.BeatmapsetRevive, - Beatmapset = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.BeatmapsetUpdate, - Beatmapset = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.BeatmapsetUpload, - Beatmapset = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.Rank, - Rank = 1, - Mode = "osu!", - Beatmap = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.RankLost, - Mode = "osu!", - Beatmap = dummyBeatmap, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.UsernameChange, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.UserSupportAgain, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.UserSupportFirst, - }, - new RecentActivity - { - User = dummyUser, - Type = RecentActivityType.UserSupportGift, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections; +using osu.Game.Overlays.Profile.Sections.Recent; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseUserProfileRecentSection : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(RecentSection), + typeof(DrawableRecentActivity), + typeof(PaginatedRecentActivityContainer), + typeof(MedalIcon) + }; + + public TestCaseUserProfileRecentSection() + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }, + new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + ChildrenEnumerable = createDummyActivities().Select(a => new DrawableRecentActivity(a)) + }, + } + }; + } + + private IEnumerable createDummyActivities() + { + var dummyBeatmap = new RecentActivity.RecentActivityBeatmap + { + Title = @"Dummy beatmap", + Url = "/b/1337", + }; + + var dummyUser = new RecentActivity.RecentActivityUser + { + Username = "DummyReborn", + Url = "/u/666", + PreviousUsername = "Dummy", + }; + + return new[] + { + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.Achievement, + Achievement = new RecentActivity.RecentActivityAchievement + { + Name = @"Feelin' It", + Slug = @"all-secret-feelinit", + }, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapPlaycount, + Count = 1337, + Beatmap = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetApprove, + Approval = BeatmapApproval.Qualified, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetDelete, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetRevive, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetRevive, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetUpdate, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.BeatmapsetUpload, + Beatmapset = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.Rank, + Rank = 1, + Mode = "osu!", + Beatmap = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.RankLost, + Mode = "osu!", + Beatmap = dummyBeatmap, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.UsernameChange, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.UserSupportAgain, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.UserSupportFirst, + }, + new RecentActivity + { + User = dummyUser, + Type = RecentActivityType.UserSupportGift, + }, + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseUserRanks.cs b/osu.Game.Tests/Visual/TestCaseUserRanks.cs index effc98c381..8329ad705a 100644 --- a/osu.Game.Tests/Visual/TestCaseUserRanks.cs +++ b/osu.Game.Tests/Visual/TestCaseUserRanks.cs @@ -1,47 +1,47 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Overlays.Profile.Sections; -using osu.Game.Overlays.Profile.Sections.Ranks; -using osu.Game.Users; -using System; -using System.Collections.Generic; -using NUnit.Framework; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseUserRanks : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) }; - - public TestCaseUserRanks() - { - RanksSection ranks; - - Add(new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f) - }, - new ScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = ranks = new RanksSection(), - }, - } - }); - - AddStep("Show cookiezi", () => ranks.User.Value = new User { Id = 124493 }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Overlays.Profile.Sections; +using osu.Game.Overlays.Profile.Sections.Ranks; +using osu.Game.Users; +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseUserRanks : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) }; + + public TestCaseUserRanks() + { + RanksSection ranks; + + Add(new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }, + new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = ranks = new RanksSection(), + }, + } + }); + + AddStep("Show cookiezi", () => ranks.User.Value = new User { Id = 124493 }); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseVolumePieces.cs b/osu.Game.Tests/Visual/TestCaseVolumePieces.cs index cfbf7fdb4d..449f48b7d7 100644 --- a/osu.Game.Tests/Visual/TestCaseVolumePieces.cs +++ b/osu.Game.Tests/Visual/TestCaseVolumePieces.cs @@ -1,30 +1,30 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Game.Overlays.Volume; -using OpenTK.Graphics; - -namespace osu.Game.Tests.Visual -{ - public class TestCaseVolumePieces : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(VolumeMeter), typeof(MuteButton) }; - - protected override void LoadComplete() - { - VolumeMeter meter; - MuteButton mute; - Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue)); - Add(mute = new MuteButton - { - Margin = new MarginPadding { Top = 200 } - }); - - AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1); - AddToggleStep("mute", b => mute.Current.Value = b); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Overlays.Volume; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseVolumePieces : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(VolumeMeter), typeof(MuteButton) }; + + protected override void LoadComplete() + { + VolumeMeter meter; + MuteButton mute; + Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue)); + Add(mute = new MuteButton + { + Margin = new MarginPadding { Top = 200 } + }); + + AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1); + AddToggleStep("mute", b => mute.Current.Value = b); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseWaveform.cs b/osu.Game.Tests/Visual/TestCaseWaveform.cs index 7d4a9d663b..776adab0d1 100644 --- a/osu.Game.Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game.Tests/Visual/TestCaseWaveform.cs @@ -1,90 +1,90 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; -using osu.Game.Screens.Edit.Screens.Compose.Timeline; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseWaveform : OsuTestCase - { - private readonly Bindable beatmapBacking = new Bindable(); - - public TestCaseWaveform() - { - FillFlowContainer flow; - Child = flow = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - Children = new Drawable[] - { - new MusicController - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Y = 100, - State = Visibility.Visible - }, - } - }; - - for (int i = 1; i <= 16; i *= 2) - { - var newDisplay = new BeatmapWaveformGraph - { - RelativeSizeAxes = Axes.Both, - Resolution = 1f / i - }; - - newDisplay.Beatmap.BindTo(beatmapBacking); - - flow.Add(new Container - { - RelativeSizeAxes = Axes.X, - Height = 100, - Children = new Drawable[] - { - newDisplay, - new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.75f - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = $"Resolution: {1f / i:0.00}" - } - } - } - } - }); - } - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) => beatmapBacking.BindTo(osuGame.Beatmap); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Screens.Edit.Screens.Compose.Timeline; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseWaveform : OsuTestCase + { + private readonly Bindable beatmapBacking = new Bindable(); + + public TestCaseWaveform() + { + FillFlowContainer flow; + Child = flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new MusicController + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Y = 100, + State = Visibility.Visible + }, + } + }; + + for (int i = 1; i <= 16; i *= 2) + { + var newDisplay = new BeatmapWaveformGraph + { + RelativeSizeAxes = Axes.Both, + Resolution = 1f / i + }; + + newDisplay.Beatmap.BindTo(beatmapBacking); + + flow.Add(new Container + { + RelativeSizeAxes = Axes.X, + Height = 100, + Children = new Drawable[] + { + newDisplay, + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.75f + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = $"Resolution: {1f / i:0.00}" + } + } + } + } + }); + } + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) => beatmapBacking.BindTo(osuGame.Beatmap); + } +} diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 2014db6c61..f635b74030 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -1,36 +1,36 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Audio -{ - [Serializable] - public class SampleInfo - { - public const string HIT_WHISTLE = @"hitwhistle"; - public const string HIT_FINISH = @"hitfinish"; - public const string HIT_NORMAL = @"hitnormal"; - public const string HIT_CLAP = @"hitclap"; - - /// - /// An optional ruleset namespace. - /// - public string Namespace; - - /// - /// The bank to load the sample from. - /// - public string Bank; - - /// - /// The name of the sample to load. - /// - public string Name; - - /// - /// The sample volume. - /// - public int Volume; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Audio +{ + [Serializable] + public class SampleInfo + { + public const string HIT_WHISTLE = @"hitwhistle"; + public const string HIT_FINISH = @"hitfinish"; + public const string HIT_NORMAL = @"hitnormal"; + public const string HIT_CLAP = @"hitclap"; + + /// + /// An optional ruleset namespace. + /// + public string Namespace; + + /// + /// The bank to load the sample from. + /// + public string Bank; + + /// + /// The name of the sample to load. + /// + public string Name; + + /// + /// The sample volume. + /// + public int Volume; + } +} diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 60cf93fd91..12a017f68c 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -1,88 +1,88 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Objects; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.IO.Serialization; -using Newtonsoft.Json; -using osu.Game.IO.Serialization.Converters; - -namespace osu.Game.Beatmaps -{ - /// - /// A Beatmap containing converted HitObjects. - /// - public class Beatmap : IJsonSerializable - where T : HitObject - { - public BeatmapInfo BeatmapInfo = new BeatmapInfo(); - public ControlPointInfo ControlPointInfo = new ControlPointInfo(); - public List Breaks = new List(); - - [JsonIgnore] - public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; - - /// - /// The HitObjects this Beatmap contains. - /// - [JsonConverter(typeof(TypedListConverter))] - public List HitObjects = new List(); - - /// - /// Total amount of break time in the beatmap. - /// - [JsonIgnore] - public double TotalBreakTime => Breaks.Sum(b => b.Duration); - - /// - /// Constructs a new beatmap. - /// - /// The original beatmap to use the parameters of. - public Beatmap(Beatmap original = null) - { - BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; - ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; - Breaks = original?.Breaks ?? Breaks; - HitObjects = original?.HitObjects ?? HitObjects; - - if (original == null && Metadata == null) - { - // we may have no metadata in cases we weren't sourced from the database. - // let's fill it (and other related fields) so we don't need to null-check it in future usages. - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Artist = @"Unknown", - Title = @"Unknown", - AuthorString = @"Unknown Creator", - }, - Version = @"Normal", - BaseDifficulty = new BeatmapDifficulty() - }; - } - } - } - - /// - /// A Beatmap containing un-converted HitObjects. - /// - public class Beatmap : Beatmap - { - /// - /// Constructs a new beatmap. - /// - /// The original beatmap to use the parameters of. - public Beatmap(Beatmap original) - : base(original) - { - } - - public Beatmap() - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO.Serialization; +using Newtonsoft.Json; +using osu.Game.IO.Serialization.Converters; + +namespace osu.Game.Beatmaps +{ + /// + /// A Beatmap containing converted HitObjects. + /// + public class Beatmap : IJsonSerializable + where T : HitObject + { + public BeatmapInfo BeatmapInfo = new BeatmapInfo(); + public ControlPointInfo ControlPointInfo = new ControlPointInfo(); + public List Breaks = new List(); + + [JsonIgnore] + public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; + + /// + /// The HitObjects this Beatmap contains. + /// + [JsonConverter(typeof(TypedListConverter))] + public List HitObjects = new List(); + + /// + /// Total amount of break time in the beatmap. + /// + [JsonIgnore] + public double TotalBreakTime => Breaks.Sum(b => b.Duration); + + /// + /// Constructs a new beatmap. + /// + /// The original beatmap to use the parameters of. + public Beatmap(Beatmap original = null) + { + BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; + ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; + Breaks = original?.Breaks ?? Breaks; + HitObjects = original?.HitObjects ?? HitObjects; + + if (original == null && Metadata == null) + { + // we may have no metadata in cases we weren't sourced from the database. + // let's fill it (and other related fields) so we don't need to null-check it in future usages. + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = @"Unknown", + Title = @"Unknown", + AuthorString = @"Unknown Creator", + }, + Version = @"Normal", + BaseDifficulty = new BeatmapDifficulty() + }; + } + } + } + + /// + /// A Beatmap containing un-converted HitObjects. + /// + public class Beatmap : Beatmap + { + /// + /// Constructs a new beatmap. + /// + /// The original beatmap to use the parameters of. + public Beatmap(Beatmap original) + : base(original) + { + } + + public Beatmap() + { + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index 2003b845d9..153cace187 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -1,112 +1,112 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Beatmaps -{ - /// - /// Converts a Beatmap for another mode. - /// - /// The type of HitObject stored in the Beatmap. - public abstract class BeatmapConverter : IBeatmapConverter - where T : HitObject - { - private event Action> ObjectConverted; - event Action> IBeatmapConverter.ObjectConverted - { - add => ObjectConverted += value; - remove => ObjectConverted -= value; - } - - /// - /// Checks if a Beatmap can be converted using this Beatmap Converter. - /// - /// The Beatmap to check. - /// Whether the Beatmap can be converted using this Beatmap Converter. - public bool CanConvert(Beatmap beatmap) => ValidConversionTypes.All(t => beatmap.HitObjects.Any(t.IsInstanceOfType)); - - /// - /// Converts a Beatmap using this Beatmap Converter. - /// - /// The un-converted Beatmap. - /// The converted Beatmap. - public Beatmap Convert(Beatmap original) - { - // We always operate on a clone of the original beatmap, to not modify it game-wide - return ConvertBeatmap(new Beatmap(original)); - } - - void IBeatmapConverter.Convert(Beatmap original) => Convert(original); - - /// - /// Performs the conversion of a Beatmap using this Beatmap Converter. - /// - /// The un-converted Beatmap. - /// The converted Beatmap. - protected virtual Beatmap ConvertBeatmap(Beatmap original) - { - var beatmap = CreateBeatmap(); - - // todo: this *must* share logic (or directly use) Beatmap's constructor. - // right now this isn't easily possible due to generic entanglement. - beatmap.BeatmapInfo = original.BeatmapInfo; - beatmap.ControlPointInfo = original.ControlPointInfo; - beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList(); - beatmap.Breaks = original.Breaks; - - return beatmap; - } - - /// - /// Converts a hit object. - /// - /// The hit object to convert. - /// The un-converted Beatmap. - /// The converted hit object. - private IEnumerable convert(HitObject original, Beatmap beatmap) - { - // Check if the hitobject is already the converted type - T tObject = original as T; - if (tObject != null) - { - yield return tObject; - yield break; - } - - var converted = ConvertHitObject(original, beatmap).ToList(); - ObjectConverted?.Invoke(original, converted); - - // Convert the hit object - foreach (var obj in converted) - { - if (obj == null) - continue; - - yield return obj; - } - } - - /// - /// The types of HitObjects that can be converted to be used for this Beatmap. - /// - protected abstract IEnumerable ValidConversionTypes { get; } - - /// - /// Creates the that will be returned by this . - /// - protected virtual Beatmap CreateBeatmap() => new Beatmap(); - - /// - /// Performs the conversion of a hit object. - /// This method is generally executed sequentially for all objects in a beatmap. - /// - /// The hit object to convert. - /// The un-converted Beatmap. - /// The converted hit object. - protected abstract IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Beatmaps +{ + /// + /// Converts a Beatmap for another mode. + /// + /// The type of HitObject stored in the Beatmap. + public abstract class BeatmapConverter : IBeatmapConverter + where T : HitObject + { + private event Action> ObjectConverted; + event Action> IBeatmapConverter.ObjectConverted + { + add => ObjectConverted += value; + remove => ObjectConverted -= value; + } + + /// + /// Checks if a Beatmap can be converted using this Beatmap Converter. + /// + /// The Beatmap to check. + /// Whether the Beatmap can be converted using this Beatmap Converter. + public bool CanConvert(Beatmap beatmap) => ValidConversionTypes.All(t => beatmap.HitObjects.Any(t.IsInstanceOfType)); + + /// + /// Converts a Beatmap using this Beatmap Converter. + /// + /// The un-converted Beatmap. + /// The converted Beatmap. + public Beatmap Convert(Beatmap original) + { + // We always operate on a clone of the original beatmap, to not modify it game-wide + return ConvertBeatmap(new Beatmap(original)); + } + + void IBeatmapConverter.Convert(Beatmap original) => Convert(original); + + /// + /// Performs the conversion of a Beatmap using this Beatmap Converter. + /// + /// The un-converted Beatmap. + /// The converted Beatmap. + protected virtual Beatmap ConvertBeatmap(Beatmap original) + { + var beatmap = CreateBeatmap(); + + // todo: this *must* share logic (or directly use) Beatmap's constructor. + // right now this isn't easily possible due to generic entanglement. + beatmap.BeatmapInfo = original.BeatmapInfo; + beatmap.ControlPointInfo = original.ControlPointInfo; + beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList(); + beatmap.Breaks = original.Breaks; + + return beatmap; + } + + /// + /// Converts a hit object. + /// + /// The hit object to convert. + /// The un-converted Beatmap. + /// The converted hit object. + private IEnumerable convert(HitObject original, Beatmap beatmap) + { + // Check if the hitobject is already the converted type + T tObject = original as T; + if (tObject != null) + { + yield return tObject; + yield break; + } + + var converted = ConvertHitObject(original, beatmap).ToList(); + ObjectConverted?.Invoke(original, converted); + + // Convert the hit object + foreach (var obj in converted) + { + if (obj == null) + continue; + + yield return obj; + } + } + + /// + /// The types of HitObjects that can be converted to be used for this Beatmap. + /// + protected abstract IEnumerable ValidConversionTypes { get; } + + /// + /// Creates the that will be returned by this . + /// + protected virtual Beatmap CreateBeatmap() => new Beatmap(); + + /// + /// Performs the conversion of a hit object. + /// This method is generally executed sequentially for all objects in a beatmap. + /// + /// The hit object to convert. + /// The un-converted Beatmap. + /// The converted hit object. + protected abstract IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap); + } +} diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 38b84b4b03..855e8fe881 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel.DataAnnotations.Schema; -using Newtonsoft.Json; - -namespace osu.Game.Beatmaps -{ - public class BeatmapDifficulty - { - /// - /// The default value used for all difficulty settings except and . - /// - public const float DEFAULT_DIFFICULTY = 5; - - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - [JsonIgnore] - public int ID { get; set; } - - public float DrainRate { get; set; } = DEFAULT_DIFFICULTY; - public float CircleSize { get; set; } = DEFAULT_DIFFICULTY; - public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY; - - private float? approachRate; - - public float ApproachRate - { - get => approachRate ?? OverallDifficulty; - set => approachRate = value; - } - - public double SliderMultiplier { get; set; } = 1; - public double SliderTickRate { get; set; } = 1; - - /// - /// Maps a difficulty value [0, 10] to a two-piece linear range of values. - /// - /// The difficulty value to be mapped. - /// Minimum of the resulting range which will be achieved by a difficulty value of 0. - /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. - /// Maximum of the resulting range which will be achieved by a difficulty value of 10. - /// Value to which the difficulty value maps in the specified range. - public static double DifficultyRange(double difficulty, double min, double mid, double max) - { - if (difficulty > 5) - return mid + (max - mid) * (difficulty - 5) / 5; - if (difficulty < 5) - return mid - (mid - min) * (5 - difficulty) / 5; - return mid; - } - - /// - /// Maps a difficulty value [0, 10] to a two-piece linear range of values. - /// - /// The difficulty value to be mapped. - /// The values that define the two linear ranges. - /// Minimum of the resulting range which will be achieved by a difficulty value of 0. - /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. - /// Maximum of the resulting range which will be achieved by a difficulty value of 10. - /// Value to which the difficulty value maps in the specified range. - public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) - => DifficultyRange(difficulty, range.od0, range.od5, range.od10); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; + +namespace osu.Game.Beatmaps +{ + public class BeatmapDifficulty + { + /// + /// The default value used for all difficulty settings except and . + /// + public const float DEFAULT_DIFFICULTY = 5; + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [JsonIgnore] + public int ID { get; set; } + + public float DrainRate { get; set; } = DEFAULT_DIFFICULTY; + public float CircleSize { get; set; } = DEFAULT_DIFFICULTY; + public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY; + + private float? approachRate; + + public float ApproachRate + { + get => approachRate ?? OverallDifficulty; + set => approachRate = value; + } + + public double SliderMultiplier { get; set; } = 1; + public double SliderTickRate { get; set; } = 1; + + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// + /// The difficulty value to be mapped. + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// Value to which the difficulty value maps in the specified range. + public static double DifficultyRange(double difficulty, double min, double mid, double max) + { + if (difficulty > 5) + return mid + (max - mid) * (difficulty - 5) / 5; + if (difficulty < 5) + return mid - (mid - min) * (5 - difficulty) / 5; + return mid; + } + + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// + /// The difficulty value to be mapped. + /// The values that define the two linear ranges. + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// Value to which the difficulty value maps in the specified range. + public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) + => DifficultyRange(difficulty, range.od0, range.od5, range.od10); + } +} diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index b151570806..a1b97afc6c 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -1,147 +1,147 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using Newtonsoft.Json; -using osu.Game.Database; -using osu.Game.IO.Serialization; -using osu.Game.Rulesets; - -namespace osu.Game.Beatmaps -{ - [Serializable] - public class BeatmapInfo : IEquatable, IJsonSerializable, IHasPrimaryKey - { - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - [JsonIgnore] - public int ID { get; set; } - - //TODO: should be in database - public int BeatmapVersion; - - private int? onlineBeatmapID; - private int? onlineBeatmapSetID; - - [JsonProperty("id")] - public int? OnlineBeatmapID - { - get { return onlineBeatmapID; } - set { onlineBeatmapID = value > 0 ? value : null; } - } - - [JsonProperty("beatmapset_id")] - [NotMapped] - public int? OnlineBeatmapSetID - { - get { return onlineBeatmapSetID; } - set { onlineBeatmapSetID = value > 0 ? value : null; } - } - - [JsonIgnore] - public int BeatmapSetInfoID { get; set; } - - [Required] - [JsonIgnore] - public BeatmapSetInfo BeatmapSet { get; set; } - - public BeatmapMetadata Metadata { get; set; } - - [JsonIgnore] - public int BaseDifficultyID { get; set; } - - public BeatmapDifficulty BaseDifficulty { get; set; } - - [NotMapped] - public BeatmapMetrics Metrics { get; set; } - - [NotMapped] - public BeatmapOnlineInfo OnlineInfo { get; set; } - - public string Path { get; set; } - - [JsonProperty("file_sha2")] - public string Hash { get; set; } - - [JsonIgnore] - public bool Hidden { get; set; } - - /// - /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). - /// - [JsonProperty("file_md5")] - public string MD5Hash { get; set; } - - // General - public int AudioLeadIn { get; set; } - public bool Countdown { get; set; } - public float StackLeniency { get; set; } - public bool SpecialStyle { get; set; } - - public int RulesetID { get; set; } - - public RulesetInfo Ruleset { get; set; } - - public bool LetterboxInBreaks { get; set; } - public bool WidescreenStoryboard { get; set; } - - // Editor - // This bookmarks stuff is necessary because DB doesn't know how to store int[] - [JsonIgnore] - public string StoredBookmarks - { - get { return string.Join(",", Bookmarks); } - set - { - if (string.IsNullOrEmpty(value)) - { - Bookmarks = new int[0]; - return; - } - - Bookmarks = value.Split(',').Select(v => - { - int val; - bool result = int.TryParse(v, out val); - return new { result, val }; - }).Where(p => p.result).Select(p => p.val).ToArray(); - } - } - - [NotMapped] - public int[] Bookmarks { get; set; } = new int[0]; - - public double DistanceSpacing { get; set; } - public int BeatDivisor { get; set; } - public int GridSize { get; set; } - public double TimelineZoom { get; set; } - - // Metadata - public string Version { get; set; } - - [JsonProperty("difficulty_rating")] - public double StarDifficulty { get; set; } - - public override string ToString() => $"{Metadata} [{Version}]"; - - public bool Equals(BeatmapInfo other) - { - if (ID == 0 || other?.ID == 0) - // one of the two BeatmapInfos we are comparing isn't sourced from a database. - // fall back to reference equality. - return ReferenceEquals(this, other); - - return ID == other?.ID; - } - - public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && - BeatmapSet.Hash == other.BeatmapSet.Hash && - (Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile; - - public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && - BeatmapSet.Hash == other.BeatmapSet.Hash && - (Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using Newtonsoft.Json; +using osu.Game.Database; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets; + +namespace osu.Game.Beatmaps +{ + [Serializable] + public class BeatmapInfo : IEquatable, IJsonSerializable, IHasPrimaryKey + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [JsonIgnore] + public int ID { get; set; } + + //TODO: should be in database + public int BeatmapVersion; + + private int? onlineBeatmapID; + private int? onlineBeatmapSetID; + + [JsonProperty("id")] + public int? OnlineBeatmapID + { + get { return onlineBeatmapID; } + set { onlineBeatmapID = value > 0 ? value : null; } + } + + [JsonProperty("beatmapset_id")] + [NotMapped] + public int? OnlineBeatmapSetID + { + get { return onlineBeatmapSetID; } + set { onlineBeatmapSetID = value > 0 ? value : null; } + } + + [JsonIgnore] + public int BeatmapSetInfoID { get; set; } + + [Required] + [JsonIgnore] + public BeatmapSetInfo BeatmapSet { get; set; } + + public BeatmapMetadata Metadata { get; set; } + + [JsonIgnore] + public int BaseDifficultyID { get; set; } + + public BeatmapDifficulty BaseDifficulty { get; set; } + + [NotMapped] + public BeatmapMetrics Metrics { get; set; } + + [NotMapped] + public BeatmapOnlineInfo OnlineInfo { get; set; } + + public string Path { get; set; } + + [JsonProperty("file_sha2")] + public string Hash { get; set; } + + [JsonIgnore] + public bool Hidden { get; set; } + + /// + /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). + /// + [JsonProperty("file_md5")] + public string MD5Hash { get; set; } + + // General + public int AudioLeadIn { get; set; } + public bool Countdown { get; set; } + public float StackLeniency { get; set; } + public bool SpecialStyle { get; set; } + + public int RulesetID { get; set; } + + public RulesetInfo Ruleset { get; set; } + + public bool LetterboxInBreaks { get; set; } + public bool WidescreenStoryboard { get; set; } + + // Editor + // This bookmarks stuff is necessary because DB doesn't know how to store int[] + [JsonIgnore] + public string StoredBookmarks + { + get { return string.Join(",", Bookmarks); } + set + { + if (string.IsNullOrEmpty(value)) + { + Bookmarks = new int[0]; + return; + } + + Bookmarks = value.Split(',').Select(v => + { + int val; + bool result = int.TryParse(v, out val); + return new { result, val }; + }).Where(p => p.result).Select(p => p.val).ToArray(); + } + } + + [NotMapped] + public int[] Bookmarks { get; set; } = new int[0]; + + public double DistanceSpacing { get; set; } + public int BeatDivisor { get; set; } + public int GridSize { get; set; } + public double TimelineZoom { get; set; } + + // Metadata + public string Version { get; set; } + + [JsonProperty("difficulty_rating")] + public double StarDifficulty { get; set; } + + public override string ToString() => $"{Metadata} [{Version}]"; + + public bool Equals(BeatmapInfo other) + { + if (ID == 0 || other?.ID == 0) + // one of the two BeatmapInfos we are comparing isn't sourced from a database. + // fall back to reference equality. + return ReferenceEquals(this, other); + + return ID == other?.ID; + } + + public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && + BeatmapSet.Hash == other.BeatmapSet.Hash && + (Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile; + + public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && + BeatmapSet.Hash == other.BeatmapSet.Hash && + (Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile; + } +} diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 1113e38d7a..645e52a6c6 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -1,355 +1,355 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using osu.Framework.Audio; -using osu.Framework.Extensions; -using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Game.Beatmaps.Formats; -using osu.Game.Database; -using osu.Game.Graphics; -using osu.Game.IO.Archives; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Notifications; -using osu.Game.Rulesets; - -namespace osu.Game.Beatmaps -{ - /// - /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. - /// - public partial class BeatmapManager : ArchiveModelManager - { - /// - /// Fired when a single difficulty has been hidden. - /// - public event Action BeatmapHidden; - - /// - /// Fired when a single difficulty has been restored. - /// - public event Action BeatmapRestored; - - /// - /// Fired when a beatmap download begins. - /// - public event Action BeatmapDownloadBegan; - - /// - /// A default representation of a WorkingBeatmap to use when no beatmap is available. - /// - public WorkingBeatmap DefaultBeatmap { private get; set; } - - public override string[] HandledExtensions => new[] { ".osz" }; - - private readonly RulesetStore rulesets; - - private readonly BeatmapStore beatmaps; - - private readonly APIAccess api; - - private readonly AudioManager audioManager; - - private readonly List currentDownloads = new List(); - - /// - /// Set a storage with access to an osu-stable install for import purposes. - /// - public Func GetStableStorage { private get; set; } - - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null) - : base(storage, contextFactory, new BeatmapStore(contextFactory), importHost) - { - beatmaps = (BeatmapStore)ModelStore; - beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); - beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); - - this.rulesets = rulesets; - this.api = api; - this.audioManager = audioManager; - } - - protected override void Populate(BeatmapSetInfo model, ArchiveReader archive) - { - model.Beatmaps = createBeatmapDifficulties(archive); - - // remove metadata from difficulties where it matches the set - foreach (BeatmapInfo b in model.Beatmaps) - if (model.Metadata.Equals(b.Metadata)) - b.Metadata = null; - } - - protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model) - { - // check if this beatmap has already been imported and exit early if so - var existingHashMatch = beatmaps.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); - if (existingHashMatch != null) - { - Undelete(existingHashMatch); - return existingHashMatch; - } - - // check if a set already exists with the same online id - if (model.OnlineBeatmapSetID != null) - { - var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID); - if (existingOnlineId != null) - { - Delete(existingOnlineId); - beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID); - } - } - - return null; - } - - /// - /// Downloads a beatmap. - /// This will post notifications tracking progress. - /// - /// The to be downloaded. - /// Whether the beatmap should be downloaded without video. Defaults to false. - public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false) - { - var existing = GetExistingDownload(beatmapSetInfo); - - if (existing != null || api == null) return; - - if (!api.LocalUser.Value.IsSupporter) - { - PostNotification?.Invoke(new SimpleNotification - { - Icon = FontAwesome.fa_superpowers, - Text = "You gotta be a supporter to download for now 'yo" - }); - return; - } - - var downloadNotification = new ProgressNotification - { - CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!", - Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}", - }; - - var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo); - - request.DownloadProgressed += progress => - { - downloadNotification.State = ProgressNotificationState.Active; - downloadNotification.Progress = progress; - }; - - request.Success += data => - { - downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}"; - - Task.Factory.StartNew(() => - { - // This gets scheduled back to the update thread, but we want the import to run in the background. - using (var stream = new MemoryStream(data)) - using (var archive = new ZipArchiveReader(stream, beatmapSetInfo.ToString())) - Import(archive); - - downloadNotification.State = ProgressNotificationState.Completed; - currentDownloads.Remove(request); - }, TaskCreationOptions.LongRunning); - }; - - request.Failure += error => - { - if (error is OperationCanceledException) return; - - downloadNotification.State = ProgressNotificationState.Completed; - Logger.Error(error, "Beatmap download failed!"); - currentDownloads.Remove(request); - }; - - downloadNotification.CancelRequested += () => - { - request.Cancel(); - currentDownloads.Remove(request); - downloadNotification.State = ProgressNotificationState.Cancelled; - return true; - }; - - currentDownloads.Add(request); - PostNotification?.Invoke(downloadNotification); - - // don't run in the main api queue as this is a long-running task. - Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning); - BeatmapDownloadBegan?.Invoke(request); - } - - /// - /// Get an existing download request if it exists. - /// - /// The whose download request is wanted. - /// The object if it exists, or null. - public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID); - - /// - /// Delete a beatmap difficulty. - /// - /// The beatmap difficulty to hide. - public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap); - - /// - /// Restore a beatmap difficulty. - /// - /// The beatmap difficulty to restore. - public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); - - /// - /// Retrieve a instance for the provided - /// - /// The beatmap to lookup. - /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. - /// A instance correlating to the provided . - public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) - { - if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) - return DefaultBeatmap; - - if (beatmapInfo.Metadata == null) - beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - - WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo, audioManager); - - previous?.TransferTo(working); - - return working; - } - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// The first result for the provided query, or null if no results were found. - public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query); - - /// - /// Returns a list of all usable s. - /// - /// A list of available . - public List GetAllUsableBeatmapSets() => beatmaps.ConsumableItems.Where(s => !s.DeletePending && !s.Protected).ToList(); - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// Results from the provided query. - public IEnumerable QueryBeatmapSets(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().Where(query); - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// The first result for the provided query, or null if no results were found. - public BeatmapInfo QueryBeatmap(Expression> query) => beatmaps.Beatmaps.AsNoTracking().FirstOrDefault(query); - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// Results from the provided query. - public IEnumerable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); - - /// - /// Denotes whether an osu-stable installation is present to perform automated imports from. - /// - public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null; - - /// - /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. - /// - public async Task ImportFromStable() - { - var stable = GetStableStorage?.Invoke(); - - if (stable == null) - { - Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return; - } - - await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs")), TaskCreationOptions.LongRunning); - } - - /// - /// Create a SHA-2 hash from the provided archive based on contained beatmap (.osu) file content. - /// - private string computeBeatmapSetHash(ArchiveReader reader) - { - // 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 => f.EndsWith(".osu"))) - using (Stream s = reader.GetStream(file)) - s.CopyTo(hashable); - - return hashable.ComputeSHA2Hash(); - } - - protected override BeatmapSetInfo CreateModel(ArchiveReader reader) - { - // let's make sure there are actually .osu files to import. - string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); - if (string.IsNullOrEmpty(mapName)) throw new InvalidOperationException("No beatmap files found in the map folder."); - - BeatmapMetadata metadata; - using (var stream = new StreamReader(reader.GetStream(mapName))) - metadata = Decoder.GetDecoder(stream).Decode(stream).Metadata; - - return new BeatmapSetInfo - { - OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, - Beatmaps = new List(), - Hash = computeBeatmapSetHash(reader), - Metadata = metadata - }; - } - - /// - /// Create all required s for the provided archive. - /// - private List createBeatmapDifficulties(ArchiveReader reader) - { - var beatmapInfos = new List(); - - foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu"))) - { - using (var raw = reader.GetStream(name)) - using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit - using (var sr = new StreamReader(ms)) - { - raw.CopyTo(ms); - ms.Position = 0; - - var decoder = Decoder.GetDecoder(sr); - Beatmap beatmap = decoder.Decode(sr); - - beatmap.BeatmapInfo.Path = name; - beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); - beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - - RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); - - // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.BeatmapInfo.Ruleset = ruleset; - beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0; - - beatmapInfos.Add(beatmap.BeatmapInfo); - } - } - - return beatmapInfos; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using osu.Framework.Audio; +using osu.Framework.Extensions; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Beatmaps.Formats; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.IO.Archives; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Notifications; +using osu.Game.Rulesets; + +namespace osu.Game.Beatmaps +{ + /// + /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. + /// + public partial class BeatmapManager : ArchiveModelManager + { + /// + /// Fired when a single difficulty has been hidden. + /// + public event Action BeatmapHidden; + + /// + /// Fired when a single difficulty has been restored. + /// + public event Action BeatmapRestored; + + /// + /// Fired when a beatmap download begins. + /// + public event Action BeatmapDownloadBegan; + + /// + /// A default representation of a WorkingBeatmap to use when no beatmap is available. + /// + public WorkingBeatmap DefaultBeatmap { private get; set; } + + public override string[] HandledExtensions => new[] { ".osz" }; + + private readonly RulesetStore rulesets; + + private readonly BeatmapStore beatmaps; + + private readonly APIAccess api; + + private readonly AudioManager audioManager; + + private readonly List currentDownloads = new List(); + + /// + /// Set a storage with access to an osu-stable install for import purposes. + /// + public Func GetStableStorage { private get; set; } + + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null) + : base(storage, contextFactory, new BeatmapStore(contextFactory), importHost) + { + beatmaps = (BeatmapStore)ModelStore; + beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); + beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); + + this.rulesets = rulesets; + this.api = api; + this.audioManager = audioManager; + } + + protected override void Populate(BeatmapSetInfo model, ArchiveReader archive) + { + model.Beatmaps = createBeatmapDifficulties(archive); + + // remove metadata from difficulties where it matches the set + foreach (BeatmapInfo b in model.Beatmaps) + if (model.Metadata.Equals(b.Metadata)) + b.Metadata = null; + } + + protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model) + { + // check if this beatmap has already been imported and exit early if so + var existingHashMatch = beatmaps.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); + if (existingHashMatch != null) + { + Undelete(existingHashMatch); + return existingHashMatch; + } + + // check if a set already exists with the same online id + if (model.OnlineBeatmapSetID != null) + { + var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID); + if (existingOnlineId != null) + { + Delete(existingOnlineId); + beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID); + } + } + + return null; + } + + /// + /// Downloads a beatmap. + /// This will post notifications tracking progress. + /// + /// The to be downloaded. + /// Whether the beatmap should be downloaded without video. Defaults to false. + public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false) + { + var existing = GetExistingDownload(beatmapSetInfo); + + if (existing != null || api == null) return; + + if (!api.LocalUser.Value.IsSupporter) + { + PostNotification?.Invoke(new SimpleNotification + { + Icon = FontAwesome.fa_superpowers, + Text = "You gotta be a supporter to download for now 'yo" + }); + return; + } + + var downloadNotification = new ProgressNotification + { + CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!", + Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}", + }; + + var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo); + + request.DownloadProgressed += progress => + { + downloadNotification.State = ProgressNotificationState.Active; + downloadNotification.Progress = progress; + }; + + request.Success += data => + { + downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}"; + + Task.Factory.StartNew(() => + { + // This gets scheduled back to the update thread, but we want the import to run in the background. + using (var stream = new MemoryStream(data)) + using (var archive = new ZipArchiveReader(stream, beatmapSetInfo.ToString())) + Import(archive); + + downloadNotification.State = ProgressNotificationState.Completed; + currentDownloads.Remove(request); + }, TaskCreationOptions.LongRunning); + }; + + request.Failure += error => + { + if (error is OperationCanceledException) return; + + downloadNotification.State = ProgressNotificationState.Completed; + Logger.Error(error, "Beatmap download failed!"); + currentDownloads.Remove(request); + }; + + downloadNotification.CancelRequested += () => + { + request.Cancel(); + currentDownloads.Remove(request); + downloadNotification.State = ProgressNotificationState.Cancelled; + return true; + }; + + currentDownloads.Add(request); + PostNotification?.Invoke(downloadNotification); + + // don't run in the main api queue as this is a long-running task. + Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning); + BeatmapDownloadBegan?.Invoke(request); + } + + /// + /// Get an existing download request if it exists. + /// + /// The whose download request is wanted. + /// The object if it exists, or null. + public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID); + + /// + /// Delete a beatmap difficulty. + /// + /// The beatmap difficulty to hide. + public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap); + + /// + /// Restore a beatmap difficulty. + /// + /// The beatmap difficulty to restore. + public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); + + /// + /// Retrieve a instance for the provided + /// + /// The beatmap to lookup. + /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. + /// A instance correlating to the provided . + public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + { + if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) + return DefaultBeatmap; + + if (beatmapInfo.Metadata == null) + beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; + + WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo, audioManager); + + previous?.TransferTo(working); + + return working; + } + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query); + + /// + /// Returns a list of all usable s. + /// + /// A list of available . + public List GetAllUsableBeatmapSets() => beatmaps.ConsumableItems.Where(s => !s.DeletePending && !s.Protected).ToList(); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// Results from the provided query. + public IEnumerable QueryBeatmapSets(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().Where(query); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public BeatmapInfo QueryBeatmap(Expression> query) => beatmaps.Beatmaps.AsNoTracking().FirstOrDefault(query); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// Results from the provided query. + public IEnumerable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); + + /// + /// Denotes whether an osu-stable installation is present to perform automated imports from. + /// + public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null; + + /// + /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. + /// + public async Task ImportFromStable() + { + var stable = GetStableStorage?.Invoke(); + + if (stable == null) + { + Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); + return; + } + + await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs")), TaskCreationOptions.LongRunning); + } + + /// + /// Create a SHA-2 hash from the provided archive based on contained beatmap (.osu) file content. + /// + private string computeBeatmapSetHash(ArchiveReader reader) + { + // 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 => f.EndsWith(".osu"))) + using (Stream s = reader.GetStream(file)) + s.CopyTo(hashable); + + return hashable.ComputeSHA2Hash(); + } + + protected override BeatmapSetInfo CreateModel(ArchiveReader reader) + { + // let's make sure there are actually .osu files to import. + string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); + if (string.IsNullOrEmpty(mapName)) throw new InvalidOperationException("No beatmap files found in the map folder."); + + BeatmapMetadata metadata; + using (var stream = new StreamReader(reader.GetStream(mapName))) + metadata = Decoder.GetDecoder(stream).Decode(stream).Metadata; + + return new BeatmapSetInfo + { + OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, + Beatmaps = new List(), + Hash = computeBeatmapSetHash(reader), + Metadata = metadata + }; + } + + /// + /// Create all required s for the provided archive. + /// + private List createBeatmapDifficulties(ArchiveReader reader) + { + var beatmapInfos = new List(); + + foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu"))) + { + using (var raw = reader.GetStream(name)) + using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit + using (var sr = new StreamReader(ms)) + { + raw.CopyTo(ms); + ms.Position = 0; + + var decoder = Decoder.GetDecoder(sr); + Beatmap beatmap = decoder.Decode(sr); + + beatmap.BeatmapInfo.Path = name; + beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); + beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); + + RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); + + // TODO: this should be done in a better place once we actually need to dynamically update it. + beatmap.BeatmapInfo.Ruleset = ruleset; + beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0; + + beatmapInfos.Add(beatmap.BeatmapInfo); + } + } + + return beatmapInfos; + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 5874314f75..8e09d66c42 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -1,125 +1,125 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using System.Linq; -using osu.Framework.Audio; -using osu.Framework.Audio.Track; -using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; -using osu.Framework.Logging; -using osu.Game.Beatmaps.Formats; -using osu.Game.Graphics.Textures; -using osu.Game.Skinning; -using osu.Game.Storyboards; - -namespace osu.Game.Beatmaps -{ - public partial class BeatmapManager - { - protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap - { - private readonly IResourceStore store; - private readonly AudioManager audioManager; - - public BeatmapManagerWorkingBeatmap(IResourceStore store, BeatmapInfo beatmapInfo, AudioManager audioManager) - : base(beatmapInfo) - { - this.store = store; - this.audioManager = audioManager; - } - - protected override Beatmap GetBeatmap() - { - try - { - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) - return Decoder.GetDecoder(stream).Decode(stream); - } - catch - { - return null; - } - } - - private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath; - - protected override Texture GetBackground() - { - if (Metadata?.BackgroundFile == null) - return null; - - try - { - return new LargeTextureStore(new RawTextureLoaderStore(store)).Get(getPathForFile(Metadata.BackgroundFile)); - } - catch - { - return null; - } - } - - protected override Track GetTrack() - { - try - { - var trackData = store.GetStream(getPathForFile(Metadata.AudioFile)); - return trackData == null ? null : new TrackBass(trackData); - } - catch - { - return new TrackVirtual(); - } - } - - protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile))); - - protected override Storyboard GetStoryboard() - { - Storyboard storyboard; - try - { - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) - { - var decoder = Decoder.GetDecoder(stream); - - // todo: support loading from both set-wide storyboard *and* beatmap specific. - if (BeatmapSetInfo?.StoryboardFile == null) - storyboard = decoder.Decode(stream); - else - { - using (var secondaryStream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - storyboard = decoder.Decode(stream, secondaryStream); - } - } - } - catch (Exception e) - { - Logger.Error(e, "Storyboard failed to load"); - storyboard = new Storyboard(); - } - - storyboard.BeatmapInfo = BeatmapInfo; - - return storyboard; - } - - protected override Skin GetSkin() - { - Skin skin; - try - { - skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager); - } - catch (Exception e) - { - Logger.Error(e, "Skin failed to load"); - skin = new DefaultSkin(); - } - - return skin; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using System.Linq; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; +using osu.Framework.Logging; +using osu.Game.Beatmaps.Formats; +using osu.Game.Graphics.Textures; +using osu.Game.Skinning; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps +{ + public partial class BeatmapManager + { + protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap + { + private readonly IResourceStore store; + private readonly AudioManager audioManager; + + public BeatmapManagerWorkingBeatmap(IResourceStore store, BeatmapInfo beatmapInfo, AudioManager audioManager) + : base(beatmapInfo) + { + this.store = store; + this.audioManager = audioManager; + } + + protected override Beatmap GetBeatmap() + { + try + { + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + return Decoder.GetDecoder(stream).Decode(stream); + } + catch + { + return null; + } + } + + private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath; + + protected override Texture GetBackground() + { + if (Metadata?.BackgroundFile == null) + return null; + + try + { + return new LargeTextureStore(new RawTextureLoaderStore(store)).Get(getPathForFile(Metadata.BackgroundFile)); + } + catch + { + return null; + } + } + + protected override Track GetTrack() + { + try + { + var trackData = store.GetStream(getPathForFile(Metadata.AudioFile)); + return trackData == null ? null : new TrackBass(trackData); + } + catch + { + return new TrackVirtual(); + } + } + + protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile))); + + protected override Storyboard GetStoryboard() + { + Storyboard storyboard; + try + { + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + { + var decoder = Decoder.GetDecoder(stream); + + // todo: support loading from both set-wide storyboard *and* beatmap specific. + if (BeatmapSetInfo?.StoryboardFile == null) + storyboard = decoder.Decode(stream); + else + { + using (var secondaryStream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + storyboard = decoder.Decode(stream, secondaryStream); + } + } + } + catch (Exception e) + { + Logger.Error(e, "Storyboard failed to load"); + storyboard = new Storyboard(); + } + + storyboard.BeatmapInfo = BeatmapInfo; + + return storyboard; + } + + protected override Skin GetSkin() + { + Skin skin; + try + { + skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager); + } + catch (Exception e) + { + Logger.Error(e, "Skin failed to load"); + skin = new DefaultSkin(); + } + + return skin; + } + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index a28266dc62..34147c18d2 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -1,98 +1,98 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using Newtonsoft.Json; -using osu.Game.Users; - -namespace osu.Game.Beatmaps -{ - [Serializable] - public class BeatmapMetadata : IEquatable - { - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - [JsonIgnore] - public int ID { get; set; } - - private int? onlineBeatmapSetID; - - [NotMapped] - [JsonProperty(@"id")] - public int? OnlineBeatmapSetID - { - get { return onlineBeatmapSetID; } - set { onlineBeatmapSetID = value > 0 ? value : null; } - } - - public string Title { get; set; } - public string TitleUnicode { get; set; } - public string Artist { get; set; } - public string ArtistUnicode { get; set; } - - [JsonIgnore] - public List Beatmaps { get; set; } - - [JsonIgnore] - public List BeatmapSets { get; set; } - - /// - /// Helper property to deserialize a username to . - /// - [JsonProperty(@"creator")] - [Column("Author")] - public string AuthorString - { - get { return Author?.Username; } - set { Author = new User { Username = value }; } - } - - /// - /// The author of the beatmaps in this set. - /// - [JsonIgnore] - public User Author; - - public string Source { get; set; } - - [JsonProperty(@"tags")] - public string Tags { get; set; } - public int PreviewTime { get; set; } - public string AudioFile { get; set; } - public string BackgroundFile { get; set; } - - public override string ToString() => $"{Artist} - {Title} ({Author})"; - - [JsonIgnore] - public string[] SearchableTerms => new[] - { - Author?.Username, - Artist, - ArtistUnicode, - Title, - TitleUnicode, - Source, - Tags - }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); - - public bool Equals(BeatmapMetadata other) - { - if (other == null) - return false; - - return onlineBeatmapSetID == other.onlineBeatmapSetID - && Title == other.Title - && TitleUnicode == other.TitleUnicode - && Artist == other.Artist - && ArtistUnicode == other.ArtistUnicode - && AuthorString == other.AuthorString - && Source == other.Source - && Tags == other.Tags - && PreviewTime == other.PreviewTime - && AudioFile == other.AudioFile - && BackgroundFile == other.BackgroundFile; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using Newtonsoft.Json; +using osu.Game.Users; + +namespace osu.Game.Beatmaps +{ + [Serializable] + public class BeatmapMetadata : IEquatable + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [JsonIgnore] + public int ID { get; set; } + + private int? onlineBeatmapSetID; + + [NotMapped] + [JsonProperty(@"id")] + public int? OnlineBeatmapSetID + { + get { return onlineBeatmapSetID; } + set { onlineBeatmapSetID = value > 0 ? value : null; } + } + + public string Title { get; set; } + public string TitleUnicode { get; set; } + public string Artist { get; set; } + public string ArtistUnicode { get; set; } + + [JsonIgnore] + public List Beatmaps { get; set; } + + [JsonIgnore] + public List BeatmapSets { get; set; } + + /// + /// Helper property to deserialize a username to . + /// + [JsonProperty(@"creator")] + [Column("Author")] + public string AuthorString + { + get { return Author?.Username; } + set { Author = new User { Username = value }; } + } + + /// + /// The author of the beatmaps in this set. + /// + [JsonIgnore] + public User Author; + + public string Source { get; set; } + + [JsonProperty(@"tags")] + public string Tags { get; set; } + public int PreviewTime { get; set; } + public string AudioFile { get; set; } + public string BackgroundFile { get; set; } + + public override string ToString() => $"{Artist} - {Title} ({Author})"; + + [JsonIgnore] + public string[] SearchableTerms => new[] + { + Author?.Username, + Artist, + ArtistUnicode, + Title, + TitleUnicode, + Source, + Tags + }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + public bool Equals(BeatmapMetadata other) + { + if (other == null) + return false; + + return onlineBeatmapSetID == other.onlineBeatmapSetID + && Title == other.Title + && TitleUnicode == other.TitleUnicode + && Artist == other.Artist + && ArtistUnicode == other.ArtistUnicode + && AuthorString == other.AuthorString + && Source == other.Source + && Tags == other.Tags + && PreviewTime == other.PreviewTime + && AudioFile == other.AudioFile + && BackgroundFile == other.BackgroundFile; + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapMetrics.cs b/osu.Game/Beatmaps/BeatmapMetrics.cs index 78527e7a02..427c2b50ac 100644 --- a/osu.Game/Beatmaps/BeatmapMetrics.cs +++ b/osu.Game/Beatmaps/BeatmapMetrics.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace osu.Game.Beatmaps -{ - /// - /// Beatmap metrics based on acculumated online data from community plays. - /// - public class BeatmapMetrics - { - /// - /// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?). - /// - public IEnumerable Ratings { get; set; } - - /// - /// Points of failure on a relative time scale (usually 0..100). - /// - [JsonProperty(@"fail")] - public IEnumerable Fails { get; set; } - - /// - /// Points of retry on a relative time scale (usually 0..100). - /// - [JsonProperty(@"exit")] - public IEnumerable Retries { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace osu.Game.Beatmaps +{ + /// + /// Beatmap metrics based on acculumated online data from community plays. + /// + public class BeatmapMetrics + { + /// + /// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?). + /// + public IEnumerable Ratings { get; set; } + + /// + /// Points of failure on a relative time scale (usually 0..100). + /// + [JsonProperty(@"fail")] + public IEnumerable Fails { get; set; } + + /// + /// Points of retry on a relative time scale (usually 0..100). + /// + [JsonProperty(@"exit")] + public IEnumerable Retries { get; set; } + } +} diff --git a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs index 58e4b2b5aa..3c1c2b10ce 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs @@ -1,36 +1,36 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps -{ - /// - /// Beatmap info retrieved for previewing locally without having the beatmap downloaded. - /// - public class BeatmapOnlineInfo - { - /// - /// The length in milliseconds of this beatmap's song. - /// - public double Length { get; set; } - - /// - /// The amount of circles in this beatmap. - /// - public int CircleCount { get; set; } - - /// - /// The amount of sliders in this beatmap. - /// - public int SliderCount { get; set; } - - /// - /// The amount of plays this beatmap has. - /// - public int PlayCount { get; set; } - - /// - /// The amount of passes this beatmap has. - /// - public int PassCount { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps +{ + /// + /// Beatmap info retrieved for previewing locally without having the beatmap downloaded. + /// + public class BeatmapOnlineInfo + { + /// + /// The length in milliseconds of this beatmap's song. + /// + public double Length { get; set; } + + /// + /// The amount of circles in this beatmap. + /// + public int CircleCount { get; set; } + + /// + /// The amount of sliders in this beatmap. + /// + public int SliderCount { get; set; } + + /// + /// The amount of plays this beatmap has. + /// + public int PlayCount { get; set; } + + /// + /// The amount of passes this beatmap has. + /// + public int PassCount { get; set; } + } +} diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index f2cc419043..8f5a2a4cab 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -1,49 +1,49 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Beatmaps -{ - /// - /// Processes a post-converted Beatmap. - /// - /// The type of HitObject contained in the Beatmap. - public class BeatmapProcessor - where TObject : HitObject - { - /// - /// Post-processes a Beatmap to add mode-specific components that aren't added during conversion. - /// - /// An example of such a usage is for combo colours. - /// - /// - /// The Beatmap to process. - public virtual void PostProcess(Beatmap beatmap) - { - IHasComboInformation lastObj = null; - - foreach (var obj in beatmap.HitObjects.OfType()) - { - if (obj.NewCombo) - { - obj.IndexInCurrentCombo = 0; - if (lastObj != null) - { - lastObj.LastInCombo = true; - obj.ComboIndex = lastObj.ComboIndex + 1; - } - } - else if (lastObj != null) - { - obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1; - obj.ComboIndex = lastObj.ComboIndex; - } - - lastObj = obj; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Beatmaps +{ + /// + /// Processes a post-converted Beatmap. + /// + /// The type of HitObject contained in the Beatmap. + public class BeatmapProcessor + where TObject : HitObject + { + /// + /// Post-processes a Beatmap to add mode-specific components that aren't added during conversion. + /// + /// An example of such a usage is for combo colours. + /// + /// + /// The Beatmap to process. + public virtual void PostProcess(Beatmap beatmap) + { + IHasComboInformation lastObj = null; + + foreach (var obj in beatmap.HitObjects.OfType()) + { + if (obj.NewCombo) + { + obj.IndexInCurrentCombo = 0; + if (lastObj != null) + { + lastObj.LastInCombo = true; + obj.ComboIndex = lastObj.ComboIndex + 1; + } + } + else if (lastObj != null) + { + obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1; + obj.ComboIndex = lastObj.ComboIndex; + } + + lastObj = obj; + } + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs index e88af6ed30..4fa286cbec 100644 --- a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using osu.Game.Database; -using osu.Game.IO; - -namespace osu.Game.Beatmaps -{ - public class BeatmapSetFileInfo : INamedFileInfo - { - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int ID { get; set; } - - public int BeatmapSetInfoID { get; set; } - - public int FileInfoID { get; set; } - - public FileInfo FileInfo { get; set; } - - [Required] - public string Filename { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using osu.Game.Database; +using osu.Game.IO; + +namespace osu.Game.Beatmaps +{ + public class BeatmapSetFileInfo : INamedFileInfo + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ID { get; set; } + + public int BeatmapSetInfoID { get; set; } + + public int FileInfoID { get; set; } + + public FileInfo FileInfo { get; set; } + + [Required] + public string Filename { get; set; } + } +} diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 1736e3fa90..fa08c6cb68 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -1,40 +1,40 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using osu.Game.Database; - -namespace osu.Game.Beatmaps -{ - public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete - { - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int ID { get; set; } - - public int? OnlineBeatmapSetID { get; set; } - - public BeatmapMetadata Metadata { get; set; } - - public List Beatmaps { get; set; } - - [NotMapped] - public BeatmapSetOnlineInfo OnlineInfo { get; set; } - - public double MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty); - - [NotMapped] - public bool DeletePending { get; set; } - - public string Hash { get; set; } - - public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename; - - public List Files { get; set; } - - public override string ToString() => Metadata.ToString(); - - public bool Protected { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using osu.Game.Database; + +namespace osu.Game.Beatmaps +{ + public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ID { get; set; } + + public int? OnlineBeatmapSetID { get; set; } + + public BeatmapMetadata Metadata { get; set; } + + public List Beatmaps { get; set; } + + [NotMapped] + public BeatmapSetOnlineInfo OnlineInfo { get; set; } + + public double MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty); + + [NotMapped] + public bool DeletePending { get; set; } + + public string Hash { get; set; } + + public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename; + + public List Files { get; set; } + + public override string ToString() => Metadata.ToString(); + + public bool Protected { get; set; } + } +} diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index f7221a6ac3..a70caf0207 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -1,82 +1,82 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using Newtonsoft.Json; - -namespace osu.Game.Beatmaps -{ - /// - /// Beatmap set info retrieved for previewing locally without having the set downloaded. - /// - public class BeatmapSetOnlineInfo - { - /// - /// The date this beatmap set was submitted to the online listing. - /// - public DateTimeOffset Submitted { get; set; } - - /// - /// The date this beatmap set was ranked. - /// - public DateTimeOffset? Ranked { get; set; } - - /// - /// The date this beatmap set was last updated. - /// - public DateTimeOffset? LastUpdated { get; set; } - - /// - /// The status of this beatmap set. - /// - public BeatmapSetOnlineStatus Status { get; set; } - - /// - /// Whether or not this beatmap set has a background video. - /// - public bool HasVideo { get; set; } - - /// - /// The different sizes of cover art for this beatmap set. - /// - public BeatmapSetOnlineCovers Covers { get; set; } - - /// - /// A small sample clip of this beatmap set's song. - /// - public string Preview { get; set; } - - /// - /// The beats per minute of this beatmap set's song. - /// - public double BPM { get; set; } - - /// - /// The amount of plays this beatmap set has. - /// - public int PlayCount { get; set; } - - /// - /// The amount of people who have favourited this beatmap set. - /// - public int FavouriteCount { get; set; } - } - - public class BeatmapSetOnlineCovers - { - public string CoverLowRes { get; set; } - - [JsonProperty(@"cover@2x")] - public string Cover { get; set; } - - public string CardLowRes { get; set; } - - [JsonProperty(@"card@2x")] - public string Card { get; set; } - - public string ListLowRes { get; set; } - - [JsonProperty(@"list@2x")] - public string List { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using Newtonsoft.Json; + +namespace osu.Game.Beatmaps +{ + /// + /// Beatmap set info retrieved for previewing locally without having the set downloaded. + /// + public class BeatmapSetOnlineInfo + { + /// + /// The date this beatmap set was submitted to the online listing. + /// + public DateTimeOffset Submitted { get; set; } + + /// + /// The date this beatmap set was ranked. + /// + public DateTimeOffset? Ranked { get; set; } + + /// + /// The date this beatmap set was last updated. + /// + public DateTimeOffset? LastUpdated { get; set; } + + /// + /// The status of this beatmap set. + /// + public BeatmapSetOnlineStatus Status { get; set; } + + /// + /// Whether or not this beatmap set has a background video. + /// + public bool HasVideo { get; set; } + + /// + /// The different sizes of cover art for this beatmap set. + /// + public BeatmapSetOnlineCovers Covers { get; set; } + + /// + /// A small sample clip of this beatmap set's song. + /// + public string Preview { get; set; } + + /// + /// The beats per minute of this beatmap set's song. + /// + public double BPM { get; set; } + + /// + /// The amount of plays this beatmap set has. + /// + public int PlayCount { get; set; } + + /// + /// The amount of people who have favourited this beatmap set. + /// + public int FavouriteCount { get; set; } + } + + public class BeatmapSetOnlineCovers + { + public string CoverLowRes { get; set; } + + [JsonProperty(@"cover@2x")] + public string Cover { get; set; } + + public string CardLowRes { get; set; } + + [JsonProperty(@"card@2x")] + public string Card { get; set; } + + public string ListLowRes { get; set; } + + [JsonProperty(@"list@2x")] + public string List { get; set; } + } +} diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs index c7f767d3b2..4f97ee4133 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps -{ - public enum BeatmapSetOnlineStatus - { - None = -3, - Graveyard = -2, - WIP = -1, - Pending = 0, - Ranked = 1, - Approved = 2, - Qualified = 3, - Loved = 4, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps +{ + public enum BeatmapSetOnlineStatus + { + None = -3, + Graveyard = -2, + WIP = -1, + Pending = 0, + Ranked = 1, + Approved = 2, + Qualified = 3, + Loved = 4, + } +} diff --git a/osu.Game/Beatmaps/BeatmapStatistic.cs b/osu.Game/Beatmaps/BeatmapStatistic.cs index d061651e71..aebe2573d7 100644 --- a/osu.Game/Beatmaps/BeatmapStatistic.cs +++ b/osu.Game/Beatmaps/BeatmapStatistic.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; - -namespace osu.Game.Beatmaps -{ - public class BeatmapStatistic - { - public FontAwesome Icon; - public string Content; - public string Name; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Beatmaps +{ + public class BeatmapStatistic + { + public FontAwesome Icon; + public string Content; + public string Name; + } +} diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 93ad1badd2..5bdc42cdf3 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -1,97 +1,97 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using osu.Game.Database; - -namespace osu.Game.Beatmaps -{ - /// - /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing - /// - public class BeatmapStore : MutableDatabaseBackedStore - { - public event Action BeatmapHidden; - public event Action BeatmapRestored; - - public BeatmapStore(IDatabaseContextFactory factory) - : base(factory) - { - } - - /// - /// Hide a in the database. - /// - /// The beatmap to hide. - /// Whether the beatmap's was changed. - public bool Hide(BeatmapInfo beatmap) - { - using (ContextFactory.GetForWrite()) - { - Refresh(ref beatmap, Beatmaps); - - if (beatmap.Hidden) return false; - beatmap.Hidden = true; - } - - BeatmapHidden?.Invoke(beatmap); - return true; - } - - /// - /// Restore a previously hidden . - /// - /// The beatmap to restore. - /// Whether the beatmap's was changed. - public bool Restore(BeatmapInfo beatmap) - { - using (ContextFactory.GetForWrite()) - { - Refresh(ref beatmap, Beatmaps); - - if (!beatmap.Hidden) return false; - beatmap.Hidden = false; - } - - BeatmapRestored?.Invoke(beatmap); - return true; - } - - protected override IQueryable AddIncludesForDeletion(IQueryable query) => - base.AddIncludesForDeletion(query) - .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) - .Include(s => s.Metadata); - - protected override IQueryable AddIncludesForConsumption(IQueryable query) => - base.AddIncludesForConsumption(query) - .Include(s => s.Metadata) - .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) - .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) - .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Files).ThenInclude(f => f.FileInfo); - - protected override void Purge(List items, OsuDbContext context) - { - // metadata is M-N so we can't rely on cascades - context.BeatmapMetadata.RemoveRange(items.Select(s => s.Metadata)); - context.BeatmapMetadata.RemoveRange(items.SelectMany(s => s.Beatmaps.Select(b => b.Metadata).Where(m => m != null))); - - // todo: we can probably make cascades work here with a FK in BeatmapDifficulty. just make to make it work correctly. - context.BeatmapDifficulty.RemoveRange(items.SelectMany(s => s.Beatmaps.Select(b => b.BaseDifficulty))); - - base.Purge(items, context); - } - - public IQueryable Beatmaps => - ContextFactory.Get().BeatmapInfo - .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) - .Include(b => b.BeatmapSet).ThenInclude(s => s.Files).ThenInclude(f => f.FileInfo) - .Include(b => b.Metadata) - .Include(b => b.Ruleset) - .Include(b => b.BaseDifficulty); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using osu.Game.Database; + +namespace osu.Game.Beatmaps +{ + /// + /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing + /// + public class BeatmapStore : MutableDatabaseBackedStore + { + public event Action BeatmapHidden; + public event Action BeatmapRestored; + + public BeatmapStore(IDatabaseContextFactory factory) + : base(factory) + { + } + + /// + /// Hide a in the database. + /// + /// The beatmap to hide. + /// Whether the beatmap's was changed. + public bool Hide(BeatmapInfo beatmap) + { + using (ContextFactory.GetForWrite()) + { + Refresh(ref beatmap, Beatmaps); + + if (beatmap.Hidden) return false; + beatmap.Hidden = true; + } + + BeatmapHidden?.Invoke(beatmap); + return true; + } + + /// + /// Restore a previously hidden . + /// + /// The beatmap to restore. + /// Whether the beatmap's was changed. + public bool Restore(BeatmapInfo beatmap) + { + using (ContextFactory.GetForWrite()) + { + Refresh(ref beatmap, Beatmaps); + + if (!beatmap.Hidden) return false; + beatmap.Hidden = false; + } + + BeatmapRestored?.Invoke(beatmap); + return true; + } + + protected override IQueryable AddIncludesForDeletion(IQueryable query) => + base.AddIncludesForDeletion(query) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) + .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) + .Include(s => s.Metadata); + + protected override IQueryable AddIncludesForConsumption(IQueryable query) => + base.AddIncludesForConsumption(query) + .Include(s => s.Metadata) + .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) + .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) + .Include(s => s.Files).ThenInclude(f => f.FileInfo); + + protected override void Purge(List items, OsuDbContext context) + { + // metadata is M-N so we can't rely on cascades + context.BeatmapMetadata.RemoveRange(items.Select(s => s.Metadata)); + context.BeatmapMetadata.RemoveRange(items.SelectMany(s => s.Beatmaps.Select(b => b.Metadata).Where(m => m != null))); + + // todo: we can probably make cascades work here with a FK in BeatmapDifficulty. just make to make it work correctly. + context.BeatmapDifficulty.RemoveRange(items.SelectMany(s => s.Beatmaps.Select(b => b.BaseDifficulty))); + + base.Purge(items, context); + } + + public IQueryable Beatmaps => + ContextFactory.Get().BeatmapInfo + .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) + .Include(b => b.BeatmapSet).ThenInclude(s => s.Files).ThenInclude(f => f.FileInfo) + .Include(b => b.Metadata) + .Include(b => b.Ruleset) + .Include(b => b.BaseDifficulty); + } +} diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 2b49716853..db9e712d86 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Beatmaps.ControlPoints -{ - public class ControlPoint : IComparable, IEquatable - { - /// - /// The time at which the control point takes effect. - /// - public double Time; - - public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time); - - public bool Equals(ControlPoint other) => Time.Equals(other?.Time); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class ControlPoint : IComparable, IEquatable + { + /// + /// The time at which the control point takes effect. + /// + public double Time; + + public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time); + + public bool Equals(ControlPoint other) => Time.Equals(other?.Time); + } +} diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 69988b2a29..dad69321a5 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -1,120 +1,120 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using osu.Framework.Lists; - -namespace osu.Game.Beatmaps.ControlPoints -{ - [Serializable] - public class ControlPointInfo - { - /// - /// All timing points. - /// - [JsonProperty] - public SortedList TimingPoints { get; private set; } = new SortedList(Comparer.Default); - - /// - /// All difficulty points. - /// - [JsonProperty] - public SortedList DifficultyPoints { get; private set; } = new SortedList(Comparer.Default); - - /// - /// All sound points. - /// - [JsonProperty] - public SortedList SamplePoints { get; private set; } = new SortedList(Comparer.Default); - - /// - /// All effect points. - /// - [JsonProperty] - public SortedList EffectPoints { get; private set; } = new SortedList(Comparer.Default); - - /// - /// Finds the difficulty control point that is active at . - /// - /// The time to find the difficulty control point at. - /// The difficulty control point. - public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time); - - /// - /// Finds the effect control point that is active at . - /// - /// The time to find the effect control point at. - /// The effect control point. - public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time); - - /// - /// Finds the sound control point that is active at . - /// - /// The time to find the sound control point at. - /// The sound control point. - public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.FirstOrDefault()); - - /// - /// Finds the timing control point that is active at . - /// - /// The time to find the timing control point at. - /// The timing control point. - public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault()); - - /// - /// Finds the maximum BPM represented by any timing control point. - /// - [JsonIgnore] - public double BPMMaximum => - 60000 / (TimingPoints.OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; - - /// - /// Finds the minimum BPM represented by any timing control point. - /// - [JsonIgnore] - public double BPMMinimum => - 60000 / (TimingPoints.OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; - - /// - /// Finds the mode BPM (most common BPM) represented by the control points. - /// - [JsonIgnore] - public double BPMMode => - 60000 / (TimingPoints.GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength; - - /// - /// Binary searches one of the control point lists to find the active control point at . - /// - /// The list to search. - /// The time to find the control point at. - /// The control point to use when is before any control points. If null, a new control point will be constructed. - /// The active control point at . - private T binarySearch(SortedList list, double time, T prePoint = null) - where T : ControlPoint, new() - { - if (list == null) - throw new ArgumentNullException(nameof(list)); - - if (list.Count == 0) - return new T(); - - if (time < list[0].Time) - return prePoint ?? new T(); - - int index = list.BinarySearch(new T { Time = time }); - - // Check if we've found an exact match (t == time) - if (index >= 0) - return list[index]; - - index = ~index; - - // BinarySearch will return the index of the first element _greater_ than the search - // This is the inactive point - the active point is the one before it (index - 1) - return list[index - 1]; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using osu.Framework.Lists; + +namespace osu.Game.Beatmaps.ControlPoints +{ + [Serializable] + public class ControlPointInfo + { + /// + /// All timing points. + /// + [JsonProperty] + public SortedList TimingPoints { get; private set; } = new SortedList(Comparer.Default); + + /// + /// All difficulty points. + /// + [JsonProperty] + public SortedList DifficultyPoints { get; private set; } = new SortedList(Comparer.Default); + + /// + /// All sound points. + /// + [JsonProperty] + public SortedList SamplePoints { get; private set; } = new SortedList(Comparer.Default); + + /// + /// All effect points. + /// + [JsonProperty] + public SortedList EffectPoints { get; private set; } = new SortedList(Comparer.Default); + + /// + /// Finds the difficulty control point that is active at . + /// + /// The time to find the difficulty control point at. + /// The difficulty control point. + public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time); + + /// + /// Finds the effect control point that is active at . + /// + /// The time to find the effect control point at. + /// The effect control point. + public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time); + + /// + /// Finds the sound control point that is active at . + /// + /// The time to find the sound control point at. + /// The sound control point. + public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.FirstOrDefault()); + + /// + /// Finds the timing control point that is active at . + /// + /// The time to find the timing control point at. + /// The timing control point. + public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault()); + + /// + /// Finds the maximum BPM represented by any timing control point. + /// + [JsonIgnore] + public double BPMMaximum => + 60000 / (TimingPoints.OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + + /// + /// Finds the minimum BPM represented by any timing control point. + /// + [JsonIgnore] + public double BPMMinimum => + 60000 / (TimingPoints.OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + + /// + /// Finds the mode BPM (most common BPM) represented by the control points. + /// + [JsonIgnore] + public double BPMMode => + 60000 / (TimingPoints.GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength; + + /// + /// Binary searches one of the control point lists to find the active control point at . + /// + /// The list to search. + /// The time to find the control point at. + /// The control point to use when is before any control points. If null, a new control point will be constructed. + /// The active control point at . + private T binarySearch(SortedList list, double time, T prePoint = null) + where T : ControlPoint, new() + { + if (list == null) + throw new ArgumentNullException(nameof(list)); + + if (list.Count == 0) + return new T(); + + if (time < list[0].Time) + return prePoint ?? new T(); + + int index = list.BinarySearch(new T { Time = time }); + + // Check if we've found an exact match (t == time) + if (index >= 0) + return list[index]; + + index = ~index; + + // BinarySearch will return the index of the first element _greater_ than the search + // This is the inactive point - the active point is the one before it (index - 1) + return list[index - 1]; + } + } +} diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 2b42553891..9f717d21e3 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; - -namespace osu.Game.Beatmaps.ControlPoints -{ - public class DifficultyControlPoint : ControlPoint - { - /// - /// The speed multiplier at this control point. - /// - public double SpeedMultiplier - { - get => speedMultiplier; - set => speedMultiplier = MathHelper.Clamp(value, 0.1, 10); - } - - private double speedMultiplier = 1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class DifficultyControlPoint : ControlPoint + { + /// + /// The speed multiplier at this control point. + /// + public double SpeedMultiplier + { + get => speedMultiplier; + set => speedMultiplier = MathHelper.Clamp(value, 0.1, 10); + } + + private double speedMultiplier = 1; + } +} diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index 566d14a0be..73d5232f44 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps.ControlPoints -{ - public class EffectControlPoint : ControlPoint - { - /// - /// Whether this control point enables Kiai mode. - /// - public bool KiaiMode; - - /// - /// Whether the first bar line of this control point is ignored. - /// - public bool OmitFirstBarLine; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class EffectControlPoint : ControlPoint + { + /// + /// Whether this control point enables Kiai mode. + /// + public bool KiaiMode; + + /// + /// Whether the first bar line of this control point is ignored. + /// + public bool OmitFirstBarLine; + } +} diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index ce06cc8a7b..5d801a1163 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Audio; - -namespace osu.Game.Beatmaps.ControlPoints -{ - public class SampleControlPoint : ControlPoint - { - public const string DEFAULT_BANK = "normal"; - - /// - /// The default sample bank at this control point. - /// - public string SampleBank = DEFAULT_BANK; - - /// - /// The default sample volume at this control point. - /// - public int SampleVolume = 100; - - /// - /// Create a SampleInfo based on the sample settings in this control point. - /// - /// The name of the same. - /// A populated . - public SampleInfo GetSampleInfo(string sampleName = SampleInfo.HIT_NORMAL) => new SampleInfo - { - Bank = SampleBank, - Name = sampleName, - Volume = SampleVolume, - }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Audio; + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class SampleControlPoint : ControlPoint + { + public const string DEFAULT_BANK = "normal"; + + /// + /// The default sample bank at this control point. + /// + public string SampleBank = DEFAULT_BANK; + + /// + /// The default sample volume at this control point. + /// + public int SampleVolume = 100; + + /// + /// Create a SampleInfo based on the sample settings in this control point. + /// + /// The name of the same. + /// A populated . + public SampleInfo GetSampleInfo(string sampleName = SampleInfo.HIT_NORMAL) => new SampleInfo + { + Bank = SampleBank, + Name = sampleName, + Volume = SampleVolume, + }; + } +} diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 0db1f08a90..d20b1b87a6 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Beatmaps.Timing; - -namespace osu.Game.Beatmaps.ControlPoints -{ - public class TimingControlPoint : ControlPoint - { - /// - /// The time signature at this control point. - /// - public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple; - - /// - /// The beat length at this control point. - /// - public double BeatLength - { - get => beatLength; - set => beatLength = MathHelper.Clamp(value, 6, 60000); - } - - private double beatLength = 1000; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Beatmaps.Timing; + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class TimingControlPoint : ControlPoint + { + /// + /// The time signature at this control point. + /// + public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple; + + /// + /// The beat length at this control point. + /// + public double BeatLength + { + get => beatLength; + set => beatLength = MathHelper.Clamp(value, 6, 60000); + } + + private double beatLength = 1000; + } +} diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 2bea31c0d3..5e2d9afd23 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects; -using System.Collections.Generic; -using osu.Game.Rulesets.Mods; -using osu.Framework.Timing; -using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; - -namespace osu.Game.Beatmaps -{ - public abstract class DifficultyCalculator - { - protected double TimeRate = 1; - - public abstract double Calculate(Dictionary categoryDifficulty = null); - } - - public abstract class DifficultyCalculator : DifficultyCalculator where T : HitObject - { - protected readonly Beatmap Beatmap; - protected readonly Mod[] Mods; - - protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null) - { - Mods = mods ?? new Mod[0]; - - var converter = CreateBeatmapConverter(beatmap); - - foreach (var mod in Mods.OfType>()) - mod.ApplyToBeatmapConverter(converter); - - Beatmap = converter.Convert(beatmap); - - ApplyMods(Mods); - - PreprocessHitObjects(); - } - - protected virtual void ApplyMods(Mod[] mods) - { - var clock = new StopwatchClock(); - mods.OfType().ForEach(m => m.ApplyToClock(clock)); - TimeRate = clock.Rate; - - foreach (var mod in Mods.OfType()) - mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty); - - foreach (var h in Beatmap.HitObjects) - h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); - - foreach (var mod in mods.OfType>()) - foreach (var obj in Beatmap.HitObjects) - mod.ApplyToHitObject(obj); - } - - protected virtual void PreprocessHitObjects() - { - } - - protected abstract BeatmapConverter CreateBeatmapConverter(Beatmap beatmap); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects; +using System.Collections.Generic; +using osu.Game.Rulesets.Mods; +using osu.Framework.Timing; +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; + +namespace osu.Game.Beatmaps +{ + public abstract class DifficultyCalculator + { + protected double TimeRate = 1; + + public abstract double Calculate(Dictionary categoryDifficulty = null); + } + + public abstract class DifficultyCalculator : DifficultyCalculator where T : HitObject + { + protected readonly Beatmap Beatmap; + protected readonly Mod[] Mods; + + protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null) + { + Mods = mods ?? new Mod[0]; + + var converter = CreateBeatmapConverter(beatmap); + + foreach (var mod in Mods.OfType>()) + mod.ApplyToBeatmapConverter(converter); + + Beatmap = converter.Convert(beatmap); + + ApplyMods(Mods); + + PreprocessHitObjects(); + } + + protected virtual void ApplyMods(Mod[] mods) + { + var clock = new StopwatchClock(); + mods.OfType().ForEach(m => m.ApplyToClock(clock)); + TimeRate = clock.Rate; + + foreach (var mod in Mods.OfType()) + mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty); + + foreach (var h in Beatmap.HitObjects) + h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); + + foreach (var mod in mods.OfType>()) + foreach (var obj in Beatmap.HitObjects) + mod.ApplyToHitObject(obj); + } + + protected virtual void PreprocessHitObjects() + { + } + + protected abstract BeatmapConverter CreateBeatmapConverter(Beatmap beatmap); + } +} diff --git a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs index 37af06e9f5..b747857e11 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Sprites; - -namespace osu.Game.Beatmaps.Drawables -{ - public class BeatmapBackgroundSprite : Sprite - { - private readonly WorkingBeatmap working; - - public BeatmapBackgroundSprite(WorkingBeatmap working) - { - if (working == null) - throw new ArgumentNullException(nameof(working)); - - this.working = working; - } - - [BackgroundDependencyLoader] - private void load() - { - if (working.Background != null) - Texture = working.Background; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Beatmaps.Drawables +{ + public class BeatmapBackgroundSprite : Sprite + { + private readonly WorkingBeatmap working; + + public BeatmapBackgroundSprite(WorkingBeatmap working) + { + if (working == null) + throw new ArgumentNullException(nameof(working)); + + this.working = working; + } + + [BackgroundDependencyLoader] + private void load() + { + if (working.Background != null) + Texture = working.Background; + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs index 90ccb27228..883c05f1e4 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs @@ -1,54 +1,54 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; - -namespace osu.Game.Beatmaps.Drawables -{ - public class BeatmapSetCover : Sprite - { - private readonly BeatmapSetInfo set; - private readonly BeatmapSetCoverType type; - - public BeatmapSetCover(BeatmapSetInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover) - { - if (set == null) - throw new ArgumentNullException(nameof(set)); - - this.set = set; - this.type = type; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - string resource = null; - - switch (type) - { - case BeatmapSetCoverType.Cover: - resource = set.OnlineInfo.Covers.Cover; - break; - case BeatmapSetCoverType.Card: - resource = set.OnlineInfo.Covers.Card; - break; - case BeatmapSetCoverType.List: - resource = set.OnlineInfo.Covers.List; - break; - } - - if (resource != null) - Texture = textures.Get(resource); - } - } - - public enum BeatmapSetCoverType - { - Cover, - Card, - List, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Beatmaps.Drawables +{ + public class BeatmapSetCover : Sprite + { + private readonly BeatmapSetInfo set; + private readonly BeatmapSetCoverType type; + + public BeatmapSetCover(BeatmapSetInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover) + { + if (set == null) + throw new ArgumentNullException(nameof(set)); + + this.set = set; + this.type = type; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + string resource = null; + + switch (type) + { + case BeatmapSetCoverType.Cover: + resource = set.OnlineInfo.Covers.Cover; + break; + case BeatmapSetCoverType.Card: + resource = set.OnlineInfo.Covers.Card; + break; + case BeatmapSetCoverType.List: + resource = set.OnlineInfo.Covers.List; + break; + } + + if (resource != null) + Texture = textures.Get(resource); + } + } + + public enum BeatmapSetCoverType + { + Cover, + Card, + List, + } +} diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 8ea7a538f9..24e6021421 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -1,54 +1,54 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Sprites; -using OpenTK.Graphics; - -namespace osu.Game.Beatmaps.Drawables -{ - public class BeatmapSetOnlineStatusPill : CircularContainer - { - private readonly OsuSpriteText statusText; - - private BeatmapSetOnlineStatus status = BeatmapSetOnlineStatus.None; - public BeatmapSetOnlineStatus Status - { - get { return status; } - set - { - if (value == status) return; - status = value; - - statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpper(); - } - } - - public BeatmapSetOnlineStatusPill(float textSize, MarginPadding textPadding) - { - AutoSizeAxes = Axes.Both; - Masking = true; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f, - }, - statusText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = @"Exo2.0-Bold", - TextSize = textSize, - Padding = textPadding, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; + +namespace osu.Game.Beatmaps.Drawables +{ + public class BeatmapSetOnlineStatusPill : CircularContainer + { + private readonly OsuSpriteText statusText; + + private BeatmapSetOnlineStatus status = BeatmapSetOnlineStatus.None; + public BeatmapSetOnlineStatus Status + { + get { return status; } + set + { + if (value == status) return; + status = value; + + statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpper(); + } + } + + public BeatmapSetOnlineStatusPill(float textSize, MarginPadding textPadding) + { + AutoSizeAxes = Axes.Both; + Masking = true; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + statusText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = @"Exo2.0-Bold", + TextSize = textSize, + Padding = textPadding, + }, + }; + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs index 2fc39c71e6..025ab0037f 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs @@ -1,79 +1,79 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using OpenTK.Graphics; - -namespace osu.Game.Beatmaps.Drawables -{ - public class DifficultyColouredContainer : Container, IHasAccentColour - { - public Color4 AccentColour { get; set; } - - private readonly BeatmapInfo beatmap; - private OsuColour palette; - - public DifficultyColouredContainer(BeatmapInfo beatmap) - { - this.beatmap = beatmap; - } - - [BackgroundDependencyLoader] - private void load(OsuColour palette) - { - if (palette == null) - throw new ArgumentNullException(nameof(palette)); - - this.palette = palette; - AccentColour = getColour(beatmap); - } - - private enum DifficultyRating - { - Easy, - Normal, - Hard, - Insane, - Expert, - ExpertPlus - } - - private DifficultyRating getDifficultyRating(BeatmapInfo beatmap) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - - var rating = beatmap.StarDifficulty; - - if (rating < 1.5) return DifficultyRating.Easy; - if (rating < 2.25) return DifficultyRating.Normal; - if (rating < 3.75) return DifficultyRating.Hard; - if (rating < 5.25) return DifficultyRating.Insane; - if (rating < 6.75) return DifficultyRating.Expert; - return DifficultyRating.ExpertPlus; - } - - private Color4 getColour(BeatmapInfo beatmap) - { - switch (getDifficultyRating(beatmap)) - { - case DifficultyRating.Easy: - return palette.Green; - default: - case DifficultyRating.Normal: - return palette.Blue; - case DifficultyRating.Hard: - return palette.Yellow; - case DifficultyRating.Insane: - return palette.Pink; - case DifficultyRating.Expert: - return palette.Purple; - case DifficultyRating.ExpertPlus: - return palette.Gray0; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using OpenTK.Graphics; + +namespace osu.Game.Beatmaps.Drawables +{ + public class DifficultyColouredContainer : Container, IHasAccentColour + { + public Color4 AccentColour { get; set; } + + private readonly BeatmapInfo beatmap; + private OsuColour palette; + + public DifficultyColouredContainer(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + } + + [BackgroundDependencyLoader] + private void load(OsuColour palette) + { + if (palette == null) + throw new ArgumentNullException(nameof(palette)); + + this.palette = palette; + AccentColour = getColour(beatmap); + } + + private enum DifficultyRating + { + Easy, + Normal, + Hard, + Insane, + Expert, + ExpertPlus + } + + private DifficultyRating getDifficultyRating(BeatmapInfo beatmap) + { + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap)); + + var rating = beatmap.StarDifficulty; + + if (rating < 1.5) return DifficultyRating.Easy; + if (rating < 2.25) return DifficultyRating.Normal; + if (rating < 3.75) return DifficultyRating.Hard; + if (rating < 5.25) return DifficultyRating.Insane; + if (rating < 6.75) return DifficultyRating.Expert; + return DifficultyRating.ExpertPlus; + } + + private Color4 getColour(BeatmapInfo beatmap) + { + switch (getDifficultyRating(beatmap)) + { + case DifficultyRating.Easy: + return palette.Green; + default: + case DifficultyRating.Normal: + return palette.Blue; + case DifficultyRating.Hard: + return palette.Yellow; + case DifficultyRating.Insane: + return palette.Pink; + case DifficultyRating.Expert: + return palette.Purple; + case DifficultyRating.ExpertPlus: + return palette.Gray0; + } + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index ace0541ea5..42c98aef24 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -1,48 +1,48 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Beatmaps.Drawables -{ - public class DifficultyIcon : DifficultyColouredContainer - { - private readonly BeatmapInfo beatmap; - - public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - - this.beatmap = beatmap; - Size = new Vector2(20); - } - - [BackgroundDependencyLoader] - private void load() - { - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = AccentColour, - Icon = FontAwesome.fa_circle - }, - new ConstrainedIconContainer - { - RelativeSizeAxes = Axes.Both, - // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = beatmap.Ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o } - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Beatmaps.Drawables +{ + public class DifficultyIcon : DifficultyColouredContainer + { + private readonly BeatmapInfo beatmap; + + public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap) + { + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap)); + + this.beatmap = beatmap; + Size = new Vector2(20); + } + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Colour = AccentColour, + Icon = FontAwesome.fa_circle + }, + new ConstrainedIconContainer + { + RelativeSizeAxes = Axes.Both, + // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) + Icon = beatmap.Ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o } + } + }; + } + } +} diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index f4a3dacd53..0424ff84f1 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -1,74 +1,74 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Audio.Track; -using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Beatmaps -{ - public class DummyWorkingBeatmap : WorkingBeatmap - { - private readonly OsuGameBase game; - - public DummyWorkingBeatmap(OsuGameBase game) - : base(new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Artist = "please load a beatmap!", - Title = "no beatmaps available!" - }, - BeatmapSet = new BeatmapSetInfo(), - BaseDifficulty = new BeatmapDifficulty - { - DrainRate = 0, - CircleSize = 0, - OverallDifficulty = 0, - ApproachRate = 0, - SliderMultiplier = 0, - SliderTickRate = 0, - }, - Ruleset = new DummyRulesetInfo() - }) - { - this.game = game; - } - - protected override Beatmap GetBeatmap() => new Beatmap(); - - protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4"); - - protected override Track GetTrack() => new TrackVirtual(); - - private class DummyRulesetInfo : RulesetInfo - { - public override Ruleset CreateInstance() => new DummyRuleset(this); - - private class DummyRuleset : Ruleset - { - public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; - - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) - { - throw new NotImplementedException(); - } - - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => null; - - public override string Description => "dummy"; - - public override string ShortName => "dummy"; - - public DummyRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Beatmaps +{ + public class DummyWorkingBeatmap : WorkingBeatmap + { + private readonly OsuGameBase game; + + public DummyWorkingBeatmap(OsuGameBase game) + : base(new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "please load a beatmap!", + Title = "no beatmaps available!" + }, + BeatmapSet = new BeatmapSetInfo(), + BaseDifficulty = new BeatmapDifficulty + { + DrainRate = 0, + CircleSize = 0, + OverallDifficulty = 0, + ApproachRate = 0, + SliderMultiplier = 0, + SliderTickRate = 0, + }, + Ruleset = new DummyRulesetInfo() + }) + { + this.game = game; + } + + protected override Beatmap GetBeatmap() => new Beatmap(); + + protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4"); + + protected override Track GetTrack() => new TrackVirtual(); + + private class DummyRulesetInfo : RulesetInfo + { + public override Ruleset CreateInstance() => new DummyRuleset(this); + + private class DummyRuleset : Ruleset + { + public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; + + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) + { + throw new NotImplementedException(); + } + + public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => null; + + public override string Description => "dummy"; + + public override string ShortName => "dummy"; + + public DummyRuleset(RulesetInfo rulesetInfo = null) + : base(rulesetInfo) + { + } + } + } + } +} diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index c07bedc8a6..bcd11f1fc8 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -1,80 +1,80 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace osu.Game.Beatmaps.Formats -{ - public abstract class Decoder : Decoder - where TOutput : new() - { - protected virtual TOutput CreateTemplateObject() => new TOutput(); - - public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams) - { - var output = CreateTemplateObject(); - foreach (StreamReader stream in otherStreams.Prepend(primaryStream)) - ParseStreamInto(stream, output); - return output; - } - - protected abstract void ParseStreamInto(StreamReader stream, TOutput beatmap); - } - - public abstract class Decoder - { - private static readonly Dictionary>> decoders = new Dictionary>>(); - - static Decoder() - { - LegacyBeatmapDecoder.Register(); - JsonBeatmapDecoder.Register(); - LegacyStoryboardDecoder.Register(); - } - - /// - /// Retrieves a to parse a . - /// - /// A stream pointing to the . - public static Decoder GetDecoder(StreamReader stream) - where T : new() - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - if (!decoders.TryGetValue(typeof(T), out var typedDecoders)) - throw new IOException(@"Unknown decoder type"); - - string line; - do - { - line = stream.ReadLine()?.Trim(); - } while (line != null && line.Length == 0); - - if (line == null) - throw new IOException(@"Unknown file format"); - - var decoder = typedDecoders.Select(d => line.StartsWith(d.Key) ? d.Value : null).FirstOrDefault(); - if (decoder == null) - throw new IOException(@"Unknown file format"); - - return (Decoder)decoder.Invoke(line); - } - - /// - /// Registers an instantiation function for a . - /// - /// A string in the file which triggers this decoder to be used. - /// A function which constructs the given . - protected static void AddDecoder(string magic, Func constructor) - { - if (!decoders.TryGetValue(typeof(T), out var typedDecoders)) - decoders.Add(typeof(T), typedDecoders = new Dictionary>()); - - typedDecoders[magic] = constructor; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace osu.Game.Beatmaps.Formats +{ + public abstract class Decoder : Decoder + where TOutput : new() + { + protected virtual TOutput CreateTemplateObject() => new TOutput(); + + public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams) + { + var output = CreateTemplateObject(); + foreach (StreamReader stream in otherStreams.Prepend(primaryStream)) + ParseStreamInto(stream, output); + return output; + } + + protected abstract void ParseStreamInto(StreamReader stream, TOutput beatmap); + } + + public abstract class Decoder + { + private static readonly Dictionary>> decoders = new Dictionary>>(); + + static Decoder() + { + LegacyBeatmapDecoder.Register(); + JsonBeatmapDecoder.Register(); + LegacyStoryboardDecoder.Register(); + } + + /// + /// Retrieves a to parse a . + /// + /// A stream pointing to the . + public static Decoder GetDecoder(StreamReader stream) + where T : new() + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (!decoders.TryGetValue(typeof(T), out var typedDecoders)) + throw new IOException(@"Unknown decoder type"); + + string line; + do + { + line = stream.ReadLine()?.Trim(); + } while (line != null && line.Length == 0); + + if (line == null) + throw new IOException(@"Unknown file format"); + + var decoder = typedDecoders.Select(d => line.StartsWith(d.Key) ? d.Value : null).FirstOrDefault(); + if (decoder == null) + throw new IOException(@"Unknown file format"); + + return (Decoder)decoder.Invoke(line); + } + + /// + /// Registers an instantiation function for a . + /// + /// A string in the file which triggers this decoder to be used. + /// A function which constructs the given . + protected static void AddDecoder(string magic, Func constructor) + { + if (!decoders.TryGetValue(typeof(T), out var typedDecoders)) + decoders.Add(typeof(T), typedDecoders = new Dictionary>()); + + typedDecoders[magic] = constructor; + } + } +} diff --git a/osu.Game/Beatmaps/Formats/IHasComboColours.cs b/osu.Game/Beatmaps/Formats/IHasComboColours.cs index 93c6c18eec..d3377c2717 100644 --- a/osu.Game/Beatmaps/Formats/IHasComboColours.cs +++ b/osu.Game/Beatmaps/Formats/IHasComboColours.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using OpenTK.Graphics; - -namespace osu.Game.Beatmaps.Formats -{ - public interface IHasComboColours - { - List ComboColours { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK.Graphics; + +namespace osu.Game.Beatmaps.Formats +{ + public interface IHasComboColours + { + List ComboColours { get; set; } + } +} diff --git a/osu.Game/Beatmaps/Formats/IHasCustomColours.cs b/osu.Game/Beatmaps/Formats/IHasCustomColours.cs index 14614a6728..32390ac484 100644 --- a/osu.Game/Beatmaps/Formats/IHasCustomColours.cs +++ b/osu.Game/Beatmaps/Formats/IHasCustomColours.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using OpenTK.Graphics; - -namespace osu.Game.Beatmaps.Formats -{ - public interface IHasCustomColours - { - Dictionary CustomColours { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK.Graphics; + +namespace osu.Game.Beatmaps.Formats +{ + public interface IHasCustomColours + { + Dictionary CustomColours { get; set; } + } +} diff --git a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs index add0f39280..e5574cd82e 100644 --- a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using osu.Game.IO.Serialization; - -namespace osu.Game.Beatmaps.Formats -{ - public class JsonBeatmapDecoder : Decoder - { - public static void Register() - { - AddDecoder("{", m => new JsonBeatmapDecoder()); - } - - protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) - { - stream.BaseStream.Position = 0; - stream.DiscardBufferedData(); - - stream.ReadToEnd().DeserializeInto(beatmap); - - foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using osu.Game.IO.Serialization; + +namespace osu.Game.Beatmaps.Formats +{ + public class JsonBeatmapDecoder : Decoder + { + public static void Register() + { + AddDecoder("{", m => new JsonBeatmapDecoder()); + } + + protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) + { + stream.BaseStream.Position = 0; + stream.DiscardBufferedData(); + + stream.ReadToEnd().DeserializeInto(beatmap); + + foreach (var hitObject in beatmap.HitObjects) + hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 52f1a01fcb..366b4f163f 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -1,389 +1,389 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Objects.Legacy; -using osu.Game.Beatmaps.ControlPoints; -using osu.Framework; - -namespace osu.Game.Beatmaps.Formats -{ - public class LegacyBeatmapDecoder : LegacyDecoder - { - public const int LATEST_VERSION = 14; - - private Beatmap beatmap; - - private ConvertHitObjectParser parser; - - private LegacySampleBank defaultSampleBank; - private int defaultSampleVolume = 100; - - public static void Register() - { - AddDecoder(@"osu file format v", m => new LegacyBeatmapDecoder(int.Parse(m.Split('v').Last()))); - } - - /// - /// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited. - /// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck. - /// - public static int UniversalOffset => RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? -22 : 0; - - /// - /// Whether or not beatmap or runtime offsets should be applied. Defaults on; only disable for testing purposes. - /// - public bool ApplyOffsets = true; - - private readonly int offset = UniversalOffset; - - public LegacyBeatmapDecoder(int version = LATEST_VERSION) : base(version) - { - // BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off) - offset += FormatVersion < 5 ? 24 : 0; - } - - protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) - { - this.beatmap = beatmap; - this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion; - - base.ParseStreamInto(stream, beatmap); - - // objects may be out of order *only* if a user has manually edited an .osu file. - // unfortunately there are ranked maps in this state (example: https://osu.ppy.sh/s/594828). - this.beatmap.HitObjects.Sort((x, y) => x.StartTime.CompareTo(y.StartTime)); - - foreach (var hitObject in this.beatmap.HitObjects) - hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); - } - - protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_"); - - protected override void ParseLine(Beatmap beatmap, Section section, string line) - { - switch (section) - { - case Section.General: - handleGeneral(line); - return; - case Section.Editor: - handleEditor(line); - return; - case Section.Metadata: - handleMetadata(line); - return; - case Section.Difficulty: - handleDifficulty(line); - return; - case Section.Events: - handleEvent(line); - return; - case Section.TimingPoints: - handleTimingPoint(line); - return; - case Section.HitObjects: - handleHitObject(line); - return; - } - - base.ParseLine(beatmap, section, line); - } - - private void handleGeneral(string line) - { - var pair = SplitKeyVal(line); - - var metadata = beatmap.BeatmapInfo.Metadata; - switch (pair.Key) - { - case @"AudioFilename": - metadata.AudioFile = pair.Value; - break; - case @"AudioLeadIn": - beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); - break; - case @"PreviewTime": - metadata.PreviewTime = getOffsetTime(int.Parse(pair.Value)); - break; - case @"Countdown": - beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; - break; - case @"SampleSet": - defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); - break; - case @"SampleVolume": - defaultSampleVolume = int.Parse(pair.Value); - break; - case @"StackLeniency": - beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"Mode": - beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); - - switch (beatmap.BeatmapInfo.RulesetID) - { - case 0: - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); - break; - case 1: - parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(); - break; - case 2: - parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(); - break; - case 3: - parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(); - break; - } - break; - case @"LetterboxInBreaks": - beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; - break; - case @"SpecialStyle": - beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; - break; - case @"WidescreenStoryboard": - beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; - break; - } - } - - private void handleEditor(string line) - { - var pair = SplitKeyVal(line); - - switch (pair.Key) - { - case @"Bookmarks": - beatmap.BeatmapInfo.StoredBookmarks = pair.Value; - break; - case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"BeatDivisor": - beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); - break; - case @"GridSize": - beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); - break; - case @"TimelineZoom": - beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - } - } - - private void handleMetadata(string line) - { - var pair = SplitKeyVal(line); - - var metadata = beatmap.BeatmapInfo.Metadata; - switch (pair.Key) - { - case @"Title": - metadata.Title = pair.Value; - break; - case @"TitleUnicode": - metadata.TitleUnicode = pair.Value; - break; - case @"Artist": - metadata.Artist = pair.Value; - break; - case @"ArtistUnicode": - metadata.ArtistUnicode = pair.Value; - break; - case @"Creator": - metadata.AuthorString = pair.Value; - break; - case @"Version": - beatmap.BeatmapInfo.Version = pair.Value; - break; - case @"Source": - beatmap.BeatmapInfo.Metadata.Source = pair.Value; - break; - case @"Tags": - beatmap.BeatmapInfo.Metadata.Tags = pair.Value; - break; - case @"BeatmapID": - beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); - break; - case @"BeatmapSetID": - beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); - metadata.OnlineBeatmapSetID = int.Parse(pair.Value); - break; - } - } - - private void handleDifficulty(string line) - { - var pair = SplitKeyVal(line); - - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; - switch (pair.Key) - { - case @"HPDrainRate": - difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"CircleSize": - difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"OverallDifficulty": - difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"ApproachRate": - difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"SliderMultiplier": - difficulty.SliderMultiplier = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"SliderTickRate": - difficulty.SliderTickRate = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - } - } - - private void handleEvent(string line) - { - string[] split = line.Split(','); - - EventType type; - if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); - - switch (type) - { - case EventType.Background: - string filename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; - break; - case EventType.Break: - var breakEvent = new BreakPeriod - { - StartTime = getOffsetTime(double.Parse(split[1], NumberFormatInfo.InvariantInfo)), - EndTime = getOffsetTime(double.Parse(split[2], NumberFormatInfo.InvariantInfo)) - }; - - if (!breakEvent.HasEffect) - return; - - beatmap.Breaks.Add(breakEvent); - break; - } - } - - private void handleTimingPoint(string line) - { - try - { - string[] split = line.Split(','); - - double time = getOffsetTime(double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo)); - double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); - double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; - - TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; - if (split.Length >= 3) - timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); - - LegacySampleBank sampleSet = defaultSampleBank; - if (split.Length >= 4) - sampleSet = (LegacySampleBank)int.Parse(split[3]); - - //SampleBank sampleBank = SampleBank.Default; - //if (split.Length >= 5) - // sampleBank = (SampleBank)int.Parse(split[4]); - - int sampleVolume = defaultSampleVolume; - if (split.Length >= 6) - sampleVolume = int.Parse(split[5]); - - bool timingChange = true; - if (split.Length >= 7) - timingChange = split[6][0] == '1'; - - bool kiaiMode = false; - bool omitFirstBarSignature = false; - if (split.Length >= 8) - { - int effectFlags = int.Parse(split[7]); - kiaiMode = (effectFlags & 1) > 0; - omitFirstBarSignature = (effectFlags & 8) > 0; - } - - string stringSampleSet = sampleSet.ToString().ToLower(); - if (stringSampleSet == @"none") - stringSampleSet = @"normal"; - - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); - SampleControlPoint samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); - - if (timingChange) - { - beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint - { - Time = time, - BeatLength = beatLength, - TimeSignature = timeSignature - }); - } - - if (speedMultiplier != difficultyPoint.SpeedMultiplier) - { - beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); - beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint - { - Time = time, - SpeedMultiplier = speedMultiplier - }); - } - - if (stringSampleSet != samplePoint.SampleBank || sampleVolume != samplePoint.SampleVolume) - { - beatmap.ControlPointInfo.SamplePoints.Add(new SampleControlPoint - { - Time = time, - SampleBank = stringSampleSet, - SampleVolume = sampleVolume - }); - } - - if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) - { - beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint - { - Time = time, - KiaiMode = kiaiMode, - OmitFirstBarLine = omitFirstBarSignature - }); - } - } - catch (FormatException e) - { - } - } - - private void handleHitObject(string line) - { - // If the ruleset wasn't specified, assume the osu!standard ruleset. - if (parser == null) - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); - - var obj = parser.Parse(line); - - if (obj != null) - { - obj.StartTime = getOffsetTime(obj.StartTime); - beatmap.HitObjects.Add(obj); - } - } - - private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0); - - private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects.Legacy; +using osu.Game.Beatmaps.ControlPoints; +using osu.Framework; + +namespace osu.Game.Beatmaps.Formats +{ + public class LegacyBeatmapDecoder : LegacyDecoder + { + public const int LATEST_VERSION = 14; + + private Beatmap beatmap; + + private ConvertHitObjectParser parser; + + private LegacySampleBank defaultSampleBank; + private int defaultSampleVolume = 100; + + public static void Register() + { + AddDecoder(@"osu file format v", m => new LegacyBeatmapDecoder(int.Parse(m.Split('v').Last()))); + } + + /// + /// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited. + /// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck. + /// + public static int UniversalOffset => RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? -22 : 0; + + /// + /// Whether or not beatmap or runtime offsets should be applied. Defaults on; only disable for testing purposes. + /// + public bool ApplyOffsets = true; + + private readonly int offset = UniversalOffset; + + public LegacyBeatmapDecoder(int version = LATEST_VERSION) : base(version) + { + // BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off) + offset += FormatVersion < 5 ? 24 : 0; + } + + protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) + { + this.beatmap = beatmap; + this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion; + + base.ParseStreamInto(stream, beatmap); + + // objects may be out of order *only* if a user has manually edited an .osu file. + // unfortunately there are ranked maps in this state (example: https://osu.ppy.sh/s/594828). + this.beatmap.HitObjects.Sort((x, y) => x.StartTime.CompareTo(y.StartTime)); + + foreach (var hitObject in this.beatmap.HitObjects) + hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); + } + + protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_"); + + protected override void ParseLine(Beatmap beatmap, Section section, string line) + { + switch (section) + { + case Section.General: + handleGeneral(line); + return; + case Section.Editor: + handleEditor(line); + return; + case Section.Metadata: + handleMetadata(line); + return; + case Section.Difficulty: + handleDifficulty(line); + return; + case Section.Events: + handleEvent(line); + return; + case Section.TimingPoints: + handleTimingPoint(line); + return; + case Section.HitObjects: + handleHitObject(line); + return; + } + + base.ParseLine(beatmap, section, line); + } + + private void handleGeneral(string line) + { + var pair = SplitKeyVal(line); + + var metadata = beatmap.BeatmapInfo.Metadata; + switch (pair.Key) + { + case @"AudioFilename": + metadata.AudioFile = pair.Value; + break; + case @"AudioLeadIn": + beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); + break; + case @"PreviewTime": + metadata.PreviewTime = getOffsetTime(int.Parse(pair.Value)); + break; + case @"Countdown": + beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; + break; + case @"SampleSet": + defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); + break; + case @"SampleVolume": + defaultSampleVolume = int.Parse(pair.Value); + break; + case @"StackLeniency": + beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"Mode": + beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + + switch (beatmap.BeatmapInfo.RulesetID) + { + case 0: + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + break; + case 1: + parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(); + break; + case 2: + parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(); + break; + case 3: + parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(); + break; + } + break; + case @"LetterboxInBreaks": + beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; + break; + case @"SpecialStyle": + beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; + break; + case @"WidescreenStoryboard": + beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; + break; + } + } + + private void handleEditor(string line) + { + var pair = SplitKeyVal(line); + + switch (pair.Key) + { + case @"Bookmarks": + beatmap.BeatmapInfo.StoredBookmarks = pair.Value; + break; + case @"DistanceSpacing": + beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"BeatDivisor": + beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); + break; + case @"GridSize": + beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); + break; + case @"TimelineZoom": + beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + } + } + + private void handleMetadata(string line) + { + var pair = SplitKeyVal(line); + + var metadata = beatmap.BeatmapInfo.Metadata; + switch (pair.Key) + { + case @"Title": + metadata.Title = pair.Value; + break; + case @"TitleUnicode": + metadata.TitleUnicode = pair.Value; + break; + case @"Artist": + metadata.Artist = pair.Value; + break; + case @"ArtistUnicode": + metadata.ArtistUnicode = pair.Value; + break; + case @"Creator": + metadata.AuthorString = pair.Value; + break; + case @"Version": + beatmap.BeatmapInfo.Version = pair.Value; + break; + case @"Source": + beatmap.BeatmapInfo.Metadata.Source = pair.Value; + break; + case @"Tags": + beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + break; + case @"BeatmapID": + beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + break; + case @"BeatmapSetID": + beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); + metadata.OnlineBeatmapSetID = int.Parse(pair.Value); + break; + } + } + + private void handleDifficulty(string line) + { + var pair = SplitKeyVal(line); + + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + switch (pair.Key) + { + case @"HPDrainRate": + difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"CircleSize": + difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"OverallDifficulty": + difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"ApproachRate": + difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"SliderMultiplier": + difficulty.SliderMultiplier = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"SliderTickRate": + difficulty.SliderTickRate = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + } + } + + private void handleEvent(string line) + { + string[] split = line.Split(','); + + EventType type; + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + switch (type) + { + case EventType.Background: + string filename = split[2].Trim('"'); + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + break; + case EventType.Break: + var breakEvent = new BreakPeriod + { + StartTime = getOffsetTime(double.Parse(split[1], NumberFormatInfo.InvariantInfo)), + EndTime = getOffsetTime(double.Parse(split[2], NumberFormatInfo.InvariantInfo)) + }; + + if (!breakEvent.HasEffect) + return; + + beatmap.Breaks.Add(breakEvent); + break; + } + } + + private void handleTimingPoint(string line) + { + try + { + string[] split = line.Split(','); + + double time = getOffsetTime(double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo)); + double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); + double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; + + TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; + if (split.Length >= 3) + timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); + + LegacySampleBank sampleSet = defaultSampleBank; + if (split.Length >= 4) + sampleSet = (LegacySampleBank)int.Parse(split[3]); + + //SampleBank sampleBank = SampleBank.Default; + //if (split.Length >= 5) + // sampleBank = (SampleBank)int.Parse(split[4]); + + int sampleVolume = defaultSampleVolume; + if (split.Length >= 6) + sampleVolume = int.Parse(split[5]); + + bool timingChange = true; + if (split.Length >= 7) + timingChange = split[6][0] == '1'; + + bool kiaiMode = false; + bool omitFirstBarSignature = false; + if (split.Length >= 8) + { + int effectFlags = int.Parse(split[7]); + kiaiMode = (effectFlags & 1) > 0; + omitFirstBarSignature = (effectFlags & 8) > 0; + } + + string stringSampleSet = sampleSet.ToString().ToLower(); + if (stringSampleSet == @"none") + stringSampleSet = @"normal"; + + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); + SampleControlPoint samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); + + if (timingChange) + { + beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + { + Time = time, + BeatLength = beatLength, + TimeSignature = timeSignature + }); + } + + if (speedMultiplier != difficultyPoint.SpeedMultiplier) + { + beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); + beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint + { + Time = time, + SpeedMultiplier = speedMultiplier + }); + } + + if (stringSampleSet != samplePoint.SampleBank || sampleVolume != samplePoint.SampleVolume) + { + beatmap.ControlPointInfo.SamplePoints.Add(new SampleControlPoint + { + Time = time, + SampleBank = stringSampleSet, + SampleVolume = sampleVolume + }); + } + + if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) + { + beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + { + Time = time, + KiaiMode = kiaiMode, + OmitFirstBarLine = omitFirstBarSignature + }); + } + } + catch (FormatException e) + { + } + } + + private void handleHitObject(string line) + { + // If the ruleset wasn't specified, assume the osu!standard ruleset. + if (parser == null) + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + + var obj = parser.Parse(line); + + if (obj != null) + { + obj.StartTime = getOffsetTime(obj.StartTime); + beatmap.HitObjects.Add(obj); + } + } + + private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0); + + private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0); + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 131c010c5c..06160a87e0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -1,164 +1,164 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using osu.Framework.Logging; -using OpenTK.Graphics; - -namespace osu.Game.Beatmaps.Formats -{ - public abstract class LegacyDecoder : Decoder - where T : new() - { - protected readonly int FormatVersion; - - protected LegacyDecoder(int version) - { - FormatVersion = version; - } - - protected override void ParseStreamInto(StreamReader stream, T beatmap) - { - Section section = Section.None; - - string line; - while ((line = stream.ReadLine()) != null) - { - if (ShouldSkipLine(line)) - continue; - - if (line.StartsWith(@"[") && line.EndsWith(@"]")) - { - if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - { - Logger.Log($"Unknown section \"{line}\" in {beatmap}"); - section = Section.None; - } - - continue; - } - - ParseLine(beatmap, section, line); - } - } - - protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//"); - - protected virtual void ParseLine(T output, Section section, string line) - { - switch (section) - { - case Section.Colours: - handleColours(output, line); - return; - } - } - - private bool hasComboColours; - - private void handleColours(T output, string line) - { - var pair = SplitKeyVal(line); - - bool isCombo = pair.Key.StartsWith(@"Combo"); - - string[] split = pair.Value.Split(','); - - if (split.Length != 3) - throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); - - if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b)) - throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); - - Color4 colour = new Color4(r, g, b, 255); - - if (isCombo) - { - if (!(output is IHasComboColours tHasComboColours)) return; - - if (!hasComboColours) - { - // remove default colours. - tHasComboColours.ComboColours.Clear(); - hasComboColours = true; - } - - tHasComboColours.ComboColours.Add(colour); - } - else - { - if (!(output is IHasCustomColours tHasCustomColours)) return; - tHasCustomColours.CustomColours[pair.Key] = colour; - } - } - - protected KeyValuePair SplitKeyVal(string line, char separator = ':') - { - var split = line.Trim().Split(new[] { separator }, 2); - - return new KeyValuePair - ( - split[0].Trim(), - split.Length > 1 ? split[1].Trim() : string.Empty - ); - } - - protected enum Section - { - None, - General, - Editor, - Metadata, - Difficulty, - Events, - TimingPoints, - Colours, - HitObjects, - Variables, - Fonts - } - - internal enum LegacySampleBank - { - None = 0, - Normal = 1, - Soft = 2, - Drum = 3 - } - - internal enum EventType - { - Background = 0, - Video = 1, - Break = 2, - Colour = 3, - Sprite = 4, - Sample = 5, - Animation = 6 - } - - internal enum LegacyOrigins - { - TopLeft, - Centre, - CentreLeft, - TopRight, - BottomCentre, - TopCentre, - Custom, - CentreRight, - BottomLeft, - BottomRight - } - - internal enum StoryLayer - { - Background = 0, - Fail = 1, - Pass = 2, - Foreground = 3 - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using osu.Framework.Logging; +using OpenTK.Graphics; + +namespace osu.Game.Beatmaps.Formats +{ + public abstract class LegacyDecoder : Decoder + where T : new() + { + protected readonly int FormatVersion; + + protected LegacyDecoder(int version) + { + FormatVersion = version; + } + + protected override void ParseStreamInto(StreamReader stream, T beatmap) + { + Section section = Section.None; + + string line; + while ((line = stream.ReadLine()) != null) + { + if (ShouldSkipLine(line)) + continue; + + if (line.StartsWith(@"[") && line.EndsWith(@"]")) + { + if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + { + Logger.Log($"Unknown section \"{line}\" in {beatmap}"); + section = Section.None; + } + + continue; + } + + ParseLine(beatmap, section, line); + } + } + + protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//"); + + protected virtual void ParseLine(T output, Section section, string line) + { + switch (section) + { + case Section.Colours: + handleColours(output, line); + return; + } + } + + private bool hasComboColours; + + private void handleColours(T output, string line) + { + var pair = SplitKeyVal(line); + + bool isCombo = pair.Key.StartsWith(@"Combo"); + + string[] split = pair.Value.Split(','); + + if (split.Length != 3) + throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); + + if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b)) + throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); + + Color4 colour = new Color4(r, g, b, 255); + + if (isCombo) + { + if (!(output is IHasComboColours tHasComboColours)) return; + + if (!hasComboColours) + { + // remove default colours. + tHasComboColours.ComboColours.Clear(); + hasComboColours = true; + } + + tHasComboColours.ComboColours.Add(colour); + } + else + { + if (!(output is IHasCustomColours tHasCustomColours)) return; + tHasCustomColours.CustomColours[pair.Key] = colour; + } + } + + protected KeyValuePair SplitKeyVal(string line, char separator = ':') + { + var split = line.Trim().Split(new[] { separator }, 2); + + return new KeyValuePair + ( + split[0].Trim(), + split.Length > 1 ? split[1].Trim() : string.Empty + ); + } + + protected enum Section + { + None, + General, + Editor, + Metadata, + Difficulty, + Events, + TimingPoints, + Colours, + HitObjects, + Variables, + Fonts + } + + internal enum LegacySampleBank + { + None = 0, + Normal = 1, + Soft = 2, + Drum = 3 + } + + internal enum EventType + { + Background = 0, + Video = 1, + Break = 2, + Colour = 3, + Sprite = 4, + Sample = 5, + Animation = 6 + } + + internal enum LegacyOrigins + { + TopLeft, + Centre, + CentreLeft, + TopRight, + BottomCentre, + TopCentre, + Custom, + CentreRight, + BottomLeft, + BottomRight + } + + internal enum StoryLayer + { + Background = 0, + Fail = 1, + Pass = 2, + Foreground = 3 + } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 85b0f8d42e..868ae5206a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -1,306 +1,306 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.IO.File; -using osu.Game.Storyboards; - -namespace osu.Game.Beatmaps.Formats -{ - public class LegacyStoryboardDecoder : LegacyDecoder - { - private StoryboardSprite storyboardSprite; - private CommandTimelineGroup timelineGroup; - - private Storyboard storyboard; - - private readonly Dictionary variables = new Dictionary(); - - public LegacyStoryboardDecoder() - : base(0) - { - } - - public static void Register() - { - // note that this isn't completely correct - AddDecoder(@"osu file format v", m => new LegacyStoryboardDecoder()); - AddDecoder(@"[Events]", m => new LegacyStoryboardDecoder()); - } - - protected override void ParseStreamInto(StreamReader stream, Storyboard storyboard) - { - this.storyboard = storyboard; - base.ParseStreamInto(stream, storyboard); - } - - protected override void ParseLine(Storyboard storyboard, Section section, string line) - { - switch (section) - { - case Section.Events: - handleEvents(line); - return; - case Section.Variables: - handleVariables(line); - return; - } - - base.ParseLine(storyboard, section, line); - } - - private void handleEvents(string line) - { - var depth = 0; - while (line.StartsWith(" ") || line.StartsWith("_")) - { - ++depth; - line = line.Substring(1); - } - - decodeVariables(ref line); - - string[] split = line.Split(','); - - if (depth == 0) - { - storyboardSprite = null; - - EventType type; - if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); - - switch (type) - { - case EventType.Sprite: - { - var layer = parseLayer(split[1]); - var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); - var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); - var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); - storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - storyboard.GetLayer(layer).Add(storyboardSprite); - } - break; - case EventType.Animation: - { - var layer = parseLayer(split[1]); - var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); - var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); - var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); - var frameCount = int.Parse(split[6]); - var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); - var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; - storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - storyboard.GetLayer(layer).Add(storyboardSprite); - } - break; - case EventType.Sample: - { - var time = double.Parse(split[1], CultureInfo.InvariantCulture); - var layer = parseLayer(split[2]); - var path = cleanFilename(split[3]); - var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); - } - break; - } - } - else - { - if (depth < 2) - timelineGroup = storyboardSprite?.TimelineGroup; - - var commandType = split[0]; - switch (commandType) - { - case "T": - { - var triggerName = split[1]; - var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue; - var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; - var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; - timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); - } - break; - case "L": - { - var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); - var loopCount = int.Parse(split[2]); - timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); - } - break; - default: - { - if (string.IsNullOrEmpty(split[3])) - split[3] = split[2]; - - var easing = (Easing)int.Parse(split[1]); - var startTime = double.Parse(split[2], CultureInfo.InvariantCulture); - var endTime = double.Parse(split[3], CultureInfo.InvariantCulture); - - switch (commandType) - { - case "F": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "S": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); - } - break; - case "V": - { - var startX = float.Parse(split[4], CultureInfo.InvariantCulture); - var startY = float.Parse(split[5], CultureInfo.InvariantCulture); - var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; - var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); - } - break; - case "R": - { - 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)); - } - break; - case "M": - { - var startX = float.Parse(split[4], CultureInfo.InvariantCulture); - var startY = float.Parse(split[5], CultureInfo.InvariantCulture); - var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; - var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); - timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); - } - break; - case "MX": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "MY": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "C": - { - var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); - var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture); - var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture); - var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed; - var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen; - var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue; - timelineGroup?.Colour.Add(easing, startTime, endTime, - new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), - new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); - } - break; - case "P": - { - var type = split[4]; - switch (type) - { - case "A": - timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); - break; - case "H": - timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); - break; - case "V": - timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); - break; - } - } - break; - default: - throw new InvalidDataException($@"Unknown command type: {commandType}"); - } - } - break; - } - } - } - - private string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); - - private Anchor parseOrigin(string value) - { - var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); - switch (origin) - { - case LegacyOrigins.TopLeft: - return Anchor.TopLeft; - case LegacyOrigins.TopCentre: - return Anchor.TopCentre; - case LegacyOrigins.TopRight: - return Anchor.TopRight; - case LegacyOrigins.CentreLeft: - return Anchor.CentreLeft; - case LegacyOrigins.Centre: - return Anchor.Centre; - case LegacyOrigins.CentreRight: - return Anchor.CentreRight; - case LegacyOrigins.BottomLeft: - return Anchor.BottomLeft; - case LegacyOrigins.BottomCentre: - return Anchor.BottomCentre; - case LegacyOrigins.BottomRight: - return Anchor.BottomRight; - } - - throw new InvalidDataException($@"Unknown origin: {value}"); - } - - private void handleVariables(string line) - { - var pair = SplitKeyVal(line, '='); - variables[pair.Key] = pair.Value; - } - - /// - /// Decodes any beatmap variables present in a line into their real values. - /// - /// The line which may contains variables. - private void decodeVariables(ref string line) - { - while (line.IndexOf('$') >= 0) - { - string origLine = line; - string[] split = line.Split(','); - for (int i = 0; i < split.Length; i++) - { - var item = split[i]; - if (item.StartsWith("$") && variables.ContainsKey(item)) - split[i] = variables[item]; - } - - line = string.Join(",", split); - if (line == origLine) - break; - } - } - - private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"'))); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.IO.File; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps.Formats +{ + public class LegacyStoryboardDecoder : LegacyDecoder + { + private StoryboardSprite storyboardSprite; + private CommandTimelineGroup timelineGroup; + + private Storyboard storyboard; + + private readonly Dictionary variables = new Dictionary(); + + public LegacyStoryboardDecoder() + : base(0) + { + } + + public static void Register() + { + // note that this isn't completely correct + AddDecoder(@"osu file format v", m => new LegacyStoryboardDecoder()); + AddDecoder(@"[Events]", m => new LegacyStoryboardDecoder()); + } + + protected override void ParseStreamInto(StreamReader stream, Storyboard storyboard) + { + this.storyboard = storyboard; + base.ParseStreamInto(stream, storyboard); + } + + protected override void ParseLine(Storyboard storyboard, Section section, string line) + { + switch (section) + { + case Section.Events: + handleEvents(line); + return; + case Section.Variables: + handleVariables(line); + return; + } + + base.ParseLine(storyboard, section, line); + } + + private void handleEvents(string line) + { + var depth = 0; + while (line.StartsWith(" ") || line.StartsWith("_")) + { + ++depth; + line = line.Substring(1); + } + + decodeVariables(ref line); + + string[] split = line.Split(','); + + if (depth == 0) + { + storyboardSprite = null; + + EventType type; + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + switch (type) + { + case EventType.Sprite: + { + var layer = parseLayer(split[1]); + var origin = parseOrigin(split[2]); + var path = cleanFilename(split[3]); + var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); + var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); + storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); + storyboard.GetLayer(layer).Add(storyboardSprite); + } + break; + case EventType.Animation: + { + var layer = parseLayer(split[1]); + var origin = parseOrigin(split[2]); + var path = cleanFilename(split[3]); + var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); + var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); + var frameCount = int.Parse(split[6]); + var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); + var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; + storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); + storyboard.GetLayer(layer).Add(storyboardSprite); + } + break; + case EventType.Sample: + { + var time = double.Parse(split[1], CultureInfo.InvariantCulture); + var layer = parseLayer(split[2]); + var path = cleanFilename(split[3]); + var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; + storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + } + break; + } + } + else + { + if (depth < 2) + timelineGroup = storyboardSprite?.TimelineGroup; + + var commandType = split[0]; + switch (commandType) + { + case "T": + { + var triggerName = split[1]; + var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue; + var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; + var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; + timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); + } + break; + case "L": + { + var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); + var loopCount = int.Parse(split[2]); + timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); + } + break; + default: + { + if (string.IsNullOrEmpty(split[3])) + split[3] = split[2]; + + var easing = (Easing)int.Parse(split[1]); + var startTime = double.Parse(split[2], CultureInfo.InvariantCulture); + var endTime = double.Parse(split[3], CultureInfo.InvariantCulture); + + switch (commandType) + { + case "F": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "S": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); + } + break; + case "V": + { + var startX = float.Parse(split[4], CultureInfo.InvariantCulture); + var startY = float.Parse(split[5], CultureInfo.InvariantCulture); + var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; + var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); + } + break; + case "R": + { + 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)); + } + break; + case "M": + { + var startX = float.Parse(split[4], CultureInfo.InvariantCulture); + var startY = float.Parse(split[5], CultureInfo.InvariantCulture); + var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; + var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; + timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); + timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); + } + break; + case "MX": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "MY": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "C": + { + var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); + var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture); + var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture); + var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed; + var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen; + var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue; + timelineGroup?.Colour.Add(easing, startTime, endTime, + new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), + new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); + } + break; + case "P": + { + var type = split[4]; + switch (type) + { + case "A": + timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); + break; + case "H": + timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); + break; + case "V": + timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); + break; + } + } + break; + default: + throw new InvalidDataException($@"Unknown command type: {commandType}"); + } + } + break; + } + } + } + + private string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); + + private Anchor parseOrigin(string value) + { + var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); + switch (origin) + { + case LegacyOrigins.TopLeft: + return Anchor.TopLeft; + case LegacyOrigins.TopCentre: + return Anchor.TopCentre; + case LegacyOrigins.TopRight: + return Anchor.TopRight; + case LegacyOrigins.CentreLeft: + return Anchor.CentreLeft; + case LegacyOrigins.Centre: + return Anchor.Centre; + case LegacyOrigins.CentreRight: + return Anchor.CentreRight; + case LegacyOrigins.BottomLeft: + return Anchor.BottomLeft; + case LegacyOrigins.BottomCentre: + return Anchor.BottomCentre; + case LegacyOrigins.BottomRight: + return Anchor.BottomRight; + } + + throw new InvalidDataException($@"Unknown origin: {value}"); + } + + private void handleVariables(string line) + { + var pair = SplitKeyVal(line, '='); + variables[pair.Key] = pair.Value; + } + + /// + /// Decodes any beatmap variables present in a line into their real values. + /// + /// The line which may contains variables. + private void decodeVariables(ref string line) + { + while (line.IndexOf('$') >= 0) + { + string origLine = line; + string[] split = line.Split(','); + for (int i = 0; i < split.Length; i++) + { + var item = split[i]; + if (item.StartsWith("$") && variables.ContainsKey(item)) + split[i] = variables[item]; + } + + line = string.Join(",", split); + if (line == origLine) + break; + } + } + + private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"'))); + } +} diff --git a/osu.Game/Beatmaps/IBeatmapConverter.cs b/osu.Game/Beatmaps/IBeatmapConverter.cs index 096ba345a1..6c25395a56 100644 --- a/osu.Game/Beatmaps/IBeatmapConverter.cs +++ b/osu.Game/Beatmaps/IBeatmapConverter.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Beatmaps -{ - public interface IBeatmapConverter - { - /// - /// Invoked when a has been converted. - /// The first argument contains the that was converted. - /// The second argument contains the s that were output from the conversion process. - /// - event Action> ObjectConverted; - - /// - /// Converts a Beatmap using this Beatmap Converter. - /// - /// The un-converted Beatmap. - void Convert(Beatmap beatmap); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Beatmaps +{ + public interface IBeatmapConverter + { + /// + /// Invoked when a has been converted. + /// The first argument contains the that was converted. + /// The second argument contains the s that were output from the conversion process. + /// + event Action> ObjectConverted; + + /// + /// Converts a Beatmap using this Beatmap Converter. + /// + /// The un-converted Beatmap. + void Convert(Beatmap beatmap); + } +} diff --git a/osu.Game/Beatmaps/Legacy/LegacyBeatmap.cs b/osu.Game/Beatmaps/Legacy/LegacyBeatmap.cs index 2a79c23e0b..eea82dac6d 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyBeatmap.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyBeatmap.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps.Legacy -{ - /// - /// A type of Beatmap loaded from a legacy .osu beatmap file (version <=15). - /// - public class LegacyBeatmap : Beatmap - { - /// - /// Constructs a new beatmap. - /// - /// The original beatmap to use the parameters of. - internal LegacyBeatmap(Beatmap original = null) - : base(original) - { - HitObjects = original?.HitObjects; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps.Legacy +{ + /// + /// A type of Beatmap loaded from a legacy .osu beatmap file (version <=15). + /// + public class LegacyBeatmap : Beatmap + { + /// + /// Constructs a new beatmap. + /// + /// The original beatmap to use the parameters of. + internal LegacyBeatmap(Beatmap original = null) + : base(original) + { + HitObjects = original?.HitObjects; + } + } +} diff --git a/osu.Game/Beatmaps/RankStatus.cs b/osu.Game/Beatmaps/RankStatus.cs index 5ca358ddef..dce4f494f1 100644 --- a/osu.Game/Beatmaps/RankStatus.cs +++ b/osu.Game/Beatmaps/RankStatus.cs @@ -1,23 +1,23 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Beatmaps -{ - public enum RankStatus - { - Any = 7, - [Description("Ranked & Approved")] - RankedApproved = 0, - Approved = 1, - Loved = 8, - Favourites = 2, - [Description("Mod Requests")] - ModRequests = 3, - Pending = 4, - Graveyard = 5, - [Description("My Maps")] - MyMaps = 6, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Beatmaps +{ + public enum RankStatus + { + Any = 7, + [Description("Ranked & Approved")] + RankedApproved = 0, + Approved = 1, + Loved = 8, + Favourites = 2, + [Description("Mod Requests")] + ModRequests = 3, + Pending = 4, + Graveyard = 5, + [Description("My Maps")] + MyMaps = 6, + } +} diff --git a/osu.Game/Beatmaps/Timing/BreakPeriod.cs b/osu.Game/Beatmaps/Timing/BreakPeriod.cs index 7345f0df82..0524704b8c 100644 --- a/osu.Game/Beatmaps/Timing/BreakPeriod.cs +++ b/osu.Game/Beatmaps/Timing/BreakPeriod.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps.Timing -{ - public class BreakPeriod - { - /// - /// The minimum duration required for a break to have any effect. - /// - public const double MIN_BREAK_DURATION = 650; - - /// - /// The break start time. - /// - public double StartTime; - - /// - /// The break end time. - /// - public double EndTime; - - /// - /// The break duration. - /// - public double Duration => EndTime - StartTime; - - /// - /// Whether the break has any effect. Breaks that are too short are culled before they are added to the beatmap. - /// - public bool HasEffect => Duration >= MIN_BREAK_DURATION; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps.Timing +{ + public class BreakPeriod + { + /// + /// The minimum duration required for a break to have any effect. + /// + public const double MIN_BREAK_DURATION = 650; + + /// + /// The break start time. + /// + public double StartTime; + + /// + /// The break end time. + /// + public double EndTime; + + /// + /// The break duration. + /// + public double Duration => EndTime - StartTime; + + /// + /// Whether the break has any effect. Breaks that are too short are culled before they are added to the beatmap. + /// + public bool HasEffect => Duration >= MIN_BREAK_DURATION; + } +} diff --git a/osu.Game/Beatmaps/Timing/TimeSignatures.cs b/osu.Game/Beatmaps/Timing/TimeSignatures.cs index 6b7d9e7e5a..aa25c76fc5 100644 --- a/osu.Game/Beatmaps/Timing/TimeSignatures.cs +++ b/osu.Game/Beatmaps/Timing/TimeSignatures.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps.Timing -{ - public enum TimeSignatures - { - SimpleQuadruple = 4, - SimpleTriple = 3 - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Beatmaps.Timing +{ + public enum TimeSignatures + { + SimpleQuadruple = 4, + SimpleTriple = 3 + } +} diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 5c0ad7685b..4080e34e81 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -1,216 +1,216 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Audio.Track; -using osu.Framework.Configuration; -using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Mods; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using osu.Game.Storyboards; -using osu.Framework.IO.File; -using System.IO; -using osu.Game.IO.Serialization; -using System.Diagnostics; -using osu.Game.Skinning; - -namespace osu.Game.Beatmaps -{ - public abstract class WorkingBeatmap : IDisposable - { - public readonly BeatmapInfo BeatmapInfo; - - public readonly BeatmapSetInfo BeatmapSetInfo; - - public readonly BeatmapMetadata Metadata; - - public readonly Bindable> Mods = new Bindable>(new Mod[] { }); - - protected WorkingBeatmap(BeatmapInfo beatmapInfo) - { - BeatmapInfo = beatmapInfo; - BeatmapSetInfo = beatmapInfo.BeatmapSet; - Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - - Mods.ValueChanged += mods => applyRateAdjustments(); - - beatmap = new AsyncLazy(populateBeatmap); - background = new AsyncLazy(populateBackground, b => b == null || !b.IsDisposed); - track = new AsyncLazy(populateTrack); - waveform = new AsyncLazy(populateWaveform); - storyboard = new AsyncLazy(populateStoryboard); - skin = new AsyncLazy(populateSkin); - } - - /// - /// Saves the . - /// - public void Save() - { - var path = FileSafety.GetTempPath(Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json"); - using (var sw = new StreamWriter(path)) - sw.WriteLine(Beatmap.Serialize()); - Process.Start(path); - } - - protected abstract Beatmap GetBeatmap(); - protected abstract Texture GetBackground(); - protected abstract Track GetTrack(); - protected virtual Skin GetSkin() => new DefaultSkin(); - protected virtual Waveform GetWaveform() => new Waveform(); - protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; - - public bool BeatmapLoaded => beatmap.IsResultAvailable; - public Beatmap Beatmap => beatmap.Value.Result; - public async Task GetBeatmapAsync() => await beatmap.Value; - - private readonly AsyncLazy beatmap; - - private Beatmap populateBeatmap() - { - var b = GetBeatmap() ?? new Beatmap(); - - // use the database-backed info. - b.BeatmapInfo = BeatmapInfo; - - return b; - } - - public bool BackgroundLoaded => background.IsResultAvailable; - public Texture Background => background.Value.Result; - public async Task GetBackgroundAsync() => await background.Value; - private AsyncLazy background; - - private Texture populateBackground() => GetBackground(); - - public bool TrackLoaded => track.IsResultAvailable; - public Track Track => track.Value.Result; - public async Task GetTrackAsync() => await track.Value; - private AsyncLazy track; - - private Track populateTrack() - { - // we want to ensure that we always have a track, even if it's a fake one. - var t = GetTrack() ?? new TrackVirtual(); - applyRateAdjustments(t); - return t; - } - - public bool WaveformLoaded => waveform.IsResultAvailable; - public Waveform Waveform => waveform.Value.Result; - public async Task GetWaveformAsync() => await waveform.Value; - private readonly AsyncLazy waveform; - - private Waveform populateWaveform() => GetWaveform(); - - public bool StoryboardLoaded => storyboard.IsResultAvailable; - public Storyboard Storyboard => storyboard.Value.Result; - public async Task GetStoryboardAsync() => await storyboard.Value; - private readonly AsyncLazy storyboard; - - private Storyboard populateStoryboard() => GetStoryboard(); - - public bool SkinLoaded => skin.IsResultAvailable; - public Skin Skin => skin.Value.Result; - public async Task GetSkinAsync() => await skin.Value; - private readonly AsyncLazy skin; - - private Skin populateSkin() => GetSkin(); - - public void TransferTo(WorkingBeatmap other) - { - if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) - other.track = track; - - if (background.IsResultAvailable && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) - other.background = background; - } - - public virtual void Dispose() - { - if (BackgroundLoaded) Background?.Dispose(); - if (WaveformLoaded) Waveform?.Dispose(); - if (StoryboardLoaded) Storyboard?.Dispose(); - if (SkinLoaded) Skin?.Dispose(); - } - - /// - /// Eagerly dispose of the audio track associated with this (if any). - /// Accessing track again will load a fresh instance. - /// - public void RecycleTrack() => track.Recycle(); - - private void applyRateAdjustments(Track t = null) - { - if (t == null && track.IsResultAvailable) t = Track; - if (t == null) return; - - t.ResetSpeedAdjustments(); - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToClock(t); - } - - public class AsyncLazy - { - private Lazy> lazy; - private readonly Func valueFactory; - private readonly Func stillValidFunction; - - private readonly object initLock = new object(); - - public AsyncLazy(Func valueFactory, Func stillValidFunction = null) - { - this.valueFactory = valueFactory; - this.stillValidFunction = stillValidFunction; - - recreate(); - } - - public void Recycle() - { - if (!IsResultAvailable) return; - - (lazy.Value.Result as IDisposable)?.Dispose(); - recreate(); - } - - public bool IsResultAvailable - { - get - { - recreateIfInvalid(); - return lazy.Value.IsCompleted; - } - } - - public Task Value - { - get - { - recreateIfInvalid(); - return lazy.Value; - } - } - - private void recreateIfInvalid() - { - lock (initLock) - { - if (!lazy.IsValueCreated || !lazy.Value.IsCompleted) - // we have not yet been initialised or haven't run the task. - return; - - if (stillValidFunction?.Invoke(lazy.Value.Result) ?? true) - // we are still in a valid state. - return; - - recreate(); - } - } - - private void recreate() => lazy = new Lazy>(() => Task.Run(valueFactory)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.Mods; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using osu.Game.Storyboards; +using osu.Framework.IO.File; +using System.IO; +using osu.Game.IO.Serialization; +using System.Diagnostics; +using osu.Game.Skinning; + +namespace osu.Game.Beatmaps +{ + public abstract class WorkingBeatmap : IDisposable + { + public readonly BeatmapInfo BeatmapInfo; + + public readonly BeatmapSetInfo BeatmapSetInfo; + + public readonly BeatmapMetadata Metadata; + + public readonly Bindable> Mods = new Bindable>(new Mod[] { }); + + protected WorkingBeatmap(BeatmapInfo beatmapInfo) + { + BeatmapInfo = beatmapInfo; + BeatmapSetInfo = beatmapInfo.BeatmapSet; + Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); + + Mods.ValueChanged += mods => applyRateAdjustments(); + + beatmap = new AsyncLazy(populateBeatmap); + background = new AsyncLazy(populateBackground, b => b == null || !b.IsDisposed); + track = new AsyncLazy(populateTrack); + waveform = new AsyncLazy(populateWaveform); + storyboard = new AsyncLazy(populateStoryboard); + skin = new AsyncLazy(populateSkin); + } + + /// + /// Saves the . + /// + public void Save() + { + var path = FileSafety.GetTempPath(Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json"); + using (var sw = new StreamWriter(path)) + sw.WriteLine(Beatmap.Serialize()); + Process.Start(path); + } + + protected abstract Beatmap GetBeatmap(); + protected abstract Texture GetBackground(); + protected abstract Track GetTrack(); + protected virtual Skin GetSkin() => new DefaultSkin(); + protected virtual Waveform GetWaveform() => new Waveform(); + protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; + + public bool BeatmapLoaded => beatmap.IsResultAvailable; + public Beatmap Beatmap => beatmap.Value.Result; + public async Task GetBeatmapAsync() => await beatmap.Value; + + private readonly AsyncLazy beatmap; + + private Beatmap populateBeatmap() + { + var b = GetBeatmap() ?? new Beatmap(); + + // use the database-backed info. + b.BeatmapInfo = BeatmapInfo; + + return b; + } + + public bool BackgroundLoaded => background.IsResultAvailable; + public Texture Background => background.Value.Result; + public async Task GetBackgroundAsync() => await background.Value; + private AsyncLazy background; + + private Texture populateBackground() => GetBackground(); + + public bool TrackLoaded => track.IsResultAvailable; + public Track Track => track.Value.Result; + public async Task GetTrackAsync() => await track.Value; + private AsyncLazy track; + + private Track populateTrack() + { + // we want to ensure that we always have a track, even if it's a fake one. + var t = GetTrack() ?? new TrackVirtual(); + applyRateAdjustments(t); + return t; + } + + public bool WaveformLoaded => waveform.IsResultAvailable; + public Waveform Waveform => waveform.Value.Result; + public async Task GetWaveformAsync() => await waveform.Value; + private readonly AsyncLazy waveform; + + private Waveform populateWaveform() => GetWaveform(); + + public bool StoryboardLoaded => storyboard.IsResultAvailable; + public Storyboard Storyboard => storyboard.Value.Result; + public async Task GetStoryboardAsync() => await storyboard.Value; + private readonly AsyncLazy storyboard; + + private Storyboard populateStoryboard() => GetStoryboard(); + + public bool SkinLoaded => skin.IsResultAvailable; + public Skin Skin => skin.Value.Result; + public async Task GetSkinAsync() => await skin.Value; + private readonly AsyncLazy skin; + + private Skin populateSkin() => GetSkin(); + + public void TransferTo(WorkingBeatmap other) + { + if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) + other.track = track; + + if (background.IsResultAvailable && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) + other.background = background; + } + + public virtual void Dispose() + { + if (BackgroundLoaded) Background?.Dispose(); + if (WaveformLoaded) Waveform?.Dispose(); + if (StoryboardLoaded) Storyboard?.Dispose(); + if (SkinLoaded) Skin?.Dispose(); + } + + /// + /// Eagerly dispose of the audio track associated with this (if any). + /// Accessing track again will load a fresh instance. + /// + public void RecycleTrack() => track.Recycle(); + + private void applyRateAdjustments(Track t = null) + { + if (t == null && track.IsResultAvailable) t = Track; + if (t == null) return; + + t.ResetSpeedAdjustments(); + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToClock(t); + } + + public class AsyncLazy + { + private Lazy> lazy; + private readonly Func valueFactory; + private readonly Func stillValidFunction; + + private readonly object initLock = new object(); + + public AsyncLazy(Func valueFactory, Func stillValidFunction = null) + { + this.valueFactory = valueFactory; + this.stillValidFunction = stillValidFunction; + + recreate(); + } + + public void Recycle() + { + if (!IsResultAvailable) return; + + (lazy.Value.Result as IDisposable)?.Dispose(); + recreate(); + } + + public bool IsResultAvailable + { + get + { + recreateIfInvalid(); + return lazy.Value.IsCompleted; + } + } + + public Task Value + { + get + { + recreateIfInvalid(); + return lazy.Value; + } + } + + private void recreateIfInvalid() + { + lock (initLock) + { + if (!lazy.IsValueCreated || !lazy.Value.IsCompleted) + // we have not yet been initialised or haven't run the task. + return; + + if (stillValidFunction?.Invoke(lazy.Value.Result) ?? true) + // we are still in a valid state. + return; + + recreate(); + } + } + + private void recreate() => lazy = new Lazy>(() => Task.Run(valueFactory)); + } + } +} diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index 1f7a84c6d3..0ef0589dff 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -1,71 +1,71 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Configuration; -using osu.Game.Rulesets; - -namespace osu.Game.Configuration -{ - public abstract class DatabasedConfigManager : ConfigManager - where T : struct - { - private readonly SettingsStore settings; - - private readonly int variant; - - private readonly List databasedSettings; - - private readonly RulesetInfo ruleset; - - protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int variant = 0) - { - this.settings = settings; - this.ruleset = ruleset; - this.variant = variant; - - databasedSettings = settings.Query(ruleset?.ID, variant); - - InitialiseDefaults(); - } - - protected override void PerformLoad() - { - } - - protected override bool PerformSave() - { - return true; - } - - protected override void AddBindable(T lookup, Bindable bindable) - { - base.AddBindable(lookup, bindable); - - var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup); - if (setting != null) - { - bindable.Parse(setting.Value); - } - else - { - settings.Update(setting = new DatabasedSetting - { - Key = lookup, - Value = bindable.Value, - RulesetID = ruleset?.ID, - Variant = variant, - }); - - databasedSettings.Add(setting); - } - - bindable.ValueChanged += v => - { - setting.Value = v; - settings.Update(setting); - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Configuration; +using osu.Game.Rulesets; + +namespace osu.Game.Configuration +{ + public abstract class DatabasedConfigManager : ConfigManager + where T : struct + { + private readonly SettingsStore settings; + + private readonly int variant; + + private readonly List databasedSettings; + + private readonly RulesetInfo ruleset; + + protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int variant = 0) + { + this.settings = settings; + this.ruleset = ruleset; + this.variant = variant; + + databasedSettings = settings.Query(ruleset?.ID, variant); + + InitialiseDefaults(); + } + + protected override void PerformLoad() + { + } + + protected override bool PerformSave() + { + return true; + } + + protected override void AddBindable(T lookup, Bindable bindable) + { + base.AddBindable(lookup, bindable); + + var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup); + if (setting != null) + { + bindable.Parse(setting.Value); + } + else + { + settings.Update(setting = new DatabasedSetting + { + Key = lookup, + Value = bindable.Value, + RulesetID = ruleset?.ID, + Variant = variant, + }); + + databasedSettings.Add(setting); + } + + bindable.ValueChanged += v => + { + setting.Value = v; + settings.Update(setting); + }; + } + } +} diff --git a/osu.Game/Configuration/DatabasedSetting.cs b/osu.Game/Configuration/DatabasedSetting.cs index 7c2f65c854..62990991b0 100644 --- a/osu.Game/Configuration/DatabasedSetting.cs +++ b/osu.Game/Configuration/DatabasedSetting.cs @@ -1,51 +1,51 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel.DataAnnotations.Schema; -using osu.Game.Database; - -namespace osu.Game.Configuration -{ - [Table("Settings")] - public class DatabasedSetting : IHasPrimaryKey - { - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int ID { get; set; } - - public int? RulesetID { get; set; } - - public int? Variant { get; set; } - - [Column("Key")] - public int IntKey - { - get => (int)Key; - private set => Key = value; - } - - [Column("Value")] - public string StringValue - { - get => Value.ToString(); - set => Value = value; - } - - public object Key; - public object Value; - - public DatabasedSetting(object key, object value) - { - Key = key; - Value = value; - } - - /// - /// Constructor for derived classes that may require serialisation. - /// - public DatabasedSetting() - { - } - - public override string ToString() => $"{Key}=>{Value}"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel.DataAnnotations.Schema; +using osu.Game.Database; + +namespace osu.Game.Configuration +{ + [Table("Settings")] + public class DatabasedSetting : IHasPrimaryKey + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ID { get; set; } + + public int? RulesetID { get; set; } + + public int? Variant { get; set; } + + [Column("Key")] + public int IntKey + { + get => (int)Key; + private set => Key = value; + } + + [Column("Value")] + public string StringValue + { + get => Value.ToString(); + set => Value = value; + } + + public object Key; + public object Value; + + public DatabasedSetting(object key, object value) + { + Key = key; + Value = value; + } + + /// + /// Constructor for derived classes that may require serialisation. + /// + public DatabasedSetting() + { + } + + public override string ToString() => $"{Key}=>{Value}"; + } +} diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 70260b349e..5ff3ddbe05 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -1,133 +1,133 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Framework.Platform; -using osu.Game.Overlays; -using osu.Game.Screens.Select; - -namespace osu.Game.Configuration -{ - public class OsuConfigManager : IniConfigManager - { - protected override void InitialiseDefaults() - { - // UI/selection defaults - Set(OsuSetting.Ruleset, 0, 0, int.MaxValue); - Set(OsuSetting.Skin, 0, 0, int.MaxValue); - - Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details); - - Set(OsuSetting.ShowConvertedBeatmaps, true); - Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); - Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1); - - Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); - - Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1); - - // Online settings - Set(OsuSetting.Username, string.Empty); - Set(OsuSetting.Token, string.Empty); - - Set(OsuSetting.SavePassword, false).ValueChanged += val => - { - if (val) Set(OsuSetting.SaveUsername, true); - }; - - Set(OsuSetting.SaveUsername, true).ValueChanged += val => - { - if (!val) Set(OsuSetting.SavePassword, false); - }; - - // Audio - Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); - - Set(OsuSetting.MenuVoice, true); - Set(OsuSetting.MenuMusic, true); - - Set(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1); - - // Input - Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2, 0.01); - Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2, 0.01); - Set(OsuSetting.AutoCursorSize, false); - - Set(OsuSetting.MouseDisableButtons, false); - Set(OsuSetting.MouseDisableWheel, false); - - // Graphics - Set(OsuSetting.ShowFpsDisplay, false); - - Set(OsuSetting.ShowStoryboard, true); - Set(OsuSetting.CursorRotation, true); - - Set(OsuSetting.MenuParallax, true); - - Set(OsuSetting.SnakingInSliders, true); - Set(OsuSetting.SnakingOutSliders, true); - - // Gameplay - Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01); - Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); - - Set(OsuSetting.ShowInterface, true); - Set(OsuSetting.KeyOverlay, false); - - Set(OsuSetting.FloatingComments, false); - - Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential); - - // Update - Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); - - Set(OsuSetting.Version, string.Empty); - - Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); - } - - public OsuConfigManager(Storage storage) : base(storage) - { - } - } - - public enum OsuSetting - { - Ruleset, - Token, - MenuCursorSize, - GameplayCursorSize, - AutoCursorSize, - DimLevel, - BlurLevel, - ShowStoryboard, - KeyOverlay, - FloatingComments, - ShowInterface, - MouseDisableButtons, - MouseDisableWheel, - AudioOffset, - VolumeInactive, - MenuMusic, - MenuVoice, - CursorRotation, - MenuParallax, - BeatmapDetailTab, - Username, - ReleaseStream, - SavePassword, - SaveUsername, - DisplayStarsMinimum, - DisplayStarsMaximum, - RandomSelectAlgorithm, - SnakingInSliders, - SnakingOutSliders, - ShowFpsDisplay, - ChatDisplayHeight, - Version, - ShowConvertedBeatmaps, - SpeedChangeVisualisation, - Skin, - ScreenshotFormat - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Platform; +using osu.Game.Overlays; +using osu.Game.Screens.Select; + +namespace osu.Game.Configuration +{ + public class OsuConfigManager : IniConfigManager + { + protected override void InitialiseDefaults() + { + // UI/selection defaults + Set(OsuSetting.Ruleset, 0, 0, int.MaxValue); + Set(OsuSetting.Skin, 0, 0, int.MaxValue); + + Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details); + + Set(OsuSetting.ShowConvertedBeatmaps, true); + Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); + Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1); + + Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); + + Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1); + + // Online settings + Set(OsuSetting.Username, string.Empty); + Set(OsuSetting.Token, string.Empty); + + Set(OsuSetting.SavePassword, false).ValueChanged += val => + { + if (val) Set(OsuSetting.SaveUsername, true); + }; + + Set(OsuSetting.SaveUsername, true).ValueChanged += val => + { + if (!val) Set(OsuSetting.SavePassword, false); + }; + + // Audio + Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); + + Set(OsuSetting.MenuVoice, true); + Set(OsuSetting.MenuMusic, true); + + Set(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1); + + // Input + Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2, 0.01); + Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2, 0.01); + Set(OsuSetting.AutoCursorSize, false); + + Set(OsuSetting.MouseDisableButtons, false); + Set(OsuSetting.MouseDisableWheel, false); + + // Graphics + Set(OsuSetting.ShowFpsDisplay, false); + + Set(OsuSetting.ShowStoryboard, true); + Set(OsuSetting.CursorRotation, true); + + Set(OsuSetting.MenuParallax, true); + + Set(OsuSetting.SnakingInSliders, true); + Set(OsuSetting.SnakingOutSliders, true); + + // Gameplay + Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01); + Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); + + Set(OsuSetting.ShowInterface, true); + Set(OsuSetting.KeyOverlay, false); + + Set(OsuSetting.FloatingComments, false); + + Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential); + + // Update + Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); + + Set(OsuSetting.Version, string.Empty); + + Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); + } + + public OsuConfigManager(Storage storage) : base(storage) + { + } + } + + public enum OsuSetting + { + Ruleset, + Token, + MenuCursorSize, + GameplayCursorSize, + AutoCursorSize, + DimLevel, + BlurLevel, + ShowStoryboard, + KeyOverlay, + FloatingComments, + ShowInterface, + MouseDisableButtons, + MouseDisableWheel, + AudioOffset, + VolumeInactive, + MenuMusic, + MenuVoice, + CursorRotation, + MenuParallax, + BeatmapDetailTab, + Username, + ReleaseStream, + SavePassword, + SaveUsername, + DisplayStarsMinimum, + DisplayStarsMaximum, + RandomSelectAlgorithm, + SnakingInSliders, + SnakingOutSliders, + ShowFpsDisplay, + ChatDisplayHeight, + Version, + ShowConvertedBeatmaps, + SpeedChangeVisualisation, + Skin, + ScreenshotFormat + } +} diff --git a/osu.Game/Configuration/RandomSelectAlgorithm.cs b/osu.Game/Configuration/RandomSelectAlgorithm.cs index 6f3d151b50..d994f59b34 100644 --- a/osu.Game/Configuration/RandomSelectAlgorithm.cs +++ b/osu.Game/Configuration/RandomSelectAlgorithm.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Configuration -{ - public enum RandomSelectAlgorithm - { - [Description("Never repeat")] - RandomPermutation, - [Description("Random")] - Random - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Configuration +{ + public enum RandomSelectAlgorithm + { + [Description("Never repeat")] + RandomPermutation, + [Description("Random")] + Random + } +} diff --git a/osu.Game/Configuration/RankingType.cs b/osu.Game/Configuration/RankingType.cs index 20483a9b73..32fe934100 100644 --- a/osu.Game/Configuration/RankingType.cs +++ b/osu.Game/Configuration/RankingType.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Configuration -{ - public enum RankingType - { - Local, - [Description("Global")] - Top, - [Description("Selected Mods")] - SelectedMod, - Friends, - Country - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Configuration +{ + public enum RankingType + { + Local, + [Description("Global")] + Top, + [Description("Selected Mods")] + SelectedMod, + Friends, + Country + } +} diff --git a/osu.Game/Configuration/ReleaseStream.cs b/osu.Game/Configuration/ReleaseStream.cs index 9deeb0bed8..ca13d0cac3 100644 --- a/osu.Game/Configuration/ReleaseStream.cs +++ b/osu.Game/Configuration/ReleaseStream.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Configuration -{ - public enum ReleaseStream - { - Lazer, - //Stable40, - //Beta40, - //Stable - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Configuration +{ + public enum ReleaseStream + { + Lazer, + //Stable40, + //Beta40, + //Stable + } +} diff --git a/osu.Game/Configuration/ScoreMeterType.cs b/osu.Game/Configuration/ScoreMeterType.cs index 6d4138c4c5..0333c3406f 100644 --- a/osu.Game/Configuration/ScoreMeterType.cs +++ b/osu.Game/Configuration/ScoreMeterType.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Configuration -{ - public enum ScoreMeterType - { - None, - Colour, - Error - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Configuration +{ + public enum ScoreMeterType + { + None, + Colour, + Error + } +} diff --git a/osu.Game/Configuration/ScreenshotFormat.cs b/osu.Game/Configuration/ScreenshotFormat.cs index b9309fae3a..43901281dc 100644 --- a/osu.Game/Configuration/ScreenshotFormat.cs +++ b/osu.Game/Configuration/ScreenshotFormat.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Configuration -{ - public enum ScreenshotFormat - { - [Description("JPG (web-friendly)")] - Jpg = 1, - [Description("PNG (lossless)")] - Png = 2 - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Configuration +{ + public enum ScreenshotFormat + { + [Description("JPG (web-friendly)")] + Jpg = 1, + [Description("PNG (lossless)")] + Png = 2 + } +} diff --git a/osu.Game/Configuration/SettingsStore.cs b/osu.Game/Configuration/SettingsStore.cs index 7b66002a79..7914e34147 100644 --- a/osu.Game/Configuration/SettingsStore.cs +++ b/osu.Game/Configuration/SettingsStore.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Database; - -namespace osu.Game.Configuration -{ - public class SettingsStore : DatabaseBackedStore - { - public event Action SettingChanged; - - public SettingsStore(DatabaseContextFactory contextFactory) - : base(contextFactory) - { - } - - /// - /// Retrieve s for a specified ruleset/variant content. - /// - /// The ruleset's internal ID. - /// An optional variant. - /// - public List Query(int? rulesetId = null, int? variant = null) => - ContextFactory.Get().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); - - public void Update(DatabasedSetting setting) - { - using (ContextFactory.GetForWrite()) - { - var newValue = setting.Value; - Refresh(ref setting); - setting.Value = newValue; - } - - SettingChanged?.Invoke(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Database; + +namespace osu.Game.Configuration +{ + public class SettingsStore : DatabaseBackedStore + { + public event Action SettingChanged; + + public SettingsStore(DatabaseContextFactory contextFactory) + : base(contextFactory) + { + } + + /// + /// Retrieve s for a specified ruleset/variant content. + /// + /// The ruleset's internal ID. + /// An optional variant. + /// + public List Query(int? rulesetId = null, int? variant = null) => + ContextFactory.Get().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); + + public void Update(DatabasedSetting setting) + { + using (ContextFactory.GetForWrite()) + { + var newValue = setting.Value; + Refresh(ref setting); + setting.Value = newValue; + } + + SettingChanged?.Invoke(); + } + } +} diff --git a/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs b/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs index 644ae0a727..d38b1a89c5 100644 --- a/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs +++ b/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Configuration -{ - public enum SpeedChangeVisualisationMethod - { - [Description("Sequential")] - Sequential, - [Description("Overlapping")] - Overlapping - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Configuration +{ + public enum SpeedChangeVisualisationMethod + { + [Description("Sequential")] + Sequential, + [Description("Overlapping")] + Overlapping + } +} diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 3afb22f0ad..e04559d547 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -1,348 +1,348 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Game.IO; -using osu.Game.IO.Archives; -using osu.Game.IPC; -using osu.Game.Overlays.Notifications; -using osu.Game.Utils; -using SharpCompress.Common; -using FileInfo = osu.Game.IO.FileInfo; - -namespace osu.Game.Database -{ - /// - /// Encapsulates a model store class to give it import functionality. - /// Adds cross-functionality with to give access to the central file store for the provided model. - /// - /// The model type. - /// The associated file join type. - public abstract class ArchiveModelManager : ICanAcceptFiles - where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete - where TFileModel : INamedFileInfo, new() - { - /// - /// Set an endpoint for notifications to be posted to. - /// - public Action PostNotification { protected get; set; } - - /// - /// Fired when a new becomes available in the database. - /// This is not guaranteed to run on the update thread. - /// - public event Action ItemAdded; - - /// - /// Fired when a is removed from the database. - /// This is not guaranteed to run on the update thread. - /// - public event Action ItemRemoved; - - public virtual string[] HandledExtensions => new[] { ".zip" }; - - protected readonly FileStore Files; - - protected readonly IDatabaseContextFactory ContextFactory; - - protected readonly MutableDatabaseBackedStore ModelStore; - - // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) - private ArchiveImportIPCChannel ipc; - - protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStore modelStore, IIpcHost importHost = null) - { - ContextFactory = contextFactory; - - ModelStore = modelStore; - ModelStore.ItemAdded += s => ItemAdded?.Invoke(s); - ModelStore.ItemRemoved += s => ItemRemoved?.Invoke(s); - - Files = new FileStore(contextFactory, storage); - - if (importHost != null) - ipc = new ArchiveImportIPCChannel(importHost, this); - - ModelStore.Cleanup(); - } - - /// - /// Import one or more items from filesystem . - /// This will post notifications tracking progress. - /// - /// One or more archive locations on disk. - public void Import(params string[] paths) - { - var notification = new ProgressNotification - { - Text = "Import is initialising...", - Progress = 0, - State = ProgressNotificationState.Active, - }; - - PostNotification?.Invoke(notification); - - List imported = new List(); - - int current = 0; - int errors = 0; - foreach (string path in paths) - { - if (notification.State == ProgressNotificationState.Cancelled) - // user requested abort - return; - - try - { - notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}"; - using (ArchiveReader reader = getReaderFrom(path)) - imported.Add(Import(reader)); - - notification.Progress = (float)current / paths.Length; - - // We may or may not want to delete the file depending on where it is stored. - // e.g. reconstructing/repairing database with items from default storage. - // Also, not always a single file, i.e. for LegacyFilesystemReader - // TODO: Add a check to prevent files from storage to be deleted. - try - { - if (File.Exists(path)) - File.Delete(path); - } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})"); - } - } - catch (Exception e) - { - e = e.InnerException ?? e; - Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); - errors++; - } - } - - notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; - notification.State = ProgressNotificationState.Completed; - } - - /// - /// Import an item from an . - /// - /// The archive to be imported. - public TModel Import(ArchiveReader archive) - { - using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. - { - // create a new model (don't yet add to database) - var item = CreateModel(archive); - - var existing = CheckForExisting(item); - - if (existing != null) return existing; - - item.Files = createFileInfos(archive, Files); - - Populate(item, archive); - - // import to store - ModelStore.Add(item); - - return item; - } - } - - /// - /// Import an item from a . - /// - /// The model to be imported. - public void Import(TModel item) => ModelStore.Add(item); - - /// - /// Perform an update of the specified item. - /// TODO: Support file changes. - /// - /// The item to update. - public void Update(TModel item) => ModelStore.Update(item); - - /// - /// Delete an item from the manager. - /// Is a no-op for already deleted items. - /// - /// The item to delete. - public void Delete(TModel item) - { - using (var usage = ContextFactory.GetForWrite()) - { - var context = usage.Context; - - context.ChangeTracker.AutoDetectChangesEnabled = false; - - // re-fetch the model on the import context. - var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == item.ID); - - if (foundModel.DeletePending) return; - - if (ModelStore.Delete(foundModel)) - Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray()); - - context.ChangeTracker.AutoDetectChangesEnabled = true; - } - } - - /// - /// Delete multiple items. - /// This will post notifications tracking progress. - /// - public void Delete(List items) - { - if (items.Count == 0) return; - - var notification = new ProgressNotification - { - Progress = 0, - CompletionText = "Deleted all beatmaps!", - State = ProgressNotificationState.Active, - }; - - PostNotification?.Invoke(notification); - - int i = 0; - - using (ContextFactory.GetForWrite()) - { - foreach (var b in items) - { - if (notification.State == ProgressNotificationState.Cancelled) - // user requested abort - return; - - notification.Text = $"Deleting ({++i} of {items.Count})"; - - Delete(b); - - notification.Progress = (float)i / items.Count; - } - } - - notification.State = ProgressNotificationState.Completed; - } - - /// - /// Restore multiple items that were previously deleted. - /// This will post notifications tracking progress. - /// - public void Undelete(List items) - { - if (!items.Any()) return; - - var notification = new ProgressNotification - { - CompletionText = "Restored all deleted items!", - Progress = 0, - State = ProgressNotificationState.Active, - }; - - PostNotification?.Invoke(notification); - - int i = 0; - - using (ContextFactory.GetForWrite()) - { - foreach (var item in items) - { - if (notification.State == ProgressNotificationState.Cancelled) - // user requested abort - return; - - notification.Text = $"Restoring ({++i} of {items.Count})"; - - Undelete(item); - - notification.Progress = (float)i / items.Count; - } - } - - notification.State = ProgressNotificationState.Completed; - } - - /// - /// Restore an item that was previously deleted. Is a no-op if the item is not in a deleted state, or has its protected flag set. - /// - /// The item to restore - public void Undelete(TModel item) - { - using (var usage = ContextFactory.GetForWrite()) - { - usage.Context.ChangeTracker.AutoDetectChangesEnabled = false; - - if (!ModelStore.Undelete(item)) return; - - Files.Reference(item.Files.Select(f => f.FileInfo).ToArray()); - - usage.Context.ChangeTracker.AutoDetectChangesEnabled = true; - } - } - - /// - /// Create all required s for the provided archive, adding them to the global file store. - /// - private List createFileInfos(ArchiveReader reader, FileStore files) - { - var fileInfos = new List(); - - // import files to manager - foreach (string file in reader.Filenames) - using (Stream s = reader.GetStream(file)) - fileInfos.Add(new TFileModel - { - Filename = file, - FileInfo = files.Add(s) - }); - - return fileInfos; - } - - /// - /// Create a barebones model from the provided archive. - /// Actual expensive population should be done in ; this should just prepare for duplicate checking. - /// - /// The archive to create the model for. - /// A model populated with minimal information. - protected abstract TModel CreateModel(ArchiveReader archive); - - /// - /// Populate the provided model completely from the given archive. - /// After this method, the model should be in a state ready to commit to a store. - /// - /// The model to populate. - /// The archive to use as a reference for population. - protected virtual void Populate(TModel model, ArchiveReader archive) - { - } - - protected virtual TModel CheckForExisting(TModel model) => null; - - private DbSet queryModel() => ContextFactory.Get().Set(); - - /// - /// Creates an from a valid storage path. - /// - /// A file or folder path resolving the archive content. - /// A reader giving access to the archive's content. - private ArchiveReader getReaderFrom(string path) - { - if (ZipUtils.IsZipArchive(path)) - return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path)); - if (Directory.Exists(path)) - return new LegacyFilesystemReader(path); - throw new InvalidFormatException($"{path} is not a valid archive"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.IO; +using osu.Game.IO.Archives; +using osu.Game.IPC; +using osu.Game.Overlays.Notifications; +using osu.Game.Utils; +using SharpCompress.Common; +using FileInfo = osu.Game.IO.FileInfo; + +namespace osu.Game.Database +{ + /// + /// Encapsulates a model store class to give it import functionality. + /// Adds cross-functionality with to give access to the central file store for the provided model. + /// + /// The model type. + /// The associated file join type. + public abstract class ArchiveModelManager : ICanAcceptFiles + where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete + where TFileModel : INamedFileInfo, new() + { + /// + /// Set an endpoint for notifications to be posted to. + /// + public Action PostNotification { protected get; set; } + + /// + /// Fired when a new becomes available in the database. + /// This is not guaranteed to run on the update thread. + /// + public event Action ItemAdded; + + /// + /// Fired when a is removed from the database. + /// This is not guaranteed to run on the update thread. + /// + public event Action ItemRemoved; + + public virtual string[] HandledExtensions => new[] { ".zip" }; + + protected readonly FileStore Files; + + protected readonly IDatabaseContextFactory ContextFactory; + + protected readonly MutableDatabaseBackedStore ModelStore; + + // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) + private ArchiveImportIPCChannel ipc; + + protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStore modelStore, IIpcHost importHost = null) + { + ContextFactory = contextFactory; + + ModelStore = modelStore; + ModelStore.ItemAdded += s => ItemAdded?.Invoke(s); + ModelStore.ItemRemoved += s => ItemRemoved?.Invoke(s); + + Files = new FileStore(contextFactory, storage); + + if (importHost != null) + ipc = new ArchiveImportIPCChannel(importHost, this); + + ModelStore.Cleanup(); + } + + /// + /// Import one or more items from filesystem . + /// This will post notifications tracking progress. + /// + /// One or more archive locations on disk. + public void Import(params string[] paths) + { + var notification = new ProgressNotification + { + Text = "Import is initialising...", + Progress = 0, + State = ProgressNotificationState.Active, + }; + + PostNotification?.Invoke(notification); + + List imported = new List(); + + int current = 0; + int errors = 0; + foreach (string path in paths) + { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + + try + { + notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}"; + using (ArchiveReader reader = getReaderFrom(path)) + imported.Add(Import(reader)); + + notification.Progress = (float)current / paths.Length; + + // We may or may not want to delete the file depending on where it is stored. + // e.g. reconstructing/repairing database with items from default storage. + // Also, not always a single file, i.e. for LegacyFilesystemReader + // TODO: Add a check to prevent files from storage to be deleted. + try + { + if (File.Exists(path)) + File.Delete(path); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})"); + } + } + catch (Exception e) + { + e = e.InnerException ?? e; + Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); + errors++; + } + } + + notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; + notification.State = ProgressNotificationState.Completed; + } + + /// + /// Import an item from an . + /// + /// The archive to be imported. + public TModel Import(ArchiveReader archive) + { + using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. + { + // create a new model (don't yet add to database) + var item = CreateModel(archive); + + var existing = CheckForExisting(item); + + if (existing != null) return existing; + + item.Files = createFileInfos(archive, Files); + + Populate(item, archive); + + // import to store + ModelStore.Add(item); + + return item; + } + } + + /// + /// Import an item from a . + /// + /// The model to be imported. + public void Import(TModel item) => ModelStore.Add(item); + + /// + /// Perform an update of the specified item. + /// TODO: Support file changes. + /// + /// The item to update. + public void Update(TModel item) => ModelStore.Update(item); + + /// + /// Delete an item from the manager. + /// Is a no-op for already deleted items. + /// + /// The item to delete. + public void Delete(TModel item) + { + using (var usage = ContextFactory.GetForWrite()) + { + var context = usage.Context; + + context.ChangeTracker.AutoDetectChangesEnabled = false; + + // re-fetch the model on the import context. + var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == item.ID); + + if (foundModel.DeletePending) return; + + if (ModelStore.Delete(foundModel)) + Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray()); + + context.ChangeTracker.AutoDetectChangesEnabled = true; + } + } + + /// + /// Delete multiple items. + /// This will post notifications tracking progress. + /// + public void Delete(List items) + { + if (items.Count == 0) return; + + var notification = new ProgressNotification + { + Progress = 0, + CompletionText = "Deleted all beatmaps!", + State = ProgressNotificationState.Active, + }; + + PostNotification?.Invoke(notification); + + int i = 0; + + using (ContextFactory.GetForWrite()) + { + foreach (var b in items) + { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + + notification.Text = $"Deleting ({++i} of {items.Count})"; + + Delete(b); + + notification.Progress = (float)i / items.Count; + } + } + + notification.State = ProgressNotificationState.Completed; + } + + /// + /// Restore multiple items that were previously deleted. + /// This will post notifications tracking progress. + /// + public void Undelete(List items) + { + if (!items.Any()) return; + + var notification = new ProgressNotification + { + CompletionText = "Restored all deleted items!", + Progress = 0, + State = ProgressNotificationState.Active, + }; + + PostNotification?.Invoke(notification); + + int i = 0; + + using (ContextFactory.GetForWrite()) + { + foreach (var item in items) + { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + + notification.Text = $"Restoring ({++i} of {items.Count})"; + + Undelete(item); + + notification.Progress = (float)i / items.Count; + } + } + + notification.State = ProgressNotificationState.Completed; + } + + /// + /// Restore an item that was previously deleted. Is a no-op if the item is not in a deleted state, or has its protected flag set. + /// + /// The item to restore + public void Undelete(TModel item) + { + using (var usage = ContextFactory.GetForWrite()) + { + usage.Context.ChangeTracker.AutoDetectChangesEnabled = false; + + if (!ModelStore.Undelete(item)) return; + + Files.Reference(item.Files.Select(f => f.FileInfo).ToArray()); + + usage.Context.ChangeTracker.AutoDetectChangesEnabled = true; + } + } + + /// + /// Create all required s for the provided archive, adding them to the global file store. + /// + private List createFileInfos(ArchiveReader reader, FileStore files) + { + var fileInfos = new List(); + + // import files to manager + foreach (string file in reader.Filenames) + using (Stream s = reader.GetStream(file)) + fileInfos.Add(new TFileModel + { + Filename = file, + FileInfo = files.Add(s) + }); + + return fileInfos; + } + + /// + /// Create a barebones model from the provided archive. + /// Actual expensive population should be done in ; this should just prepare for duplicate checking. + /// + /// The archive to create the model for. + /// A model populated with minimal information. + protected abstract TModel CreateModel(ArchiveReader archive); + + /// + /// Populate the provided model completely from the given archive. + /// After this method, the model should be in a state ready to commit to a store. + /// + /// The model to populate. + /// The archive to use as a reference for population. + protected virtual void Populate(TModel model, ArchiveReader archive) + { + } + + protected virtual TModel CheckForExisting(TModel model) => null; + + private DbSet queryModel() => ContextFactory.Get().Set(); + + /// + /// Creates an from a valid storage path. + /// + /// A file or folder path resolving the archive content. + /// A reader giving access to the archive's content. + private ArchiveReader getReaderFrom(string path) + { + if (ZipUtils.IsZipArchive(path)) + return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path)); + if (Directory.Exists(path)) + return new LegacyFilesystemReader(path); + throw new InvalidFormatException($"{path} is not a valid archive"); + } + } +} diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 4b582bdfea..c6fb51c056 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -1,52 +1,52 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using Microsoft.EntityFrameworkCore; -using osu.Framework.Platform; - -namespace osu.Game.Database -{ - public abstract class DatabaseBackedStore - { - protected readonly Storage Storage; - - protected readonly IDatabaseContextFactory ContextFactory; - - /// - /// Refresh an instance potentially from a different thread with a local context-tracked instance. - /// - /// The object to use as a reference when negotiating a local instance. - /// An optional lookup source which will be used to query and populate a freshly retrieved replacement. If not provided, the refreshed object will still be returned but will not have any includes. - /// A valid EF-stored type. - protected virtual void Refresh(ref T obj, IQueryable lookupSource = null) where T : class, IHasPrimaryKey - { - using (var usage = ContextFactory.GetForWrite()) - { - var context = usage.Context; - - if (context.Entry(obj).State != EntityState.Detached) return; - - var id = obj.ID; - var foundObject = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find(id); - if (foundObject != null) - obj = foundObject; - else - context.Add(obj); - } - } - - protected DatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null) - { - ContextFactory = contextFactory; - Storage = storage; - } - - /// - /// Perform any common clean-up tasks. Should be run when idle, or whenever necessary. - /// - public virtual void Cleanup() - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using Microsoft.EntityFrameworkCore; +using osu.Framework.Platform; + +namespace osu.Game.Database +{ + public abstract class DatabaseBackedStore + { + protected readonly Storage Storage; + + protected readonly IDatabaseContextFactory ContextFactory; + + /// + /// Refresh an instance potentially from a different thread with a local context-tracked instance. + /// + /// The object to use as a reference when negotiating a local instance. + /// An optional lookup source which will be used to query and populate a freshly retrieved replacement. If not provided, the refreshed object will still be returned but will not have any includes. + /// A valid EF-stored type. + protected virtual void Refresh(ref T obj, IQueryable lookupSource = null) where T : class, IHasPrimaryKey + { + using (var usage = ContextFactory.GetForWrite()) + { + var context = usage.Context; + + if (context.Entry(obj).State != EntityState.Detached) return; + + var id = obj.ID; + var foundObject = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find(id); + if (foundObject != null) + obj = foundObject; + else + context.Add(obj); + } + } + + protected DatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null) + { + ContextFactory = contextFactory; + Storage = storage; + } + + /// + /// Perform any common clean-up tasks. Should be run when idle, or whenever necessary. + /// + public virtual void Cleanup() + { + } + } +} diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index 06737e61eb..71960303b5 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -1,94 +1,94 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Threading; -using osu.Framework.Platform; - -namespace osu.Game.Database -{ - public class DatabaseContextFactory : IDatabaseContextFactory - { - private readonly GameHost host; - - private const string database_name = @"client"; - - private ThreadLocal threadContexts; - - private readonly object writeLock = new object(); - - private bool currentWriteDidWrite; - private int currentWriteUsages; - - public DatabaseContextFactory(GameHost host) - { - this.host = host; - recycleThreadContexts(); - } - - /// - /// Get a context for the current thread for read-only usage. - /// If a is in progress, the existing write-safe context will be returned. - /// - public OsuDbContext Get() => threadContexts.Value; - - /// - /// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context). - /// This method may block if a write is already active on a different thread. - /// - /// A usage containing a usable context. - public DatabaseWriteUsage GetForWrite() - { - Monitor.Enter(writeLock); - - Interlocked.Increment(ref currentWriteUsages); - - return new DatabaseWriteUsage(threadContexts.Value, usageCompleted); - } - - private void usageCompleted(DatabaseWriteUsage usage) - { - int usages = Interlocked.Decrement(ref currentWriteUsages); - - try - { - currentWriteDidWrite |= usage.PerformedWrite; - - if (usages > 0) return; - - if (currentWriteDidWrite) - { - // explicitly dispose to ensure any outstanding flushes happen as soon as possible (and underlying resources are purged). - usage.Context.Dispose(); - - currentWriteDidWrite = false; - - // once all writes are complete, we want to refresh thread-specific contexts to make sure they don't have stale local caches. - recycleThreadContexts(); - } - } - finally - { - Monitor.Exit(writeLock); - } - } - - private void recycleThreadContexts() => threadContexts = new ThreadLocal(CreateContext); - - protected virtual OsuDbContext CreateContext() - { - var ctx = new OsuDbContext(host.Storage.GetDatabaseConnectionString(database_name)); - ctx.Database.AutoTransactionsEnabled = false; - - return ctx; - } - - public void ResetDatabase() - { - lock (writeLock) - { - recycleThreadContexts(); - host.Storage.DeleteDatabase(database_name); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Threading; +using osu.Framework.Platform; + +namespace osu.Game.Database +{ + public class DatabaseContextFactory : IDatabaseContextFactory + { + private readonly GameHost host; + + private const string database_name = @"client"; + + private ThreadLocal threadContexts; + + private readonly object writeLock = new object(); + + private bool currentWriteDidWrite; + private int currentWriteUsages; + + public DatabaseContextFactory(GameHost host) + { + this.host = host; + recycleThreadContexts(); + } + + /// + /// Get a context for the current thread for read-only usage. + /// If a is in progress, the existing write-safe context will be returned. + /// + public OsuDbContext Get() => threadContexts.Value; + + /// + /// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context). + /// This method may block if a write is already active on a different thread. + /// + /// A usage containing a usable context. + public DatabaseWriteUsage GetForWrite() + { + Monitor.Enter(writeLock); + + Interlocked.Increment(ref currentWriteUsages); + + return new DatabaseWriteUsage(threadContexts.Value, usageCompleted); + } + + private void usageCompleted(DatabaseWriteUsage usage) + { + int usages = Interlocked.Decrement(ref currentWriteUsages); + + try + { + currentWriteDidWrite |= usage.PerformedWrite; + + if (usages > 0) return; + + if (currentWriteDidWrite) + { + // explicitly dispose to ensure any outstanding flushes happen as soon as possible (and underlying resources are purged). + usage.Context.Dispose(); + + currentWriteDidWrite = false; + + // once all writes are complete, we want to refresh thread-specific contexts to make sure they don't have stale local caches. + recycleThreadContexts(); + } + } + finally + { + Monitor.Exit(writeLock); + } + } + + private void recycleThreadContexts() => threadContexts = new ThreadLocal(CreateContext); + + protected virtual OsuDbContext CreateContext() + { + var ctx = new OsuDbContext(host.Storage.GetDatabaseConnectionString(database_name)); + ctx.Database.AutoTransactionsEnabled = false; + + return ctx; + } + + public void ResetDatabase() + { + lock (writeLock) + { + recycleThreadContexts(); + host.Storage.DeleteDatabase(database_name); + } + } + } +} diff --git a/osu.Game/Database/DatabaseWriteUsage.cs b/osu.Game/Database/DatabaseWriteUsage.cs index 52dd0ee268..7858c1a0d1 100644 --- a/osu.Game/Database/DatabaseWriteUsage.cs +++ b/osu.Game/Database/DatabaseWriteUsage.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using Microsoft.EntityFrameworkCore.Storage; - -namespace osu.Game.Database -{ - public class DatabaseWriteUsage : IDisposable - { - public readonly OsuDbContext Context; - private readonly IDbContextTransaction transaction; - private readonly Action usageCompleted; - - public DatabaseWriteUsage(OsuDbContext context, Action onCompleted) - { - Context = context; - transaction = Context.BeginTransaction(); - usageCompleted = onCompleted; - } - - public bool PerformedWrite { get; private set; } - - private bool isDisposed; - - protected void Dispose(bool disposing) - { - if (isDisposed) return; - isDisposed = true; - - PerformedWrite |= Context.SaveChanges(transaction) > 0; - usageCompleted?.Invoke(this); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~DatabaseWriteUsage() - { - Dispose(false); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using Microsoft.EntityFrameworkCore.Storage; + +namespace osu.Game.Database +{ + public class DatabaseWriteUsage : IDisposable + { + public readonly OsuDbContext Context; + private readonly IDbContextTransaction transaction; + private readonly Action usageCompleted; + + public DatabaseWriteUsage(OsuDbContext context, Action onCompleted) + { + Context = context; + transaction = Context.BeginTransaction(); + usageCompleted = onCompleted; + } + + public bool PerformedWrite { get; private set; } + + private bool isDisposed; + + protected void Dispose(bool disposing) + { + if (isDisposed) return; + isDisposed = true; + + PerformedWrite |= Context.SaveChanges(transaction) > 0; + usageCompleted?.Invoke(this); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~DatabaseWriteUsage() + { + Dispose(false); + } + } +} diff --git a/osu.Game/Database/ICanAcceptFiles.cs b/osu.Game/Database/ICanAcceptFiles.cs index ab26525619..a3766ef5aa 100644 --- a/osu.Game/Database/ICanAcceptFiles.cs +++ b/osu.Game/Database/ICanAcceptFiles.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Database -{ - /// - /// A class which can accept files for importing. - /// - public interface ICanAcceptFiles - { - /// - /// Import the specified paths. - /// - /// The files which should be imported. - void Import(params string[] paths); - - /// - /// An array of accepted file extensions (in the standard format of ".abc"). - /// - string[] HandledExtensions { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Database +{ + /// + /// A class which can accept files for importing. + /// + public interface ICanAcceptFiles + { + /// + /// Import the specified paths. + /// + /// The files which should be imported. + void Import(params string[] paths); + + /// + /// An array of accepted file extensions (in the standard format of ".abc"). + /// + string[] HandledExtensions { get; } + } +} diff --git a/osu.Game/Database/IDatabaseContextFactory.cs b/osu.Game/Database/IDatabaseContextFactory.cs index bc1bc0349c..372e1770e4 100644 --- a/osu.Game/Database/IDatabaseContextFactory.cs +++ b/osu.Game/Database/IDatabaseContextFactory.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Database -{ - public interface IDatabaseContextFactory - { - /// - /// Get a context for read-only usage. - /// - OsuDbContext Get(); - - /// - /// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context). - /// This method may block if a write is already active on a different thread. - /// - /// A usage containing a usable context. - DatabaseWriteUsage GetForWrite(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Database +{ + public interface IDatabaseContextFactory + { + /// + /// Get a context for read-only usage. + /// + OsuDbContext Get(); + + /// + /// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context). + /// This method may block if a write is already active on a different thread. + /// + /// A usage containing a usable context. + DatabaseWriteUsage GetForWrite(); + } +} diff --git a/osu.Game/Database/IHasFiles.cs b/osu.Game/Database/IHasFiles.cs index faf3f16dfe..3aaba37efc 100644 --- a/osu.Game/Database/IHasFiles.cs +++ b/osu.Game/Database/IHasFiles.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; - -namespace osu.Game.Database -{ - /// - /// A model that contains a list of files it is responsible for. - /// - /// The model representing a file. - public interface IHasFiles - where TFile : INamedFileInfo - - { - List Files { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; + +namespace osu.Game.Database +{ + /// + /// A model that contains a list of files it is responsible for. + /// + /// The model representing a file. + public interface IHasFiles + where TFile : INamedFileInfo + + { + List Files { get; set; } + } +} diff --git a/osu.Game/Database/IHasPrimaryKey.cs b/osu.Game/Database/IHasPrimaryKey.cs index c5ad5f22f9..2ee356baea 100644 --- a/osu.Game/Database/IHasPrimaryKey.cs +++ b/osu.Game/Database/IHasPrimaryKey.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Database -{ - public interface IHasPrimaryKey - { - int ID { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Database +{ + public interface IHasPrimaryKey + { + int ID { get; set; } + } +} diff --git a/osu.Game/Database/INamedFileInfo.cs b/osu.Game/Database/INamedFileInfo.cs index 8de451dd78..751024d184 100644 --- a/osu.Game/Database/INamedFileInfo.cs +++ b/osu.Game/Database/INamedFileInfo.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.IO; - -namespace osu.Game.Database -{ - /// - /// Represent a join model which gives a filename and scope to a . - /// - public interface INamedFileInfo - { - FileInfo FileInfo { get; set; } - string Filename { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.IO; + +namespace osu.Game.Database +{ + /// + /// Represent a join model which gives a filename and scope to a . + /// + public interface INamedFileInfo + { + FileInfo FileInfo { get; set; } + string Filename { get; set; } + } +} diff --git a/osu.Game/Database/ISoftDelete.cs b/osu.Game/Database/ISoftDelete.cs index c884d7af00..38fba62421 100644 --- a/osu.Game/Database/ISoftDelete.cs +++ b/osu.Game/Database/ISoftDelete.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Database -{ - /// - /// A model that can be deleted from user's view without being instantly lost. - /// - public interface ISoftDelete - { - /// - /// Whether this model is marked for future deletion. - /// - bool DeletePending { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Database +{ + /// + /// A model that can be deleted from user's view without being instantly lost. + /// + public interface ISoftDelete + { + /// + /// Whether this model is marked for future deletion. + /// + bool DeletePending { get; set; } + } +} diff --git a/osu.Game/Database/MutableDatabaseBackedStore.cs b/osu.Game/Database/MutableDatabaseBackedStore.cs index 4ab55691f2..8569d81f01 100644 --- a/osu.Game/Database/MutableDatabaseBackedStore.cs +++ b/osu.Game/Database/MutableDatabaseBackedStore.cs @@ -1,149 +1,149 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using osu.Framework.Platform; - -namespace osu.Game.Database -{ - /// - /// A typed store which supports basic addition, deletion and updating for soft-deletable models. - /// - /// The databased model. - public abstract class MutableDatabaseBackedStore : DatabaseBackedStore - where T : class, IHasPrimaryKey, ISoftDelete - { - public event Action ItemAdded; - public event Action ItemRemoved; - - protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null) - : base(contextFactory, storage) - { - } - - /// - /// Access items pre-populated with includes for consumption. - /// - public IQueryable ConsumableItems => AddIncludesForConsumption(ContextFactory.Get().Set()); - - /// - /// Add a to the database. - /// - /// The item to add. - public void Add(T item) - { - using (var usage = ContextFactory.GetForWrite()) - { - var context = usage.Context; - context.Attach(item); - } - - ItemAdded?.Invoke(item); - } - - /// - /// Update a in the database. - /// - /// The item to update. - public void Update(T item) - { - ItemRemoved?.Invoke(item); - - using (var usage = ContextFactory.GetForWrite()) - usage.Context.Update(item); - - ItemAdded?.Invoke(item); - } - - /// - /// Delete a from the database. - /// - /// The item to delete. - public bool Delete(T item) - { - using (ContextFactory.GetForWrite()) - { - Refresh(ref item); - - if (item.DeletePending) return false; - item.DeletePending = true; - } - - ItemRemoved?.Invoke(item); - return true; - } - - /// - /// Restore a from a deleted state. - /// - /// The item to undelete. - public bool Undelete(T item) - { - using (ContextFactory.GetForWrite()) - { - Refresh(ref item, ConsumableItems); - - if (!item.DeletePending) return false; - item.DeletePending = false; - } - - ItemAdded?.Invoke(item); - return true; - } - - /// - /// Allow implementations to add database-side includes or constraints when querying for consumption of items. - /// - /// The input query. - /// A potentially modified output query. - protected virtual IQueryable AddIncludesForConsumption(IQueryable query) => query; - - /// - /// Allow implementations to add database-side includes or constraints when deleting items. - /// Included properties could then be subsequently deleted by overriding . - /// - /// The input query. - /// A potentially modified output query. - protected virtual IQueryable AddIncludesForDeletion(IQueryable query) => query; - - /// - /// Called when removing an item completely from the database. - /// - /// The items to be purged. - /// The write context which can be used to perform subsequent deletions. - protected virtual void Purge(List items, OsuDbContext context) => context.RemoveRange(items); - - public override void Cleanup() - { - base.Cleanup(); - PurgeDeletable(); - } - - /// - /// Purge items in a pending delete state. - /// - /// An optional query limiting the scope of the purge. - public void PurgeDeletable(Expression> query = null) - { - using (var usage = ContextFactory.GetForWrite()) - { - var context = usage.Context; - - var lookup = context.Set().Where(s => s.DeletePending); - - if (query != null) lookup = lookup.Where(query); - - lookup = AddIncludesForDeletion(lookup); - - var purgeable = lookup.ToList(); - - if (!purgeable.Any()) return; - - Purge(purgeable, context); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using osu.Framework.Platform; + +namespace osu.Game.Database +{ + /// + /// A typed store which supports basic addition, deletion and updating for soft-deletable models. + /// + /// The databased model. + public abstract class MutableDatabaseBackedStore : DatabaseBackedStore + where T : class, IHasPrimaryKey, ISoftDelete + { + public event Action ItemAdded; + public event Action ItemRemoved; + + protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null) + : base(contextFactory, storage) + { + } + + /// + /// Access items pre-populated with includes for consumption. + /// + public IQueryable ConsumableItems => AddIncludesForConsumption(ContextFactory.Get().Set()); + + /// + /// Add a to the database. + /// + /// The item to add. + public void Add(T item) + { + using (var usage = ContextFactory.GetForWrite()) + { + var context = usage.Context; + context.Attach(item); + } + + ItemAdded?.Invoke(item); + } + + /// + /// Update a in the database. + /// + /// The item to update. + public void Update(T item) + { + ItemRemoved?.Invoke(item); + + using (var usage = ContextFactory.GetForWrite()) + usage.Context.Update(item); + + ItemAdded?.Invoke(item); + } + + /// + /// Delete a from the database. + /// + /// The item to delete. + public bool Delete(T item) + { + using (ContextFactory.GetForWrite()) + { + Refresh(ref item); + + if (item.DeletePending) return false; + item.DeletePending = true; + } + + ItemRemoved?.Invoke(item); + return true; + } + + /// + /// Restore a from a deleted state. + /// + /// The item to undelete. + public bool Undelete(T item) + { + using (ContextFactory.GetForWrite()) + { + Refresh(ref item, ConsumableItems); + + if (!item.DeletePending) return false; + item.DeletePending = false; + } + + ItemAdded?.Invoke(item); + return true; + } + + /// + /// Allow implementations to add database-side includes or constraints when querying for consumption of items. + /// + /// The input query. + /// A potentially modified output query. + protected virtual IQueryable AddIncludesForConsumption(IQueryable query) => query; + + /// + /// Allow implementations to add database-side includes or constraints when deleting items. + /// Included properties could then be subsequently deleted by overriding . + /// + /// The input query. + /// A potentially modified output query. + protected virtual IQueryable AddIncludesForDeletion(IQueryable query) => query; + + /// + /// Called when removing an item completely from the database. + /// + /// The items to be purged. + /// The write context which can be used to perform subsequent deletions. + protected virtual void Purge(List items, OsuDbContext context) => context.RemoveRange(items); + + public override void Cleanup() + { + base.Cleanup(); + PurgeDeletable(); + } + + /// + /// Purge items in a pending delete state. + /// + /// An optional query limiting the scope of the purge. + public void PurgeDeletable(Expression> query = null) + { + using (var usage = ContextFactory.GetForWrite()) + { + var context = usage.Context; + + var lookup = context.Set().Where(s => s.DeletePending); + + if (query != null) lookup = lookup.Where(query); + + lookup = AddIncludesForDeletion(lookup); + + var purgeable = lookup.ToList(); + + if (!purgeable.Any()) return; + + Purge(purgeable, context); + } + } + } +} diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index a4b0c30478..1979ce3648 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -1,209 +1,209 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.Extensions.Logging; -using osu.Framework.Logging; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.IO; -using osu.Game.Rulesets; -using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding; -using LogLevel = Microsoft.Extensions.Logging.LogLevel; -using osu.Game.Skinning; - -namespace osu.Game.Database -{ - public class OsuDbContext : DbContext - { - public DbSet BeatmapInfo { get; set; } - public DbSet BeatmapDifficulty { get; set; } - public DbSet BeatmapMetadata { get; set; } - public DbSet BeatmapSetInfo { get; set; } - public DbSet DatabasedKeyBinding { get; set; } - public DbSet DatabasedSetting { get; set; } - public DbSet FileInfo { get; set; } - public DbSet RulesetInfo { get; set; } - public DbSet SkinInfo { get; set; } - - private readonly string connectionString; - - private static readonly Lazy logger = new Lazy(() => new OsuDbLoggerFactory()); - - static OsuDbContext() - { - // required to initialise native SQLite libraries on some platforms. - SQLitePCL.Batteries_V2.Init(); - } - - /// - /// Create a new in-memory OsuDbContext instance. - /// - public OsuDbContext() - : this("DataSource=:memory:") - { - // required for tooling (see https://wildermuth.com/2017/07/06/Program-cs-in-ASP-NET-Core-2-0). - - Migrate(); - } - - /// - /// Create a new OsuDbContext instance. - /// - /// A valid SQLite connection string. - public OsuDbContext(string connectionString) - { - this.connectionString = connectionString; - - var connection = Database.GetDbConnection(); - connection.Open(); - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "PRAGMA journal_mode=WAL;"; - cmd.ExecuteNonQuery(); - } - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - optionsBuilder - // this is required for the time being due to the way we are querying in places like BeatmapStore. - // if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled. - .ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning)) - .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) - .UseLoggerFactory(logger.Value); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity().HasIndex(b => b.OnlineBeatmapID).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.MD5Hash).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); - - modelBuilder.Entity().HasIndex(b => b.OnlineBeatmapSetID).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.DeletePending); - modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); - - modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); - modelBuilder.Entity().HasIndex(b => b.IntAction); - - modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); - - modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.ReferenceCount); - - modelBuilder.Entity().HasIndex(b => b.Available); - modelBuilder.Entity().HasIndex(b => b.ShortName).IsUnique(); - - modelBuilder.Entity().HasOne(b => b.BaseDifficulty); - } - - public IDbContextTransaction BeginTransaction() - { - // return Database.BeginTransaction(); - return null; - } - - public int SaveChanges(IDbContextTransaction transaction = null) - { - var ret = base.SaveChanges(); - if (ret > 0) transaction?.Commit(); - return ret; - } - - private class OsuDbLoggerFactory : ILoggerFactory - { - #region Disposal - - public void Dispose() - { - } - - #endregion - - public ILogger CreateLogger(string categoryName) => new OsuDbLogger(); - - public void AddProvider(ILoggerProvider provider) - { - // no-op. called by tooling. - } - - private class OsuDbLoggerProvider : ILoggerProvider - { - #region Disposal - - public void Dispose() - { - } - - #endregion - - public ILogger CreateLogger(string categoryName) => new OsuDbLogger(); - } - - private class OsuDbLogger : ILogger - { - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - if (logLevel < LogLevel.Information) - return; - - Framework.Logging.LogLevel frameworkLogLevel; - - switch (logLevel) - { - default: - frameworkLogLevel = Framework.Logging.LogLevel.Debug; - break; - case LogLevel.Warning: - frameworkLogLevel = Framework.Logging.LogLevel.Important; - break; - case LogLevel.Error: - case LogLevel.Critical: - frameworkLogLevel = Framework.Logging.LogLevel.Error; - break; - } - - Logger.Log(formatter(state, exception), LoggingTarget.Database, frameworkLogLevel); - } - - public bool IsEnabled(LogLevel logLevel) - { -#if DEBUG_DATABASE - return logLevel > LogLevel.Debug; -#else - return logLevel > LogLevel.Information; -#endif - } - - public IDisposable BeginScope(TState state) => null; - } - } - - public void Migrate() - { - try - { - Database.Migrate(); - } - catch (Exception e) - { - throw new MigrationFailedException(e); - } - } - } - - public class MigrationFailedException : Exception - { - public MigrationFailedException(Exception exception) - : base("sqlite-net migration failed", exception) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Logging; +using osu.Framework.Logging; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.IO; +using osu.Game.Rulesets; +using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; +using osu.Game.Skinning; + +namespace osu.Game.Database +{ + public class OsuDbContext : DbContext + { + public DbSet BeatmapInfo { get; set; } + public DbSet BeatmapDifficulty { get; set; } + public DbSet BeatmapMetadata { get; set; } + public DbSet BeatmapSetInfo { get; set; } + public DbSet DatabasedKeyBinding { get; set; } + public DbSet DatabasedSetting { get; set; } + public DbSet FileInfo { get; set; } + public DbSet RulesetInfo { get; set; } + public DbSet SkinInfo { get; set; } + + private readonly string connectionString; + + private static readonly Lazy logger = new Lazy(() => new OsuDbLoggerFactory()); + + static OsuDbContext() + { + // required to initialise native SQLite libraries on some platforms. + SQLitePCL.Batteries_V2.Init(); + } + + /// + /// Create a new in-memory OsuDbContext instance. + /// + public OsuDbContext() + : this("DataSource=:memory:") + { + // required for tooling (see https://wildermuth.com/2017/07/06/Program-cs-in-ASP-NET-Core-2-0). + + Migrate(); + } + + /// + /// Create a new OsuDbContext instance. + /// + /// A valid SQLite connection string. + public OsuDbContext(string connectionString) + { + this.connectionString = connectionString; + + var connection = Database.GetDbConnection(); + connection.Open(); + using (var cmd = connection.CreateCommand()) + { + cmd.CommandText = "PRAGMA journal_mode=WAL;"; + cmd.ExecuteNonQuery(); + } + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + optionsBuilder + // this is required for the time being due to the way we are querying in places like BeatmapStore. + // if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled. + .ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning)) + .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) + .UseLoggerFactory(logger.Value); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity().HasIndex(b => b.OnlineBeatmapID).IsUnique(); + modelBuilder.Entity().HasIndex(b => b.MD5Hash).IsUnique(); + modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); + + modelBuilder.Entity().HasIndex(b => b.OnlineBeatmapSetID).IsUnique(); + modelBuilder.Entity().HasIndex(b => b.DeletePending); + modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); + + modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); + modelBuilder.Entity().HasIndex(b => b.IntAction); + + modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); + + modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); + modelBuilder.Entity().HasIndex(b => b.ReferenceCount); + + modelBuilder.Entity().HasIndex(b => b.Available); + modelBuilder.Entity().HasIndex(b => b.ShortName).IsUnique(); + + modelBuilder.Entity().HasOne(b => b.BaseDifficulty); + } + + public IDbContextTransaction BeginTransaction() + { + // return Database.BeginTransaction(); + return null; + } + + public int SaveChanges(IDbContextTransaction transaction = null) + { + var ret = base.SaveChanges(); + if (ret > 0) transaction?.Commit(); + return ret; + } + + private class OsuDbLoggerFactory : ILoggerFactory + { + #region Disposal + + public void Dispose() + { + } + + #endregion + + public ILogger CreateLogger(string categoryName) => new OsuDbLogger(); + + public void AddProvider(ILoggerProvider provider) + { + // no-op. called by tooling. + } + + private class OsuDbLoggerProvider : ILoggerProvider + { + #region Disposal + + public void Dispose() + { + } + + #endregion + + public ILogger CreateLogger(string categoryName) => new OsuDbLogger(); + } + + private class OsuDbLogger : ILogger + { + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (logLevel < LogLevel.Information) + return; + + Framework.Logging.LogLevel frameworkLogLevel; + + switch (logLevel) + { + default: + frameworkLogLevel = Framework.Logging.LogLevel.Debug; + break; + case LogLevel.Warning: + frameworkLogLevel = Framework.Logging.LogLevel.Important; + break; + case LogLevel.Error: + case LogLevel.Critical: + frameworkLogLevel = Framework.Logging.LogLevel.Error; + break; + } + + Logger.Log(formatter(state, exception), LoggingTarget.Database, frameworkLogLevel); + } + + public bool IsEnabled(LogLevel logLevel) + { +#if DEBUG_DATABASE + return logLevel > LogLevel.Debug; +#else + return logLevel > LogLevel.Information; +#endif + } + + public IDisposable BeginScope(TState state) => null; + } + } + + public void Migrate() + { + try + { + Database.Migrate(); + } + catch (Exception e) + { + throw new MigrationFailedException(e); + } + } + } + + public class MigrationFailedException : Exception + { + public MigrationFailedException(Exception exception) + : base("sqlite-net migration failed", exception) + { + } + } +} diff --git a/osu.Game/Database/SingletonContextFactory.cs b/osu.Game/Database/SingletonContextFactory.cs index 067e4fd8eb..74951e8433 100644 --- a/osu.Game/Database/SingletonContextFactory.cs +++ b/osu.Game/Database/SingletonContextFactory.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Database -{ - public class SingletonContextFactory : IDatabaseContextFactory - { - private readonly OsuDbContext context; - - public SingletonContextFactory(OsuDbContext context) - { - this.context = context; - } - - public OsuDbContext Get() => context; - - public DatabaseWriteUsage GetForWrite() => new DatabaseWriteUsage(context, null); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Database +{ + public class SingletonContextFactory : IDatabaseContextFactory + { + private readonly OsuDbContext context; + + public SingletonContextFactory(OsuDbContext context) + { + this.context = context; + } + + public OsuDbContext Get() => context; + + public DatabaseWriteUsage GetForWrite() => new DatabaseWriteUsage(context, null); + } +} diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index c5cc14c25f..d5825a8c42 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using OpenTK.Graphics; -using osu.Game.Graphics.Textures; - -namespace osu.Game.Graphics.Backgrounds -{ - public class Background : BufferedContainer - { - public Sprite Sprite; - - private readonly string textureName; - - public Background(string textureName = @"") - { - CacheDrawnFrameBuffer = true; - - this.textureName = textureName; - RelativeSizeAxes = Axes.Both; - - Add(Sprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Colour = Color4.DarkGray, - FillMode = FillMode.Fill, - }); - } - - [BackgroundDependencyLoader] - private void load(LargeTextureStore textures) - { - if (!string.IsNullOrEmpty(textureName)) - Sprite.Texture = textures.Get(textureName); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using OpenTK.Graphics; +using osu.Game.Graphics.Textures; + +namespace osu.Game.Graphics.Backgrounds +{ + public class Background : BufferedContainer + { + public Sprite Sprite; + + private readonly string textureName; + + public Background(string textureName = @"") + { + CacheDrawnFrameBuffer = true; + + this.textureName = textureName; + RelativeSizeAxes = Axes.Both; + + Add(Sprite = new Sprite + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.DarkGray, + FillMode = FillMode.Fill, + }); + } + + [BackgroundDependencyLoader] + private void load(LargeTextureStore textures) + { + if (!string.IsNullOrEmpty(textureName)) + Sprite.Texture = textures.Get(textureName); + } + } +} diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 89ed8044e6..01b09c0a40 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -1,280 +1,280 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.MathUtils; -using OpenTK; -using OpenTK.Graphics; -using System; -using osu.Framework.Graphics.Shaders; -using osu.Framework.Graphics.Textures; -using OpenTK.Graphics.ES30; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Allocation; -using System.Collections.Generic; -using osu.Framework.Graphics.Batches; -using osu.Framework.Graphics.OpenGL.Vertices; -using osu.Framework.Lists; - -namespace osu.Game.Graphics.Backgrounds -{ - public class Triangles : Drawable - { - private const float triangle_size = 100; - private const float base_velocity = 50; - - /// - /// How many screen-space pixels are smoothed over. - /// Same behavior as Sprite's EdgeSmoothness. - /// - private const float edge_smoothness = 1; - - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - - - public Color4 ColourLight = Color4.White; - public Color4 ColourDark = Color4.Black; - - /// - /// Whether we want to expire triangles as they exit our draw area completely. - /// - protected virtual bool ExpireOffScreenTriangles => true; - - /// - /// Whether we should create new triangles as others expire. - /// - protected virtual bool CreateNewTriangles => true; - - /// - /// The amount of triangles we want compared to the default distribution. - /// - protected virtual float SpawnRatio => 1; - - private float triangleScale = 1; - - /// - /// Whether we should drop-off alpha values of triangles more quickly to improve - /// the visual appearance of fading. This defaults to on as it is generally more - /// aesthetically pleasing, but should be turned off in buffered containers. - /// - public bool HideAlphaDiscrepancies = true; - - /// - /// The relative velocity of the triangles. Default is 1. - /// - public float Velocity = 1; - - private readonly SortedList parts = new SortedList(Comparer.Default); - - private Shader shader; - private readonly Texture texture; - - public Triangles() - { - texture = Texture.WhitePixel; - } - - [BackgroundDependencyLoader] - private void load(ShaderManager shaders) - { - shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - addTriangles(true); - } - - public float TriangleScale - { - get { return triangleScale; } - set - { - float change = value / triangleScale; - triangleScale = value; - - for (int i = 0; i < parts.Count; i++) - { - TriangleParticle newParticle = parts[i]; - newParticle.Scale *= change; - parts[i] = newParticle; - } - } - } - - protected override void Update() - { - base.Update(); - - Invalidate(Invalidation.DrawNode, shallPropagate: false); - - if (CreateNewTriangles) - addTriangles(false); - - float adjustedAlpha = HideAlphaDiscrepancies ? - // Cubically scale alpha to make it drop off more sharply. - (float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) : - 1; - - float elapsedSeconds = (float)Time.Elapsed / 1000; - // Since position is relative, the velocity needs to scale inversely with DrawHeight. - // Since we will later multiply by the scale of individual triangles we normalize by - // dividing by triangleScale. - float movedDistance = -elapsedSeconds * Velocity * base_velocity / (DrawHeight * triangleScale); - - for (int i = 0; i < parts.Count; i++) - { - TriangleParticle newParticle = parts[i]; - - // Scale moved distance by the size of the triangle. Smaller triangles should move more slowly. - newParticle.Position.Y += parts[i].Scale * movedDistance; - newParticle.Colour.A = adjustedAlpha; - - parts[i] = newParticle; - - float bottomPos = parts[i].Position.Y + triangle_size * parts[i].Scale * 0.866f / DrawHeight; - if (bottomPos < 0) - parts.RemoveAt(i); - } - } - - private void addTriangles(bool randomY) - { - int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); - - for (int i = 0; i < aimTriangleCount - parts.Count; i++) - parts.Add(createTriangle(randomY)); - } - - private TriangleParticle createTriangle(bool randomY) - { - TriangleParticle particle = CreateTriangle(); - - particle.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() : 1); - particle.Colour = CreateTriangleShade(); - - return particle; - } - - /// - /// Creates a triangle particle with a random scale. - /// - /// The triangle particle. - protected virtual TriangleParticle CreateTriangle() - { - const float std_dev = 0.16f; - const float mean = 0.5f; - - float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats - float u2 = 1 - RNG.NextSingle(); - float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) - var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) - - return new TriangleParticle { Scale = scale }; - } - - /// - /// Creates a shade of colour for the triangles. - /// - /// The colour. - protected virtual Color4 CreateTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); - - protected override DrawNode CreateDrawNode() => new TrianglesDrawNode(); - - private readonly TrianglesDrawNodeSharedData sharedData = new TrianglesDrawNodeSharedData(); - protected override void ApplyDrawNode(DrawNode node) - { - base.ApplyDrawNode(node); - - var trianglesNode = (TrianglesDrawNode)node; - - trianglesNode.Shader = shader; - trianglesNode.Texture = texture; - trianglesNode.Size = DrawSize; - trianglesNode.Shared = sharedData; - - trianglesNode.Parts.Clear(); - trianglesNode.Parts.AddRange(parts); - } - - private class TrianglesDrawNodeSharedData - { - public readonly LinearBatch VertexBatch = new LinearBatch(100 * 3, 10, PrimitiveType.Triangles); - } - - private class TrianglesDrawNode : DrawNode - { - public Shader Shader; - public Texture Texture; - - public TrianglesDrawNodeSharedData Shared; - - public readonly List Parts = new List(); - public Vector2 Size; - - public override void Draw(Action vertexAction) - { - base.Draw(vertexAction); - - Shader.Bind(); - Texture.TextureGL.Bind(); - - Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy; - - foreach (TriangleParticle particle in Parts) - { - var offset = triangle_size * new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f); - var size = new Vector2(2 * offset.X, offset.Y); - - var triangle = new Triangle( - Vector2Extensions.Transform(particle.Position * Size, DrawInfo.Matrix), - Vector2Extensions.Transform(particle.Position * Size + offset, DrawInfo.Matrix), - Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix) - ); - - ColourInfo colourInfo = DrawInfo.Colour; - colourInfo.ApplyChild(particle.Colour); - - Texture.DrawTriangle( - triangle, - colourInfo, - null, - Shared.VertexBatch.AddAction, - Vector2.Divide(localInflationAmount, size)); - } - - Shader.Unbind(); - } - } - - protected struct TriangleParticle : IComparable - { - /// - /// The position of the top vertex of the triangle. - /// - public Vector2 Position; - - /// - /// The colour of the triangle. - /// - public Color4 Colour; - - /// - /// The scale of the triangle. - /// - public float Scale; - - /// - /// Compares two s. This is a reverse comparer because when the - /// triangles are added to the particles list, they should be drawn from largest to smallest - /// such that the smaller triangles appear on top. - /// - /// - /// - public int CompareTo(TriangleParticle other) => other.Scale.CompareTo(Scale); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using OpenTK; +using OpenTK.Graphics; +using System; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using OpenTK.Graphics.ES30; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Allocation; +using System.Collections.Generic; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Lists; + +namespace osu.Game.Graphics.Backgrounds +{ + public class Triangles : Drawable + { + private const float triangle_size = 100; + private const float base_velocity = 50; + + /// + /// How many screen-space pixels are smoothed over. + /// Same behavior as Sprite's EdgeSmoothness. + /// + private const float edge_smoothness = 1; + + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; + + + public Color4 ColourLight = Color4.White; + public Color4 ColourDark = Color4.Black; + + /// + /// Whether we want to expire triangles as they exit our draw area completely. + /// + protected virtual bool ExpireOffScreenTriangles => true; + + /// + /// Whether we should create new triangles as others expire. + /// + protected virtual bool CreateNewTriangles => true; + + /// + /// The amount of triangles we want compared to the default distribution. + /// + protected virtual float SpawnRatio => 1; + + private float triangleScale = 1; + + /// + /// Whether we should drop-off alpha values of triangles more quickly to improve + /// the visual appearance of fading. This defaults to on as it is generally more + /// aesthetically pleasing, but should be turned off in buffered containers. + /// + public bool HideAlphaDiscrepancies = true; + + /// + /// The relative velocity of the triangles. Default is 1. + /// + public float Velocity = 1; + + private readonly SortedList parts = new SortedList(Comparer.Default); + + private Shader shader; + private readonly Texture texture; + + public Triangles() + { + texture = Texture.WhitePixel; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders) + { + shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + addTriangles(true); + } + + public float TriangleScale + { + get { return triangleScale; } + set + { + float change = value / triangleScale; + triangleScale = value; + + for (int i = 0; i < parts.Count; i++) + { + TriangleParticle newParticle = parts[i]; + newParticle.Scale *= change; + parts[i] = newParticle; + } + } + } + + protected override void Update() + { + base.Update(); + + Invalidate(Invalidation.DrawNode, shallPropagate: false); + + if (CreateNewTriangles) + addTriangles(false); + + float adjustedAlpha = HideAlphaDiscrepancies ? + // Cubically scale alpha to make it drop off more sharply. + (float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) : + 1; + + float elapsedSeconds = (float)Time.Elapsed / 1000; + // Since position is relative, the velocity needs to scale inversely with DrawHeight. + // Since we will later multiply by the scale of individual triangles we normalize by + // dividing by triangleScale. + float movedDistance = -elapsedSeconds * Velocity * base_velocity / (DrawHeight * triangleScale); + + for (int i = 0; i < parts.Count; i++) + { + TriangleParticle newParticle = parts[i]; + + // Scale moved distance by the size of the triangle. Smaller triangles should move more slowly. + newParticle.Position.Y += parts[i].Scale * movedDistance; + newParticle.Colour.A = adjustedAlpha; + + parts[i] = newParticle; + + float bottomPos = parts[i].Position.Y + triangle_size * parts[i].Scale * 0.866f / DrawHeight; + if (bottomPos < 0) + parts.RemoveAt(i); + } + } + + private void addTriangles(bool randomY) + { + int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); + + for (int i = 0; i < aimTriangleCount - parts.Count; i++) + parts.Add(createTriangle(randomY)); + } + + private TriangleParticle createTriangle(bool randomY) + { + TriangleParticle particle = CreateTriangle(); + + particle.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() : 1); + particle.Colour = CreateTriangleShade(); + + return particle; + } + + /// + /// Creates a triangle particle with a random scale. + /// + /// The triangle particle. + protected virtual TriangleParticle CreateTriangle() + { + const float std_dev = 0.16f; + const float mean = 0.5f; + + float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats + float u2 = 1 - RNG.NextSingle(); + float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) + var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) + + return new TriangleParticle { Scale = scale }; + } + + /// + /// Creates a shade of colour for the triangles. + /// + /// The colour. + protected virtual Color4 CreateTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); + + protected override DrawNode CreateDrawNode() => new TrianglesDrawNode(); + + private readonly TrianglesDrawNodeSharedData sharedData = new TrianglesDrawNodeSharedData(); + protected override void ApplyDrawNode(DrawNode node) + { + base.ApplyDrawNode(node); + + var trianglesNode = (TrianglesDrawNode)node; + + trianglesNode.Shader = shader; + trianglesNode.Texture = texture; + trianglesNode.Size = DrawSize; + trianglesNode.Shared = sharedData; + + trianglesNode.Parts.Clear(); + trianglesNode.Parts.AddRange(parts); + } + + private class TrianglesDrawNodeSharedData + { + public readonly LinearBatch VertexBatch = new LinearBatch(100 * 3, 10, PrimitiveType.Triangles); + } + + private class TrianglesDrawNode : DrawNode + { + public Shader Shader; + public Texture Texture; + + public TrianglesDrawNodeSharedData Shared; + + public readonly List Parts = new List(); + public Vector2 Size; + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + Shader.Bind(); + Texture.TextureGL.Bind(); + + Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy; + + foreach (TriangleParticle particle in Parts) + { + var offset = triangle_size * new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f); + var size = new Vector2(2 * offset.X, offset.Y); + + var triangle = new Triangle( + Vector2Extensions.Transform(particle.Position * Size, DrawInfo.Matrix), + Vector2Extensions.Transform(particle.Position * Size + offset, DrawInfo.Matrix), + Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix) + ); + + ColourInfo colourInfo = DrawInfo.Colour; + colourInfo.ApplyChild(particle.Colour); + + Texture.DrawTriangle( + triangle, + colourInfo, + null, + Shared.VertexBatch.AddAction, + Vector2.Divide(localInflationAmount, size)); + } + + Shader.Unbind(); + } + } + + protected struct TriangleParticle : IComparable + { + /// + /// The position of the top vertex of the triangle. + /// + public Vector2 Position; + + /// + /// The colour of the triangle. + /// + public Color4 Colour; + + /// + /// The scale of the triangle. + /// + public float Scale; + + /// + /// Compares two s. This is a reverse comparer because when the + /// triangles are added to the particles list, they should be drawn from largest to smallest + /// such that the smaller triangles appear on top. + /// + /// + /// + public int CompareTo(TriangleParticle other) => other.Scale.CompareTo(Scale); + } + } +} diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 5a5f283fb9..bf16af4706 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -1,86 +1,86 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio.Track; -using osu.Framework.Configuration; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Graphics.Containers -{ - public class BeatSyncedContainer : Container - { - protected readonly Bindable Beatmap = new Bindable(); - - private int lastBeat; - private TimingControlPoint lastTimingPoint; - - /// - /// The amount of time before a beat we should fire . - /// This allows for adding easing to animations that may be synchronised to the beat. - /// - protected double EarlyActivationMilliseconds; - - /// - /// The time in milliseconds until the next beat. - /// - public double TimeUntilNextBeat { get; private set; } - - /// - /// The time in milliseconds since the last beat - /// - public double TimeSinceLastBeat { get; private set; } - - protected override void Update() - { - if (!Beatmap.Value.TrackLoaded || !Beatmap.Value.BeatmapLoaded) return; - - var track = Beatmap.Value.Track; - var beatmap = Beatmap.Value.Beatmap; - - if (track == null || beatmap == null) - return; - - double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime; - - TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); - - if (timingPoint.BeatLength == 0) - return; - - int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); - - // The beats before the start of the first control point are off by 1, this should do the trick - if (currentTrackTime < timingPoint.Time) - beatIndex--; - - TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength; - if (TimeUntilNextBeat < 0) - TimeUntilNextBeat += timingPoint.BeatLength; - - TimeSinceLastBeat = timingPoint.BeatLength - TimeUntilNextBeat; - - if (timingPoint.Equals(lastTimingPoint) && beatIndex == lastBeat) - return; - - using (BeginDelayedSequence(-TimeSinceLastBeat, true)) - OnNewBeat(beatIndex, timingPoint, effectPoint, track.CurrentAmplitudes); - - lastBeat = beatIndex; - lastTimingPoint = timingPoint; - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase game) - { - Beatmap.BindTo(game.Beatmap); - } - - protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Graphics.Containers +{ + public class BeatSyncedContainer : Container + { + protected readonly Bindable Beatmap = new Bindable(); + + private int lastBeat; + private TimingControlPoint lastTimingPoint; + + /// + /// The amount of time before a beat we should fire . + /// This allows for adding easing to animations that may be synchronised to the beat. + /// + protected double EarlyActivationMilliseconds; + + /// + /// The time in milliseconds until the next beat. + /// + public double TimeUntilNextBeat { get; private set; } + + /// + /// The time in milliseconds since the last beat + /// + public double TimeSinceLastBeat { get; private set; } + + protected override void Update() + { + if (!Beatmap.Value.TrackLoaded || !Beatmap.Value.BeatmapLoaded) return; + + var track = Beatmap.Value.Track; + var beatmap = Beatmap.Value.Beatmap; + + if (track == null || beatmap == null) + return; + + double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime; + + TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); + + if (timingPoint.BeatLength == 0) + return; + + int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); + + // The beats before the start of the first control point are off by 1, this should do the trick + if (currentTrackTime < timingPoint.Time) + beatIndex--; + + TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength; + if (TimeUntilNextBeat < 0) + TimeUntilNextBeat += timingPoint.BeatLength; + + TimeSinceLastBeat = timingPoint.BeatLength - TimeUntilNextBeat; + + if (timingPoint.Equals(lastTimingPoint) && beatIndex == lastBeat) + return; + + using (BeginDelayedSequence(-TimeSinceLastBeat, true)) + OnNewBeat(beatIndex, timingPoint, effectPoint, track.CurrentAmplitudes); + + lastBeat = beatIndex; + lastTimingPoint = timingPoint; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + Beatmap.BindTo(game.Beatmap); + } + + protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + } + } +} diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs index ad0b815957..7289bfe52c 100644 --- a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs +++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs @@ -1,62 +1,62 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Graphics.Containers -{ - /// - /// Display an icon that is forced to scale to the size of this container. - /// - public class ConstrainedIconContainer : CompositeDrawable - { - public Drawable Icon - { - get - { - return InternalChild; - } - - set - { - InternalChild = value; - } - } - - /// - /// Determines an edge effect of this . - /// Edge effects are e.g. glow or a shadow. - /// Only has an effect when is true. - /// - public new EdgeEffectParameters EdgeEffect - { - get { return base.EdgeEffect; } - set { base.EdgeEffect = value; } - } - - protected override void Update() - { - base.Update(); - if (InternalChildren.Count > 0 && InternalChild.DrawSize.X > 0) - { - // We're modifying scale here for a few reasons - // - Guarantees correctness if BorderWidth is being used - // - If we were to use RelativeSize/FillMode, we'd need to set the Icon's RelativeSizeAxes directly. - // We can't do this because we would need access to AutoSizeAxes to set it to none. - // Other issues come up along the way too, so it's not a good solution. - var fitScale = Math.Min(DrawSize.X / InternalChild.DrawSize.X, DrawSize.Y / InternalChild.DrawSize.Y); - InternalChild.Scale = new Vector2(fitScale); - InternalChild.Anchor = Anchor.Centre; - InternalChild.Origin = Anchor.Centre; - } - } - - public ConstrainedIconContainer() - { - Masking = true; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Graphics.Containers +{ + /// + /// Display an icon that is forced to scale to the size of this container. + /// + public class ConstrainedIconContainer : CompositeDrawable + { + public Drawable Icon + { + get + { + return InternalChild; + } + + set + { + InternalChild = value; + } + } + + /// + /// Determines an edge effect of this . + /// Edge effects are e.g. glow or a shadow. + /// Only has an effect when is true. + /// + public new EdgeEffectParameters EdgeEffect + { + get { return base.EdgeEffect; } + set { base.EdgeEffect = value; } + } + + protected override void Update() + { + base.Update(); + if (InternalChildren.Count > 0 && InternalChild.DrawSize.X > 0) + { + // We're modifying scale here for a few reasons + // - Guarantees correctness if BorderWidth is being used + // - If we were to use RelativeSize/FillMode, we'd need to set the Icon's RelativeSizeAxes directly. + // We can't do this because we would need access to AutoSizeAxes to set it to none. + // Other issues come up along the way too, so it's not a good solution. + var fitScale = Math.Min(DrawSize.X / InternalChild.DrawSize.X, DrawSize.Y / InternalChild.DrawSize.Y); + InternalChild.Scale = new Vector2(fitScale); + InternalChild.Anchor = Anchor.Centre; + InternalChild.Origin = Anchor.Centre; + } + } + + public ConstrainedIconContainer() + { + Masking = true; + } + } +} diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 1d231ada23..8e18dbd2f5 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -1,104 +1,104 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Online.Chat; -using System; -using System.Diagnostics; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Sprites; -using System.Collections.Generic; -using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; - -namespace osu.Game.Graphics.Containers -{ - public class LinkFlowContainer : OsuTextFlowContainer - { - public LinkFlowContainer(Action defaultCreationParameters = null) - : base(defaultCreationParameters) - { - } - - public override bool HandleMouseInput => true; - - private OsuGame game; - - private Action showNotImplementedError; - - [BackgroundDependencyLoader(true)] - private void load(OsuGame game, NotificationOverlay notifications) - { - // will be null in tests - this.game = game; - - showNotImplementedError = () => notifications?.Post(new SimpleNotification - { - Text = @"This link type is not yet supported!", - Icon = FontAwesome.fa_life_saver, - }); - } - - public void AddLinks(string text, List links) - { - if (string.IsNullOrEmpty(text) || links == null) - return; - - if (links.Count == 0) - { - AddText(text); - return; - } - - int previousLinkEnd = 0; - foreach (var link in links) - { - AddText(text.Substring(previousLinkEnd, link.Index - previousLinkEnd)); - AddLink(text.Substring(link.Index, link.Length), link.Url, link.Action, link.Argument); - previousLinkEnd = link.Index + link.Length; - } - - AddText(text.Substring(previousLinkEnd)); - } - - public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null) - { - AddInternal(new DrawableLinkCompiler(AddText(text).ToList()) - { - TooltipText = tooltipText ?? (url != text ? url : string.Empty), - Action = () => - { - switch (linkType) - { - case LinkAction.OpenBeatmap: - // todo: replace this with overlay.ShowBeatmap(id) once an appropriate API call is implemented. - if (int.TryParse(linkArgument, out int beatmapId)) - Process.Start($"https://osu.ppy.sh/b/{beatmapId}"); - break; - case LinkAction.OpenBeatmapSet: - if (int.TryParse(linkArgument, out int setId)) - game?.ShowBeatmapSet(setId); - break; - case LinkAction.OpenChannel: - game?.OpenChannel(linkArgument); - break; - case LinkAction.OpenEditorTimestamp: - case LinkAction.JoinMultiplayerMatch: - case LinkAction.Spectate: - showNotImplementedError?.Invoke(); - break; - case LinkAction.External: - Process.Start(url); - break; - case LinkAction.OpenUserProfile: - if (long.TryParse(linkArgument, out long userId)) - game?.ShowUser(userId); - break; - default: - throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action."); - } - }, - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Online.Chat; +using System; +using System.Diagnostics; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using System.Collections.Generic; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Graphics.Containers +{ + public class LinkFlowContainer : OsuTextFlowContainer + { + public LinkFlowContainer(Action defaultCreationParameters = null) + : base(defaultCreationParameters) + { + } + + public override bool HandleMouseInput => true; + + private OsuGame game; + + private Action showNotImplementedError; + + [BackgroundDependencyLoader(true)] + private void load(OsuGame game, NotificationOverlay notifications) + { + // will be null in tests + this.game = game; + + showNotImplementedError = () => notifications?.Post(new SimpleNotification + { + Text = @"This link type is not yet supported!", + Icon = FontAwesome.fa_life_saver, + }); + } + + public void AddLinks(string text, List links) + { + if (string.IsNullOrEmpty(text) || links == null) + return; + + if (links.Count == 0) + { + AddText(text); + return; + } + + int previousLinkEnd = 0; + foreach (var link in links) + { + AddText(text.Substring(previousLinkEnd, link.Index - previousLinkEnd)); + AddLink(text.Substring(link.Index, link.Length), link.Url, link.Action, link.Argument); + previousLinkEnd = link.Index + link.Length; + } + + AddText(text.Substring(previousLinkEnd)); + } + + public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null) + { + AddInternal(new DrawableLinkCompiler(AddText(text).ToList()) + { + TooltipText = tooltipText ?? (url != text ? url : string.Empty), + Action = () => + { + switch (linkType) + { + case LinkAction.OpenBeatmap: + // todo: replace this with overlay.ShowBeatmap(id) once an appropriate API call is implemented. + if (int.TryParse(linkArgument, out int beatmapId)) + Process.Start($"https://osu.ppy.sh/b/{beatmapId}"); + break; + case LinkAction.OpenBeatmapSet: + if (int.TryParse(linkArgument, out int setId)) + game?.ShowBeatmapSet(setId); + break; + case LinkAction.OpenChannel: + game?.OpenChannel(linkArgument); + break; + case LinkAction.OpenEditorTimestamp: + case LinkAction.JoinMultiplayerMatch: + case LinkAction.Spectate: + showNotImplementedError?.Invoke(); + break; + case LinkAction.External: + Process.Start(url); + break; + case LinkAction.OpenUserProfile: + if (long.TryParse(linkArgument, out long userId)) + game?.ShowUser(userId); + break; + default: + throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action."); + } + }, + }); + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index b9ee1f4463..cf80a549a4 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -1,42 +1,42 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Graphics.Containers -{ - public class OsuClickableContainer : ClickableContainer - { - private readonly HoverSampleSet sampleSet; - - private readonly Container content = new Container { RelativeSizeAxes = Axes.Both }; - - protected override Container Content => content; - - protected virtual HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet); - - public OsuClickableContainer(HoverSampleSet sampleSet = HoverSampleSet.Normal) - { - this.sampleSet = sampleSet; - } - - [BackgroundDependencyLoader] - private void load() - { - if (AutoSizeAxes != Axes.None) - { - content.RelativeSizeAxes = RelativeSizeAxes; - content.AutoSizeAxes = AutoSizeAxes; - } - - InternalChildren = new Drawable[] - { - content, - CreateHoverClickSounds(sampleSet) - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.Containers +{ + public class OsuClickableContainer : ClickableContainer + { + private readonly HoverSampleSet sampleSet; + + private readonly Container content = new Container { RelativeSizeAxes = Axes.Both }; + + protected override Container Content => content; + + protected virtual HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet); + + public OsuClickableContainer(HoverSampleSet sampleSet = HoverSampleSet.Normal) + { + this.sampleSet = sampleSet; + } + + [BackgroundDependencyLoader] + private void load() + { + if (AutoSizeAxes != Axes.None) + { + content.RelativeSizeAxes = RelativeSizeAxes; + content.AutoSizeAxes = AutoSizeAxes; + } + + InternalChildren = new Drawable[] + { + content, + CreateHoverClickSounds(sampleSet) + }; + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index ec461b86fd..2a30e0d032 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -1,73 +1,73 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using OpenTK; - -namespace osu.Game.Graphics.Containers -{ - public class OsuFocusedOverlayContainer : FocusedOverlayContainer - { - private SampleChannel samplePopIn; - private SampleChannel samplePopOut; - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in"); - samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out"); - - StateChanged += onStateChanged; - } - - /// - /// Whether mouse input should be blocked screen-wide while this overlay is visible. - /// Performing mouse actions outside of the valid extents will hide the overlay but pass the events through. - /// - public virtual bool BlockScreenWideMouse => BlockPassThroughMouse; - - // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos); - - protected override bool OnClick(InputState state) - { - if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) - { - State = Visibility.Hidden; - return true; - } - - return base.OnClick(state); - } - - protected override bool OnDragStart(InputState state) - { - if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) - { - State = Visibility.Hidden; - return true; - } - - return base.OnDragStart(state); - } - - protected override bool OnDrag(InputState state) => State == Visibility.Hidden; - - private void onStateChanged(Visibility visibility) - { - switch (visibility) - { - case Visibility.Visible: - samplePopIn?.Play(); - break; - case Visibility.Hidden: - samplePopOut?.Play(); - break; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using OpenTK; + +namespace osu.Game.Graphics.Containers +{ + public class OsuFocusedOverlayContainer : FocusedOverlayContainer + { + private SampleChannel samplePopIn; + private SampleChannel samplePopOut; + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in"); + samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out"); + + StateChanged += onStateChanged; + } + + /// + /// Whether mouse input should be blocked screen-wide while this overlay is visible. + /// Performing mouse actions outside of the valid extents will hide the overlay but pass the events through. + /// + public virtual bool BlockScreenWideMouse => BlockPassThroughMouse; + + // receive input outside our bounds so we can trigger a close event on ourselves. + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos); + + protected override bool OnClick(InputState state) + { + if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) + { + State = Visibility.Hidden; + return true; + } + + return base.OnClick(state); + } + + protected override bool OnDragStart(InputState state) + { + if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) + { + State = Visibility.Hidden; + return true; + } + + return base.OnDragStart(state); + } + + protected override bool OnDrag(InputState state) => State == Visibility.Hidden; + + private void onStateChanged(Visibility visibility) + { + switch (visibility) + { + case Visibility.Visible: + samplePopIn?.Play(); + break; + case Visibility.Hidden: + samplePopOut?.Play(); + break; + } + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index fd1742871b..e88dad93ef 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -1,45 +1,45 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Input; - -namespace osu.Game.Graphics.Containers -{ - public class OsuHoverContainer : OsuClickableContainer - { - protected Color4 HoverColour; - - protected Color4 IdleColour = Color4.White; - - protected virtual IEnumerable EffectTargets => new[] { Content }; - - protected override bool OnHover(InputState state) - { - EffectTargets.ForEach(d => d.FadeColour(HoverColour, 500, Easing.OutQuint)); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - EffectTargets.ForEach(d => d.FadeColour(IdleColour, 500, Easing.OutQuint)); - base.OnHoverLost(state); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - HoverColour = colours.Yellow; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - EffectTargets.ForEach(d => d.FadeColour(IdleColour)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Input; + +namespace osu.Game.Graphics.Containers +{ + public class OsuHoverContainer : OsuClickableContainer + { + protected Color4 HoverColour; + + protected Color4 IdleColour = Color4.White; + + protected virtual IEnumerable EffectTargets => new[] { Content }; + + protected override bool OnHover(InputState state) + { + EffectTargets.ForEach(d => d.FadeColour(HoverColour, 500, Easing.OutQuint)); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + EffectTargets.ForEach(d => d.FadeColour(IdleColour, 500, Easing.OutQuint)); + base.OnHoverLost(state); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + HoverColour = colours.Yellow; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + EffectTargets.ForEach(d => d.FadeColour(IdleColour)); + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 1d5a2af899..ebea9c49de 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -1,75 +1,75 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using OpenTK.Input; - -namespace osu.Game.Graphics.Containers -{ - public class OsuScrollContainer : ScrollContainer - { - /// - /// Allows controlling the scroll bar from any position in the container using the right mouse button. - /// Uses the value of to smoothly scroll to the dragged location. - /// - public bool RightMouseScrollbar = false; - - /// - /// Controls the rate with which the target position is approached when performing a relative drag. Default is 0.02. - /// - public double DistanceDecayOnRightMouseScrollbar = 0.02; - - private bool shouldPerformRightMouseScroll(InputState state) => RightMouseScrollbar && state.Mouse.IsPressed(MouseButton.Right); - - private void scrollToRelative(float value) => ScrollTo(Clamp((value - Scrollbar.DrawSize[ScrollDim] / 2) / Scrollbar.Size[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar); - - private bool mouseScrollBarDragging; - - protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging; - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - if (shouldPerformRightMouseScroll(state)) - { - scrollToRelative(state.Mouse.Position[ScrollDim]); - return true; - } - - return base.OnMouseDown(state, args); - } - - protected override bool OnDrag(InputState state) - { - if (mouseScrollBarDragging) - { - scrollToRelative(state.Mouse.Position[ScrollDim]); - return true; - } - - return base.OnDrag(state); - } - - protected override bool OnDragStart(InputState state) - { - if (shouldPerformRightMouseScroll(state)) - { - mouseScrollBarDragging = true; - return true; - } - - return base.OnDragStart(state); - } - - protected override bool OnDragEnd(InputState state) - { - if (mouseScrollBarDragging) - { - mouseScrollBarDragging = false; - return true; - } - - return base.OnDragEnd(state); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using OpenTK.Input; + +namespace osu.Game.Graphics.Containers +{ + public class OsuScrollContainer : ScrollContainer + { + /// + /// Allows controlling the scroll bar from any position in the container using the right mouse button. + /// Uses the value of to smoothly scroll to the dragged location. + /// + public bool RightMouseScrollbar = false; + + /// + /// Controls the rate with which the target position is approached when performing a relative drag. Default is 0.02. + /// + public double DistanceDecayOnRightMouseScrollbar = 0.02; + + private bool shouldPerformRightMouseScroll(InputState state) => RightMouseScrollbar && state.Mouse.IsPressed(MouseButton.Right); + + private void scrollToRelative(float value) => ScrollTo(Clamp((value - Scrollbar.DrawSize[ScrollDim] / 2) / Scrollbar.Size[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar); + + private bool mouseScrollBarDragging; + + protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging; + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + if (shouldPerformRightMouseScroll(state)) + { + scrollToRelative(state.Mouse.Position[ScrollDim]); + return true; + } + + return base.OnMouseDown(state, args); + } + + protected override bool OnDrag(InputState state) + { + if (mouseScrollBarDragging) + { + scrollToRelative(state.Mouse.Position[ScrollDim]); + return true; + } + + return base.OnDrag(state); + } + + protected override bool OnDragStart(InputState state) + { + if (shouldPerformRightMouseScroll(state)) + { + mouseScrollBarDragging = true; + return true; + } + + return base.OnDragStart(state); + } + + protected override bool OnDragEnd(InputState state) + { + if (mouseScrollBarDragging) + { + mouseScrollBarDragging = false; + return true; + } + + return base.OnDragEnd(state); + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs index 51adf5f232..119af4d762 100644 --- a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Graphics.Containers -{ - public class OsuTextFlowContainer : TextFlowContainer - { - public OsuTextFlowContainer(Action defaultCreationParameters = null) : base(defaultCreationParameters) - { - } - - protected override SpriteText CreateSpriteText() => new OsuSpriteText(); - - public void AddIcon(FontAwesome icon, Action creationParameters = null) => AddText(((char)icon).ToString(), creationParameters); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.Containers +{ + public class OsuTextFlowContainer : TextFlowContainer + { + public OsuTextFlowContainer(Action defaultCreationParameters = null) : base(defaultCreationParameters) + { + } + + protected override SpriteText CreateSpriteText() => new OsuSpriteText(); + + public void AddIcon(FontAwesome icon, Action creationParameters = null) => AddText(((char)icon).ToString(), creationParameters); + } +} diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index 97d6225534..dc635ce7e7 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -1,78 +1,78 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Framework.Input; -using OpenTK; -using osu.Framework.Allocation; -using osu.Game.Configuration; -using osu.Framework.Configuration; -using osu.Framework.MathUtils; - -namespace osu.Game.Graphics.Containers -{ - public class ParallaxContainer : Container, IRequireHighFrequencyMousePosition - { - public const float DEFAULT_PARALLAX_AMOUNT = 0.02f; - - public float ParallaxAmount = DEFAULT_PARALLAX_AMOUNT; - - private Bindable parallaxEnabled; - - public ParallaxContainer() - { - RelativeSizeAxes = Axes.Both; - AddInternal(content = new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }); - } - - private readonly Container content; - private InputManager input; - - protected override Container Content => content; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - parallaxEnabled = config.GetBindable(OsuSetting.MenuParallax); - parallaxEnabled.ValueChanged += delegate - { - if (!parallaxEnabled) - { - content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint); - content.Scale = new Vector2(1 + ParallaxAmount); - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - input = GetContainingInputManager(); - } - - private bool firstUpdate = true; - - protected override void Update() - { - base.Update(); - - if (parallaxEnabled) - { - Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount; - - double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000); - - content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint); - content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint); - } - - firstUpdate = false; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Input; +using OpenTK; +using osu.Framework.Allocation; +using osu.Game.Configuration; +using osu.Framework.Configuration; +using osu.Framework.MathUtils; + +namespace osu.Game.Graphics.Containers +{ + public class ParallaxContainer : Container, IRequireHighFrequencyMousePosition + { + public const float DEFAULT_PARALLAX_AMOUNT = 0.02f; + + public float ParallaxAmount = DEFAULT_PARALLAX_AMOUNT; + + private Bindable parallaxEnabled; + + public ParallaxContainer() + { + RelativeSizeAxes = Axes.Both; + AddInternal(content = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + } + + private readonly Container content; + private InputManager input; + + protected override Container Content => content; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + parallaxEnabled = config.GetBindable(OsuSetting.MenuParallax); + parallaxEnabled.ValueChanged += delegate + { + if (!parallaxEnabled) + { + content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint); + content.Scale = new Vector2(1 + ParallaxAmount); + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + input = GetContainingInputManager(); + } + + private bool firstUpdate = true; + + protected override void Update() + { + base.Update(); + + if (parallaxEnabled) + { + Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount; + + double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000); + + content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint); + content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint); + } + + firstUpdate = false; + } + } +} diff --git a/osu.Game/Graphics/Containers/ReverseChildIDFillFlowContainer.cs b/osu.Game/Graphics/Containers/ReverseChildIDFillFlowContainer.cs index 5803c8a5db..e6dcb336e7 100644 --- a/osu.Game/Graphics/Containers/ReverseChildIDFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/ReverseChildIDFillFlowContainer.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Graphics.Containers -{ - public class ReverseChildIDFillFlowContainer : FillFlowContainer where T : Drawable - { - protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Graphics.Containers +{ + public class ReverseChildIDFillFlowContainer : FillFlowContainer where T : Drawable + { + protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); + } +} diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index acb74588fd..36fdbe6e94 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -1,194 +1,194 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Graphics.Containers -{ - /// - /// A container that can scroll to each section inside it. - /// - public class SectionsContainer : Container - where T : Drawable - { - private Drawable expandableHeader, fixedHeader, footer, headerBackground; - private readonly ScrollContainer scrollContainer; - private readonly Container headerBackgroundContainer; - private readonly FlowContainer scrollContentContainer; - - protected override Container Content => scrollContentContainer; - - public Drawable ExpandableHeader - { - get { return expandableHeader; } - set - { - if (value == expandableHeader) return; - - expandableHeader?.Expire(); - expandableHeader = value; - if (value == null) return; - - AddInternal(expandableHeader); - lastKnownScroll = float.NaN; - } - } - - public Drawable FixedHeader - { - get { return fixedHeader; } - set - { - if (value == fixedHeader) return; - - fixedHeader?.Expire(); - fixedHeader = value; - if (value == null) return; - - AddInternal(fixedHeader); - lastKnownScroll = float.NaN; - } - } - - public Drawable Footer - { - get { return footer; } - set - { - if (value == footer) return; - - if (footer != null) - scrollContainer.Remove(footer); - footer = value; - if (value == null) return; - - footer.Anchor |= Anchor.y2; - footer.Origin |= Anchor.y2; - scrollContainer.Add(footer); - lastKnownScroll = float.NaN; - } - } - - public Drawable HeaderBackground - { - get { return headerBackground; } - set - { - if (value == headerBackground) return; - - headerBackgroundContainer.Clear(); - headerBackground = value; - if (value == null) return; - - headerBackgroundContainer.Add(headerBackground); - - lastKnownScroll = float.NaN; - } - } - - public Bindable SelectedSection { get; } = new Bindable(); - - protected virtual FlowContainer CreateScrollContentContainer() - => new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }; - - public override void Add(T drawable) - { - base.Add(drawable); - lastKnownScroll = float.NaN; - headerHeight = float.NaN; - footerHeight = float.NaN; - } - - private float headerHeight, footerHeight; - private readonly MarginPadding originalSectionsMargin; - private void updateSectionsMargin() - { - if (!Children.Any()) return; - - var newMargin = originalSectionsMargin; - newMargin.Top += headerHeight; - newMargin.Bottom += footerHeight; - - scrollContentContainer.Margin = newMargin; - } - - public SectionsContainer() - { - AddInternal(scrollContainer = new ScrollContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - ScrollbarVisible = false, - Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() } - }); - AddInternal(headerBackgroundContainer = new Container - { - RelativeSizeAxes = Axes.X - }); - originalSectionsMargin = scrollContentContainer.Margin; - } - - public void ScrollTo(Drawable section) => scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - (FixedHeader?.BoundingBox.Height ?? 0)); - - public void ScrollToTop() => scrollContainer.ScrollTo(0); - - private float lastKnownScroll; - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - float headerH = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); - float footerH = Footer?.LayoutSize.Y ?? 0; - if (headerH != headerHeight || footerH != footerHeight) - { - headerHeight = headerH; - footerHeight = footerH; - updateSectionsMargin(); - } - - float currentScroll = scrollContainer.Current; - - if (currentScroll != lastKnownScroll) - { - lastKnownScroll = currentScroll; - - if (ExpandableHeader != null && FixedHeader != null) - { - float offset = Math.Min(ExpandableHeader.LayoutSize.Y, currentScroll); - - ExpandableHeader.Y = -offset; - FixedHeader.Y = -offset + ExpandableHeader.LayoutSize.Y; - } - - headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); - headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; - - T bestMatch = null; - float minDiff = float.MaxValue; - float scrollOffset = FixedHeader?.LayoutSize.Y ?? 0; - - foreach (var section in Children) - { - float diff = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset); - if (diff < minDiff) - { - minDiff = diff; - bestMatch = section; - } - } - - if (bestMatch != null) - SelectedSection.Value = bestMatch; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Graphics.Containers +{ + /// + /// A container that can scroll to each section inside it. + /// + public class SectionsContainer : Container + where T : Drawable + { + private Drawable expandableHeader, fixedHeader, footer, headerBackground; + private readonly ScrollContainer scrollContainer; + private readonly Container headerBackgroundContainer; + private readonly FlowContainer scrollContentContainer; + + protected override Container Content => scrollContentContainer; + + public Drawable ExpandableHeader + { + get { return expandableHeader; } + set + { + if (value == expandableHeader) return; + + expandableHeader?.Expire(); + expandableHeader = value; + if (value == null) return; + + AddInternal(expandableHeader); + lastKnownScroll = float.NaN; + } + } + + public Drawable FixedHeader + { + get { return fixedHeader; } + set + { + if (value == fixedHeader) return; + + fixedHeader?.Expire(); + fixedHeader = value; + if (value == null) return; + + AddInternal(fixedHeader); + lastKnownScroll = float.NaN; + } + } + + public Drawable Footer + { + get { return footer; } + set + { + if (value == footer) return; + + if (footer != null) + scrollContainer.Remove(footer); + footer = value; + if (value == null) return; + + footer.Anchor |= Anchor.y2; + footer.Origin |= Anchor.y2; + scrollContainer.Add(footer); + lastKnownScroll = float.NaN; + } + } + + public Drawable HeaderBackground + { + get { return headerBackground; } + set + { + if (value == headerBackground) return; + + headerBackgroundContainer.Clear(); + headerBackground = value; + if (value == null) return; + + headerBackgroundContainer.Add(headerBackground); + + lastKnownScroll = float.NaN; + } + } + + public Bindable SelectedSection { get; } = new Bindable(); + + protected virtual FlowContainer CreateScrollContentContainer() + => new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }; + + public override void Add(T drawable) + { + base.Add(drawable); + lastKnownScroll = float.NaN; + headerHeight = float.NaN; + footerHeight = float.NaN; + } + + private float headerHeight, footerHeight; + private readonly MarginPadding originalSectionsMargin; + private void updateSectionsMargin() + { + if (!Children.Any()) return; + + var newMargin = originalSectionsMargin; + newMargin.Top += headerHeight; + newMargin.Bottom += footerHeight; + + scrollContentContainer.Margin = newMargin; + } + + public SectionsContainer() + { + AddInternal(scrollContainer = new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + ScrollbarVisible = false, + Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() } + }); + AddInternal(headerBackgroundContainer = new Container + { + RelativeSizeAxes = Axes.X + }); + originalSectionsMargin = scrollContentContainer.Margin; + } + + public void ScrollTo(Drawable section) => scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - (FixedHeader?.BoundingBox.Height ?? 0)); + + public void ScrollToTop() => scrollContainer.ScrollTo(0); + + private float lastKnownScroll; + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + float headerH = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); + float footerH = Footer?.LayoutSize.Y ?? 0; + if (headerH != headerHeight || footerH != footerHeight) + { + headerHeight = headerH; + footerHeight = footerH; + updateSectionsMargin(); + } + + float currentScroll = scrollContainer.Current; + + if (currentScroll != lastKnownScroll) + { + lastKnownScroll = currentScroll; + + if (ExpandableHeader != null && FixedHeader != null) + { + float offset = Math.Min(ExpandableHeader.LayoutSize.Y, currentScroll); + + ExpandableHeader.Y = -offset; + FixedHeader.Y = -offset + ExpandableHeader.LayoutSize.Y; + } + + headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); + headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; + + T bestMatch = null; + float minDiff = float.MaxValue; + float scrollOffset = FixedHeader?.LayoutSize.Y ?? 0; + + foreach (var section in Children) + { + float diff = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset); + if (diff < minDiff) + { + minDiff = diff; + bestMatch = section; + } + } + + if (bestMatch != null) + SelectedSection.Value = bestMatch; + } + } + } +} diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs index 0fae4579fa..81ae3198c7 100644 --- a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs +++ b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs @@ -1,67 +1,67 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Input; - -namespace osu.Game.Graphics.Cursor -{ - /// - /// A container which provides a which can be overridden by hovered s. - /// - public class CursorOverrideContainer : Container, IProvideCursor - { - protected override Container Content => content; - private readonly Container content; - - /// - /// Whether any cursors can be displayed. - /// - public bool CanShowCursor = true; - - public CursorContainer Cursor { get; } - public bool ProvidingUserCursor => true; - - public CursorOverrideContainer() - { - AddRangeInternal(new Drawable[] - { - Cursor = new MenuCursor { State = Visibility.Hidden }, - content = new Container { RelativeSizeAxes = Axes.Both } - }); - } - - private InputManager inputManager; - - protected override void LoadComplete() - { - base.LoadComplete(); - inputManager = GetContainingInputManager(); - } - - private IProvideCursor currentTarget; - protected override void Update() - { - base.Update(); - - if (!CanShowCursor) - { - currentTarget?.Cursor?.Hide(); - return; - } - - var newTarget = inputManager.HoveredDrawables.OfType().FirstOrDefault(t => t.ProvidingUserCursor) ?? this; - - if (currentTarget == newTarget) - return; - - currentTarget?.Cursor?.Hide(); - newTarget.Cursor?.Show(); - - currentTarget = newTarget; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Input; + +namespace osu.Game.Graphics.Cursor +{ + /// + /// A container which provides a which can be overridden by hovered s. + /// + public class CursorOverrideContainer : Container, IProvideCursor + { + protected override Container Content => content; + private readonly Container content; + + /// + /// Whether any cursors can be displayed. + /// + public bool CanShowCursor = true; + + public CursorContainer Cursor { get; } + public bool ProvidingUserCursor => true; + + public CursorOverrideContainer() + { + AddRangeInternal(new Drawable[] + { + Cursor = new MenuCursor { State = Visibility.Hidden }, + content = new Container { RelativeSizeAxes = Axes.Both } + }); + } + + private InputManager inputManager; + + protected override void LoadComplete() + { + base.LoadComplete(); + inputManager = GetContainingInputManager(); + } + + private IProvideCursor currentTarget; + protected override void Update() + { + base.Update(); + + if (!CanShowCursor) + { + currentTarget?.Cursor?.Hide(); + return; + } + + var newTarget = inputManager.HoveredDrawables.OfType().FirstOrDefault(t => t.ProvidingUserCursor) ?? this; + + if (currentTarget == newTarget) + return; + + currentTarget?.Cursor?.Hide(); + newTarget.Cursor?.Show(); + + currentTarget = newTarget; + } + } +} diff --git a/osu.Game/Graphics/Cursor/IProvideCursor.cs b/osu.Game/Graphics/Cursor/IProvideCursor.cs index 91b44234fb..c7b23bbc00 100644 --- a/osu.Game/Graphics/Cursor/IProvideCursor.cs +++ b/osu.Game/Graphics/Cursor/IProvideCursor.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Cursor; - -namespace osu.Game.Graphics.Cursor -{ - /// - /// Interface for s that display cursors which can replace the user's cursor. - /// - public interface IProvideCursor : IDrawable - { - /// - /// The cursor provided by this . - /// May be null if no cursor should be visible. - /// - CursorContainer Cursor { get; } - - /// - /// Whether should be displayed as the singular user cursor. This will temporarily hide any other user cursor. - /// This value is checked every frame and may be used to control whether multiple cursors are displayed (e.g. watching replays). - /// - bool ProvidingUserCursor { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; + +namespace osu.Game.Graphics.Cursor +{ + /// + /// Interface for s that display cursors which can replace the user's cursor. + /// + public interface IProvideCursor : IDrawable + { + /// + /// The cursor provided by this . + /// May be null if no cursor should be visible. + /// + CursorContainer Cursor { get; } + + /// + /// Whether should be displayed as the singular user cursor. This will temporarily hide any other user cursor. + /// This value is checked every frame and may be used to control whether multiple cursors are displayed (e.g. watching replays). + /// + bool ProvidingUserCursor { get; } + } +} diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index bdee7d289d..34d815ec14 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -1,157 +1,157 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Game.Configuration; -using System; -using System.Diagnostics; -using osu.Framework.Graphics.Textures; - -namespace osu.Game.Graphics.Cursor -{ - public class MenuCursor : CursorContainer - { - protected override Drawable CreateCursor() => new Cursor(); - - private Bindable cursorRotate; - private bool dragging; - - private bool startRotation; - - protected override bool OnMouseMove(InputState state) - { - if (cursorRotate && dragging) - { - Debug.Assert(state.Mouse.PositionMouseDown != null); - - // don't start rotating until we're moved a minimum distance away from the mouse down location, - // else it can have an annoying effect. - // ReSharper disable once PossibleInvalidOperationException - startRotation |= Vector2Extensions.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30; - - if (startRotation) - { - Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown.Value; - float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f; - - // Always rotate in the direction of least distance - float diff = (degrees - ActiveCursor.Rotation) % 360; - if (diff < -180) diff += 360; - if (diff > 180) diff -= 360; - degrees = ActiveCursor.Rotation + diff; - - ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint); - } - } - - return base.OnMouseMove(state); - } - - protected override bool OnDragStart(InputState state) - { - dragging = true; - return base.OnDragStart(state); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - ActiveCursor.Scale = new Vector2(1); - ActiveCursor.ScaleTo(0.90f, 800, Easing.OutQuint); - - ((Cursor)ActiveCursor).AdditiveLayer.Alpha = 0; - ((Cursor)ActiveCursor).AdditiveLayer.FadeInFromZero(800, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - if (!state.Mouse.HasMainButtonPressed) - { - dragging = false; - startRotation = false; - - ((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint); - ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf); - ActiveCursor.ScaleTo(1, 500, Easing.OutElastic); - } - - return base.OnMouseUp(state, args); - } - - protected override bool OnClick(InputState state) - { - ((Cursor)ActiveCursor).AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint); - - return base.OnClick(state); - } - - protected override void PopIn() - { - ActiveCursor.FadeTo(1, 250, Easing.OutQuint); - ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); - } - - protected override void PopOut() - { - ActiveCursor.FadeTo(0, 250, Easing.OutQuint); - ActiveCursor.ScaleTo(0.6f, 250, Easing.In); - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - cursorRotate = config.GetBindable(OsuSetting.CursorRotation); - } - - public class Cursor : Container - { - private Container cursorContainer; - private Bindable cursorScale; - private const float base_scale = 0.15f; - - public Sprite AdditiveLayer; - - public Cursor() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config, TextureStore textures, OsuColour colour) - { - Children = new Drawable[] - { - cursorContainer = new Container - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Sprite - { - Texture = textures.Get(@"Cursor/menu-cursor"), - }, - AdditiveLayer = new Sprite - { - Blending = BlendingMode.Additive, - Colour = colour.Pink, - Alpha = 0, - Texture = textures.Get(@"Cursor/menu-cursor-additive"), - }, - } - } - }; - - cursorScale = config.GetBindable(OsuSetting.MenuCursorSize); - cursorScale.ValueChanged += newScale => cursorContainer.Scale = new Vector2((float)newScale * base_scale); - cursorScale.TriggerChange(); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Configuration; +using System; +using System.Diagnostics; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Graphics.Cursor +{ + public class MenuCursor : CursorContainer + { + protected override Drawable CreateCursor() => new Cursor(); + + private Bindable cursorRotate; + private bool dragging; + + private bool startRotation; + + protected override bool OnMouseMove(InputState state) + { + if (cursorRotate && dragging) + { + Debug.Assert(state.Mouse.PositionMouseDown != null); + + // don't start rotating until we're moved a minimum distance away from the mouse down location, + // else it can have an annoying effect. + // ReSharper disable once PossibleInvalidOperationException + startRotation |= Vector2Extensions.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30; + + if (startRotation) + { + Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown.Value; + float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f; + + // Always rotate in the direction of least distance + float diff = (degrees - ActiveCursor.Rotation) % 360; + if (diff < -180) diff += 360; + if (diff > 180) diff -= 360; + degrees = ActiveCursor.Rotation + diff; + + ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint); + } + } + + return base.OnMouseMove(state); + } + + protected override bool OnDragStart(InputState state) + { + dragging = true; + return base.OnDragStart(state); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + ActiveCursor.Scale = new Vector2(1); + ActiveCursor.ScaleTo(0.90f, 800, Easing.OutQuint); + + ((Cursor)ActiveCursor).AdditiveLayer.Alpha = 0; + ((Cursor)ActiveCursor).AdditiveLayer.FadeInFromZero(800, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + if (!state.Mouse.HasMainButtonPressed) + { + dragging = false; + startRotation = false; + + ((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint); + ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf); + ActiveCursor.ScaleTo(1, 500, Easing.OutElastic); + } + + return base.OnMouseUp(state, args); + } + + protected override bool OnClick(InputState state) + { + ((Cursor)ActiveCursor).AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint); + + return base.OnClick(state); + } + + protected override void PopIn() + { + ActiveCursor.FadeTo(1, 250, Easing.OutQuint); + ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); + } + + protected override void PopOut() + { + ActiveCursor.FadeTo(0, 250, Easing.OutQuint); + ActiveCursor.ScaleTo(0.6f, 250, Easing.In); + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + cursorRotate = config.GetBindable(OsuSetting.CursorRotation); + } + + public class Cursor : Container + { + private Container cursorContainer; + private Bindable cursorScale; + private const float base_scale = 0.15f; + + public Sprite AdditiveLayer; + + public Cursor() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, TextureStore textures, OsuColour colour) + { + Children = new Drawable[] + { + cursorContainer = new Container + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Sprite + { + Texture = textures.Get(@"Cursor/menu-cursor"), + }, + AdditiveLayer = new Sprite + { + Blending = BlendingMode.Additive, + Colour = colour.Pink, + Alpha = 0, + Texture = textures.Get(@"Cursor/menu-cursor-additive"), + }, + } + } + }; + + cursorScale = config.GetBindable(OsuSetting.MenuCursorSize); + cursorScale.ValueChanged += newScale => cursorContainer.Scale = new Vector2((float)newScale * base_scale); + cursorScale.TriggerChange(); + } + } + } +} diff --git a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs index d8910c5398..9408d63573 100644 --- a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Graphics.Cursor -{ - public class OsuContextMenuContainer : ContextMenuContainer - { - protected override Menu CreateMenu() => new OsuContextMenu(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.Cursor +{ + public class OsuContextMenuContainer : ContextMenuContainer + { + protected override Menu CreateMenu() => new OsuContextMenu(); + } +} diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index 3706f31ded..c0e331148d 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -1,105 +1,105 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Graphics.Cursor -{ - public class OsuTooltipContainer : TooltipContainer - { - protected override ITooltip CreateTooltip() => new OsuTooltip(); - - public OsuTooltipContainer(CursorContainer cursor) : base(cursor) - { - } - - public class OsuTooltip : Tooltip - { - private readonly Box background; - private readonly OsuSpriteText text; - private bool instantMovement = true; - - public override string TooltipText - { - set - { - if (value == text.Text) return; - - text.Text = value; - if (IsPresent) - { - AutoSizeDuration = 250; - background.FlashColour(OsuColour.Gray(0.4f), 1000, Easing.OutQuint); - } - else - AutoSizeDuration = 0; - } - } - - private const float text_size = 16; - - public OsuTooltip() - { - AutoSizeEasing = Easing.OutQuint; - - CornerRadius = 5; - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(40), - Radius = 5, - }; - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.9f, - }, - text = new OsuSpriteText - { - TextSize = text_size, - Padding = new MarginPadding(5), - Font = @"Exo2.0-Regular", - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - background.Colour = colour.Gray3; - } - - protected override void PopIn() - { - instantMovement |= !IsPresent; - this.FadeIn(500, Easing.OutQuint); - } - - protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint); - - public override void Move(Vector2 pos) - { - if (instantMovement) - { - Position = pos; - instantMovement = false; - } - else - { - this.MoveTo(pos, 200, Easing.OutQuint); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.Cursor +{ + public class OsuTooltipContainer : TooltipContainer + { + protected override ITooltip CreateTooltip() => new OsuTooltip(); + + public OsuTooltipContainer(CursorContainer cursor) : base(cursor) + { + } + + public class OsuTooltip : Tooltip + { + private readonly Box background; + private readonly OsuSpriteText text; + private bool instantMovement = true; + + public override string TooltipText + { + set + { + if (value == text.Text) return; + + text.Text = value; + if (IsPresent) + { + AutoSizeDuration = 250; + background.FlashColour(OsuColour.Gray(0.4f), 1000, Easing.OutQuint); + } + else + AutoSizeDuration = 0; + } + } + + private const float text_size = 16; + + public OsuTooltip() + { + AutoSizeEasing = Easing.OutQuint; + + CornerRadius = 5; + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(40), + Radius = 5, + }; + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.9f, + }, + text = new OsuSpriteText + { + TextSize = text_size, + Padding = new MarginPadding(5), + Font = @"Exo2.0-Regular", + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + background.Colour = colour.Gray3; + } + + protected override void PopIn() + { + instantMovement |= !IsPresent; + this.FadeIn(500, Easing.OutQuint); + } + + protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint); + + public override void Move(Vector2 pos) + { + if (instantMovement) + { + Position = pos; + instantMovement = false; + } + else + { + this.MoveTo(pos, 200, Easing.OutQuint); + } + } + } + } +} diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index d2a7250aaa..763e57e397 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -1,65 +1,65 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using Humanizer; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Cursor; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Graphics -{ - public class DrawableDate : OsuSpriteText, IHasTooltip - { - private readonly DateTimeOffset date; - - public DrawableDate(DateTimeOffset date) - { - AutoSizeAxes = Axes.Both; - Font = "Exo2.0-RegularItalic"; - - this.date = date.ToLocalTime(); - } - - [BackgroundDependencyLoader] - private void load() - { - updateTime(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Scheduler.Add(updateTimeWithReschedule); - } - - private void updateTimeWithReschedule() - { - updateTime(); - - var diffToNow = DateTimeOffset.Now.Subtract(date); - - double timeUntilNextUpdate = 1000; - if (diffToNow.TotalSeconds > 60) - { - timeUntilNextUpdate *= 60; - if (diffToNow.TotalMinutes > 60) - { - timeUntilNextUpdate *= 60; - - if (diffToNow.TotalHours > 24) - timeUntilNextUpdate *= 24; - } - } - - Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate); - } - - public override bool HandleMouseInput => true; - - private void updateTime() => Text = date.Humanize(); - - public string TooltipText => date.ToString(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using Humanizer; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics +{ + public class DrawableDate : OsuSpriteText, IHasTooltip + { + private readonly DateTimeOffset date; + + public DrawableDate(DateTimeOffset date) + { + AutoSizeAxes = Axes.Both; + Font = "Exo2.0-RegularItalic"; + + this.date = date.ToLocalTime(); + } + + [BackgroundDependencyLoader] + private void load() + { + updateTime(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Scheduler.Add(updateTimeWithReschedule); + } + + private void updateTimeWithReschedule() + { + updateTime(); + + var diffToNow = DateTimeOffset.Now.Subtract(date); + + double timeUntilNextUpdate = 1000; + if (diffToNow.TotalSeconds > 60) + { + timeUntilNextUpdate *= 60; + if (diffToNow.TotalMinutes > 60) + { + timeUntilNextUpdate *= 60; + + if (diffToNow.TotalHours > 24) + timeUntilNextUpdate *= 24; + } + } + + Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate); + } + + public override bool HandleMouseInput => true; + + private void updateTime() => Text = date.Humanize(); + + public string TooltipText => date.ToString(); + } +} diff --git a/osu.Game/Graphics/IHasAccentColour.cs b/osu.Game/Graphics/IHasAccentColour.cs index 84120764d1..64c240aa84 100644 --- a/osu.Game/Graphics/IHasAccentColour.cs +++ b/osu.Game/Graphics/IHasAccentColour.cs @@ -1,38 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Transforms; - -namespace osu.Game.Graphics -{ - /// - /// A type of drawable that has an accent colour. - /// The accent colour is used to colorize various objects inside a drawable - /// without colorizing the drawable itself. - /// - public interface IHasAccentColour : IDrawable - { - Color4 AccentColour { get; set; } - } - - public static class AccentedColourExtensions - { - /// - /// Smoothly adjusts over time. - /// - /// A to which further transforms can be added. - public static TransformSequence FadeAccent(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None) - where T : IHasAccentColour - => accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing); - - /// - /// Smoothly adjusts over time. - /// - /// A to which further transforms can be added. - public static TransformSequence FadeAccent(this TransformSequence t, Color4 newColour, double duration = 0, Easing easing = Easing.None) - where T : Drawable, IHasAccentColour - => t.Append(o => o.FadeAccent(newColour, duration, easing)); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Graphics +{ + /// + /// A type of drawable that has an accent colour. + /// The accent colour is used to colorize various objects inside a drawable + /// without colorizing the drawable itself. + /// + public interface IHasAccentColour : IDrawable + { + Color4 AccentColour { get; set; } + } + + public static class AccentedColourExtensions + { + /// + /// Smoothly adjusts over time. + /// + /// A to which further transforms can be added. + public static TransformSequence FadeAccent(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None) + where T : IHasAccentColour + => accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing); + + /// + /// Smoothly adjusts over time. + /// + /// A to which further transforms can be added. + public static TransformSequence FadeAccent(this TransformSequence t, Color4 newColour, double duration = 0, Easing easing = Easing.None) + where T : Drawable, IHasAccentColour + => t.Append(o => o.FadeAccent(newColour, duration, easing)); + } +} diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 8e24807eaa..7236248f18 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -1,96 +1,96 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK.Graphics; - -namespace osu.Game.Graphics -{ - public class OsuColour - { - public static Color4 Gray(float amt) => new Color4(amt, amt, amt, 1f); - public static Color4 Gray(byte amt) => new Color4(amt, amt, amt, 255); - - public static Color4 FromHex(string hex) - { - if (hex[0] == '#') - hex = hex.Substring(1); - - switch (hex.Length) - { - default: - throw new ArgumentException(@"Invalid hex string length!"); - case 3: - 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), - 255); - case 6: - return new Color4( - Convert.ToByte(hex.Substring(0, 2), 16), - Convert.ToByte(hex.Substring(2, 2), 16), - Convert.ToByte(hex.Substring(4, 2), 16), - 255); - } - } - - // 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"); - public readonly Color4 Purple = FromHex(@"8866ee"); - public readonly Color4 PurpleDark = FromHex(@"6644cc"); - public readonly Color4 PurpleDarker = FromHex(@"441188"); - - public readonly Color4 PinkLighter = FromHex(@"ffddee"); - public readonly Color4 PinkLight = FromHex(@"ff99cc"); - public readonly Color4 Pink = FromHex(@"ff66aa"); - public readonly Color4 PinkDark = FromHex(@"cc5288"); - public readonly Color4 PinkDarker = FromHex(@"bb1177"); - - public readonly Color4 BlueLighter = FromHex(@"ddffff"); - public readonly Color4 BlueLight = FromHex(@"99eeff"); - public readonly Color4 Blue = FromHex(@"66ccff"); - public readonly Color4 BlueDark = FromHex(@"44aadd"); - public readonly Color4 BlueDarker = FromHex(@"2299bb"); - - public readonly Color4 YellowLighter = FromHex(@"ffffdd"); - public readonly Color4 YellowLight = FromHex(@"ffdd55"); - public readonly Color4 Yellow = FromHex(@"ffcc22"); - public readonly Color4 YellowDark = FromHex(@"eeaa00"); - public readonly Color4 YellowDarker = FromHex(@"cc6600"); - - public readonly Color4 GreenLighter = FromHex(@"eeffcc"); - public readonly Color4 GreenLight = FromHex(@"b3d944"); - public readonly Color4 Green = FromHex(@"88b300"); - public readonly Color4 GreenDark = FromHex(@"668800"); - public readonly Color4 GreenDarker = FromHex(@"445500"); - - public readonly Color4 Gray0 = FromHex(@"000"); - public readonly Color4 Gray1 = FromHex(@"111"); - public readonly Color4 Gray2 = FromHex(@"222"); - public readonly Color4 Gray3 = FromHex(@"333"); - public readonly Color4 Gray4 = FromHex(@"444"); - public readonly Color4 Gray5 = FromHex(@"555"); - public readonly Color4 Gray6 = FromHex(@"666"); - public readonly Color4 Gray7 = FromHex(@"777"); - public readonly Color4 Gray8 = FromHex(@"888"); - public readonly Color4 Gray9 = FromHex(@"999"); - public readonly Color4 GrayA = FromHex(@"aaa"); - public readonly Color4 GrayB = FromHex(@"bbb"); - public readonly Color4 GrayC = FromHex(@"ccc"); - public readonly Color4 GrayD = FromHex(@"ddd"); - public readonly Color4 GrayE = FromHex(@"eee"); - public readonly Color4 GrayF = FromHex(@"fff"); - - public readonly Color4 RedLighter = FromHex(@"ffeded"); - public readonly Color4 RedLight = FromHex(@"ed7787"); - public readonly Color4 Red = FromHex(@"ed1121"); - public readonly Color4 RedDark = FromHex(@"ba0011"); - public readonly Color4 RedDarker = FromHex(@"870000"); - - public readonly Color4 ChatBlue = FromHex(@"17292e"); - - public readonly Color4 ContextMenuGray = FromHex(@"223034"); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK.Graphics; + +namespace osu.Game.Graphics +{ + public class OsuColour + { + public static Color4 Gray(float amt) => new Color4(amt, amt, amt, 1f); + public static Color4 Gray(byte amt) => new Color4(amt, amt, amt, 255); + + public static Color4 FromHex(string hex) + { + if (hex[0] == '#') + hex = hex.Substring(1); + + switch (hex.Length) + { + default: + throw new ArgumentException(@"Invalid hex string length!"); + case 3: + 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), + 255); + case 6: + return new Color4( + Convert.ToByte(hex.Substring(0, 2), 16), + Convert.ToByte(hex.Substring(2, 2), 16), + Convert.ToByte(hex.Substring(4, 2), 16), + 255); + } + } + + // 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"); + public readonly Color4 Purple = FromHex(@"8866ee"); + public readonly Color4 PurpleDark = FromHex(@"6644cc"); + public readonly Color4 PurpleDarker = FromHex(@"441188"); + + public readonly Color4 PinkLighter = FromHex(@"ffddee"); + public readonly Color4 PinkLight = FromHex(@"ff99cc"); + public readonly Color4 Pink = FromHex(@"ff66aa"); + public readonly Color4 PinkDark = FromHex(@"cc5288"); + public readonly Color4 PinkDarker = FromHex(@"bb1177"); + + public readonly Color4 BlueLighter = FromHex(@"ddffff"); + public readonly Color4 BlueLight = FromHex(@"99eeff"); + public readonly Color4 Blue = FromHex(@"66ccff"); + public readonly Color4 BlueDark = FromHex(@"44aadd"); + public readonly Color4 BlueDarker = FromHex(@"2299bb"); + + public readonly Color4 YellowLighter = FromHex(@"ffffdd"); + public readonly Color4 YellowLight = FromHex(@"ffdd55"); + public readonly Color4 Yellow = FromHex(@"ffcc22"); + public readonly Color4 YellowDark = FromHex(@"eeaa00"); + public readonly Color4 YellowDarker = FromHex(@"cc6600"); + + public readonly Color4 GreenLighter = FromHex(@"eeffcc"); + public readonly Color4 GreenLight = FromHex(@"b3d944"); + public readonly Color4 Green = FromHex(@"88b300"); + public readonly Color4 GreenDark = FromHex(@"668800"); + public readonly Color4 GreenDarker = FromHex(@"445500"); + + public readonly Color4 Gray0 = FromHex(@"000"); + public readonly Color4 Gray1 = FromHex(@"111"); + public readonly Color4 Gray2 = FromHex(@"222"); + public readonly Color4 Gray3 = FromHex(@"333"); + public readonly Color4 Gray4 = FromHex(@"444"); + public readonly Color4 Gray5 = FromHex(@"555"); + public readonly Color4 Gray6 = FromHex(@"666"); + public readonly Color4 Gray7 = FromHex(@"777"); + public readonly Color4 Gray8 = FromHex(@"888"); + public readonly Color4 Gray9 = FromHex(@"999"); + public readonly Color4 GrayA = FromHex(@"aaa"); + public readonly Color4 GrayB = FromHex(@"bbb"); + public readonly Color4 GrayC = FromHex(@"ccc"); + public readonly Color4 GrayD = FromHex(@"ddd"); + public readonly Color4 GrayE = FromHex(@"eee"); + public readonly Color4 GrayF = FromHex(@"fff"); + + public readonly Color4 RedLighter = FromHex(@"ffeded"); + public readonly Color4 RedLight = FromHex(@"ed7787"); + public readonly Color4 Red = FromHex(@"ed1121"); + public readonly Color4 RedDark = FromHex(@"ba0011"); + public readonly Color4 RedDarker = FromHex(@"870000"); + + public readonly Color4 ChatBlue = FromHex(@"17292e"); + + public readonly Color4 ContextMenuGray = FromHex(@"223034"); + } +} diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index b0cd997837..5e0b9c9340 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -1,110 +1,110 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Drawing.Imaging; -using System.IO; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Configuration; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.Input.Bindings; -using osu.Framework.Platform; -using osu.Game.Configuration; -using osu.Game.Input.Bindings; -using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; - -namespace osu.Game.Graphics -{ - public class ScreenshotManager : Container, IKeyBindingHandler, IHandleGlobalInput - { - private Bindable screenshotFormat; - private GameHost host; - private Storage storage; - private NotificationOverlay notificationOverlay; - - private SampleChannel shutter; - - [BackgroundDependencyLoader] - private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio) - { - this.host = host; - this.storage = storage.GetStorageForDirectory(@"screenshots"); - this.notificationOverlay = notificationOverlay; - - screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); - - shutter = audio.Sample.Get("UI/shutter"); - } - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.TakeScreenshot: - shutter.Play(); - TakeScreenshotAsync(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => false; - - public async void TakeScreenshotAsync() - { - using (var bitmap = await host.TakeScreenshotAsync()) - { - var fileName = getFileName(); - if (fileName == null) return; - - var stream = storage.GetStream(fileName, FileAccess.Write); - - switch (screenshotFormat.Value) - { - case ScreenshotFormat.Png: - bitmap.Save(stream, ImageFormat.Png); - break; - case ScreenshotFormat.Jpg: - bitmap.Save(stream, ImageFormat.Jpeg); - break; - default: - throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); - } - - notificationOverlay.Post(new SimpleNotification - { - Text = $"{fileName} saved!", - Activated = () => - { - storage.OpenInNativeExplorer(); - return true; - } - }); - } - } - - private string getFileName() - { - var dt = DateTime.Now; - var fileExt = screenshotFormat.ToString().ToLower(); - - var withoutIndex = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}.{fileExt}"; - if (!storage.Exists(withoutIndex)) - return withoutIndex; - - for (ulong i = 1; i < ulong.MaxValue; i++) - { - var indexedName = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}-{i}.{fileExt}"; - if (!storage.Exists(indexedName)) - return indexedName; - } - - return null; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Drawing.Imaging; +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Platform; +using osu.Game.Configuration; +using osu.Game.Input.Bindings; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Graphics +{ + public class ScreenshotManager : Container, IKeyBindingHandler, IHandleGlobalInput + { + private Bindable screenshotFormat; + private GameHost host; + private Storage storage; + private NotificationOverlay notificationOverlay; + + private SampleChannel shutter; + + [BackgroundDependencyLoader] + private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio) + { + this.host = host; + this.storage = storage.GetStorageForDirectory(@"screenshots"); + this.notificationOverlay = notificationOverlay; + + screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat); + + shutter = audio.Sample.Get("UI/shutter"); + } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.TakeScreenshot: + shutter.Play(); + TakeScreenshotAsync(); + return true; + } + + return false; + } + + public bool OnReleased(GlobalAction action) => false; + + public async void TakeScreenshotAsync() + { + using (var bitmap = await host.TakeScreenshotAsync()) + { + var fileName = getFileName(); + if (fileName == null) return; + + var stream = storage.GetStream(fileName, FileAccess.Write); + + switch (screenshotFormat.Value) + { + case ScreenshotFormat.Png: + bitmap.Save(stream, ImageFormat.Png); + break; + case ScreenshotFormat.Jpg: + bitmap.Save(stream, ImageFormat.Jpeg); + break; + default: + throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); + } + + notificationOverlay.Post(new SimpleNotification + { + Text = $"{fileName} saved!", + Activated = () => + { + storage.OpenInNativeExplorer(); + return true; + } + }); + } + } + + private string getFileName() + { + var dt = DateTime.Now; + var fileExt = screenshotFormat.ToString().ToLower(); + + var withoutIndex = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}.{fileExt}"; + if (!storage.Exists(withoutIndex)) + return withoutIndex; + + for (ulong i = 1; i < ulong.MaxValue; i++) + { + var indexedName = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}-{i}.{fileExt}"; + if (!storage.Exists(indexedName)) + return indexedName; + } + + return null; + } + } +} diff --git a/osu.Game/Graphics/SpriteIcon.cs b/osu.Game/Graphics/SpriteIcon.cs index 4324119481..6acd20719e 100644 --- a/osu.Game/Graphics/SpriteIcon.cs +++ b/osu.Game/Graphics/SpriteIcon.cs @@ -1,1009 +1,1009 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics; -using osu.Framework.IO.Stores; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Caching; - -namespace osu.Game.Graphics -{ - public class SpriteIcon : CompositeDrawable - { - private Sprite spriteShadow; - private Sprite spriteMain; - - private Cached layout = new Cached(); - private Container shadowVisibility; - - private FontStore store; - - [BackgroundDependencyLoader] - private void load(FontStore store) - { - this.store = store; - - InternalChildren = new Drawable[] - { - shadowVisibility = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Child = spriteShadow = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Y = 2, - Colour = new Color4(0f, 0f, 0f, 0.2f), - }, - Alpha = shadow ? 1 : 0, - }, - spriteMain = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit - }, - }; - - updateTexture(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - updateTexture(); - } - - private FontAwesome loadedIcon; - private void updateTexture() - { - var loadableIcon = icon; - - if (loadableIcon == loadedIcon) return; - - var texture = store?.Get(((char)loadableIcon).ToString()); - - spriteMain.Texture = texture; - spriteShadow.Texture = texture; - - if (Size == Vector2.Zero) - Size = new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0); - - loadedIcon = loadableIcon; - } - - public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) - { - if ((invalidation & Invalidation.Colour) > 0 && Shadow) - layout.Invalidate(); - return base.Invalidate(invalidation, source, shallPropagate); - } - - protected override void Update() - { - if (!layout.IsValid) - { - //adjust shadow alpha based on highest component intensity to avoid muddy display of darker text. - //squared result for quadratic fall-off seems to give the best result. - var avgColour = (Color4)DrawInfo.Colour.AverageColour; - - spriteShadow.Alpha = (float)Math.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2); - - layout.Validate(); - } - } - - private bool shadow; - public bool Shadow - { - get { return shadow; } - set - { - shadow = value; - if (shadowVisibility != null) - shadowVisibility.Alpha = value ? 1 : 0; - } - } - - private FontAwesome icon; - - public FontAwesome Icon - { - get - { - return icon; - } - - set - { - if (icon == value) return; - - icon = value; - if (IsLoaded) - updateTexture(); - } - } - } - - public enum FontAwesome - { - fa_500px = 0xf26e, - fa_address_book = 0xf2b9, - fa_address_book_o = 0xf2ba, - fa_address_card = 0xf2bb, - fa_address_card_o = 0xf2bc, - fa_adjust = 0xf042, - fa_adn = 0xf170, - fa_align_center = 0xf037, - fa_align_justify = 0xf039, - fa_align_left = 0xf036, - fa_align_right = 0xf038, - fa_amazon = 0xf270, - fa_ambulance = 0xf0f9, - fa_american_sign_language_interpreting = 0xf2a3, - fa_anchor = 0xf13d, - fa_android = 0xf17b, - fa_angellist = 0xf209, - fa_angle_double_down = 0xf103, - fa_angle_double_left = 0xf100, - fa_angle_double_right = 0xf101, - fa_angle_double_up = 0xf102, - fa_angle_down = 0xf107, - fa_angle_left = 0xf104, - fa_angle_right = 0xf105, - fa_angle_up = 0xf106, - fa_apple = 0xf179, - fa_archive = 0xf187, - fa_area_chart = 0xf1fe, - fa_arrow_circle_down = 0xf0ab, - fa_arrow_circle_left = 0xf0a8, - fa_arrow_circle_o_down = 0xf01a, - fa_arrow_circle_o_left = 0xf190, - fa_arrow_circle_o_right = 0xf18e, - fa_arrow_circle_o_up = 0xf01b, - fa_arrow_circle_right = 0xf0a9, - fa_arrow_circle_up = 0xf0aa, - fa_arrow_down = 0xf063, - fa_arrow_left = 0xf060, - fa_arrow_right = 0xf061, - fa_arrow_up = 0xf062, - fa_arrows = 0xf047, - fa_arrows_alt = 0xf0b2, - fa_arrows_h = 0xf07e, - fa_arrows_v = 0xf07d, - fa_asl_interpreting = 0xf2a3, - fa_assistive_listening_systems = 0xf2a2, - fa_asterisk = 0xf069, - fa_at = 0xf1fa, - fa_audio_description = 0xf29e, - fa_automobile = 0xf1b9, - fa_backward = 0xf04a, - fa_balance_scale = 0xf24e, - fa_ban = 0xf05e, - fa_bandcamp = 0xf2d5, - fa_bank = 0xf19c, - fa_bar_chart = 0xf080, - fa_bar_chart_o = 0xf080, - fa_barcode = 0xf02a, - fa_bars = 0xf0c9, - fa_bath = 0xf2cd, - fa_bathtub = 0xf2cd, - fa_battery = 0xf240, - fa_battery_0 = 0xf244, - fa_battery_1 = 0xf243, - fa_battery_2 = 0xf242, - fa_battery_3 = 0xf241, - fa_battery_4 = 0xf240, - fa_battery_empty = 0xf244, - fa_battery_full = 0xf240, - fa_battery_half = 0xf242, - fa_battery_quarter = 0xf243, - fa_battery_three_quarters = 0xf241, - fa_bed = 0xf236, - fa_beer = 0xf0fc, - fa_behance = 0xf1b4, - fa_behance_square = 0xf1b5, - fa_bell = 0xf0f3, - fa_bell_o = 0xf0a2, - fa_bell_slash = 0xf1f6, - fa_bell_slash_o = 0xf1f7, - fa_bicycle = 0xf206, - fa_binoculars = 0xf1e5, - fa_birthday_cake = 0xf1fd, - fa_bitbucket = 0xf171, - fa_bitbucket_square = 0xf172, - fa_bitcoin = 0xf15a, - fa_black_tie = 0xf27e, - fa_blind = 0xf29d, - fa_bluetooth = 0xf293, - fa_bluetooth_b = 0xf294, - fa_bold = 0xf032, - fa_bolt = 0xf0e7, - fa_bomb = 0xf1e2, - fa_book = 0xf02d, - fa_bookmark = 0xf02e, - fa_bookmark_o = 0xf097, - fa_braille = 0xf2a1, - fa_briefcase = 0xf0b1, - fa_btc = 0xf15a, - fa_bug = 0xf188, - fa_building = 0xf1ad, - fa_building_o = 0xf0f7, - fa_bullhorn = 0xf0a1, - fa_bullseye = 0xf140, - fa_bus = 0xf207, - fa_buysellads = 0xf20d, - fa_cab = 0xf1ba, - fa_calculator = 0xf1ec, - fa_calendar = 0xf073, - fa_calendar_check_o = 0xf274, - fa_calendar_minus_o = 0xf272, - fa_calendar_o = 0xf133, - fa_calendar_plus_o = 0xf271, - fa_calendar_times_o = 0xf273, - fa_camera = 0xf030, - fa_camera_retro = 0xf083, - fa_car = 0xf1b9, - fa_caret_down = 0xf0d7, - fa_caret_left = 0xf0d9, - fa_caret_right = 0xf0da, - fa_caret_square_o_down = 0xf150, - fa_caret_square_o_left = 0xf191, - fa_caret_square_o_right = 0xf152, - fa_caret_square_o_up = 0xf151, - fa_caret_up = 0xf0d8, - fa_cart_arrow_down = 0xf218, - fa_cart_plus = 0xf217, - fa_cc = 0xf20a, - fa_cc_amex = 0xf1f3, - fa_cc_diners_club = 0xf24c, - fa_cc_discover = 0xf1f2, - fa_cc_jcb = 0xf24b, - fa_cc_mastercard = 0xf1f1, - fa_cc_paypal = 0xf1f4, - fa_cc_stripe = 0xf1f5, - fa_cc_visa = 0xf1f0, - fa_certificate = 0xf0a3, - fa_chain = 0xf0c1, - fa_chain_broken = 0xf127, - fa_check = 0xf00c, - fa_check_circle = 0xf058, - fa_check_circle_o = 0xf05d, - fa_check_square = 0xf14a, - fa_check_square_o = 0xf046, - fa_chevron_circle_down = 0xf13a, - fa_chevron_circle_left = 0xf137, - fa_chevron_circle_right = 0xf138, - fa_chevron_circle_up = 0xf139, - fa_chevron_down = 0xf078, - fa_chevron_left = 0xf053, - fa_chevron_right = 0xf054, - fa_chevron_up = 0xf077, - fa_child = 0xf1ae, - fa_chrome = 0xf268, - fa_circle = 0xf111, - fa_circle_o = 0xf10c, - fa_circle_o_notch = 0xf1ce, - fa_circle_thin = 0xf1db, - fa_clipboard = 0xf0ea, - fa_clock_o = 0xf017, - fa_clone = 0xf24d, - fa_close = 0xf00d, - fa_cloud = 0xf0c2, - fa_cloud_download = 0xf0ed, - fa_cloud_upload = 0xf0ee, - fa_cny = 0xf157, - fa_code = 0xf121, - fa_code_fork = 0xf126, - fa_codepen = 0xf1cb, - fa_codiepie = 0xf284, - fa_coffee = 0xf0f4, - fa_cog = 0xf013, - fa_cogs = 0xf085, - fa_columns = 0xf0db, - fa_comment = 0xf075, - fa_comment_o = 0xf0e5, - fa_commenting = 0xf27a, - fa_commenting_o = 0xf27b, - fa_comments = 0xf086, - fa_comments_o = 0xf0e6, - fa_compass = 0xf14e, - fa_compress = 0xf066, - fa_connectdevelop = 0xf20e, - fa_contao = 0xf26d, - fa_copy = 0xf0c5, - fa_copyright = 0xf1f9, - fa_creative_commons = 0xf25e, - fa_credit_card = 0xf09d, - fa_credit_card_alt = 0xf283, - fa_crop = 0xf125, - fa_crosshairs = 0xf05b, - fa_css3 = 0xf13c, - fa_cube = 0xf1b2, - fa_cubes = 0xf1b3, - fa_cut = 0xf0c4, - fa_cutlery = 0xf0f5, - fa_dashboard = 0xf0e4, - fa_dashcube = 0xf210, - fa_database = 0xf1c0, - fa_deaf = 0xf2a4, - fa_deafness = 0xf2a4, - fa_dedent = 0xf03b, - fa_delicious = 0xf1a5, - fa_desktop = 0xf108, - fa_deviantart = 0xf1bd, - fa_diamond = 0xf219, - fa_digg = 0xf1a6, - fa_dollar = 0xf155, - fa_dot_circle_o = 0xf192, - fa_download = 0xf019, - fa_dribbble = 0xf17d, - fa_drivers_license = 0xf2c2, - fa_drivers_license_o = 0xf2c3, - fa_dropbox = 0xf16b, - fa_drupal = 0xf1a9, - fa_edge = 0xf282, - fa_edit = 0xf044, - fa_eercast = 0xf2da, - fa_eject = 0xf052, - fa_ellipsis_h = 0xf141, - fa_ellipsis_v = 0xf142, - fa_empire = 0xf1d1, - fa_envelope = 0xf0e0, - fa_envelope_o = 0xf003, - fa_envelope_open = 0xf2b6, - fa_envelope_open_o = 0xf2b7, - fa_envelope_square = 0xf199, - fa_envira = 0xf299, - fa_eraser = 0xf12d, - fa_etsy = 0xf2d7, - fa_eur = 0xf153, - fa_euro = 0xf153, - fa_exchange = 0xf0ec, - fa_exclamation = 0xf12a, - fa_exclamation_circle = 0xf06a, - fa_exclamation_triangle = 0xf071, - fa_expand = 0xf065, - fa_expeditedssl = 0xf23e, - fa_external_link = 0xf08e, - fa_external_link_square = 0xf14c, - fa_eye = 0xf06e, - fa_eye_slash = 0xf070, - fa_eyedropper = 0xf1fb, - fa_fa = 0xf2b4, - fa_facebook = 0xf09a, - fa_facebook_f = 0xf09a, - fa_facebook_official = 0xf230, - fa_facebook_square = 0xf082, - fa_fast_backward = 0xf049, - fa_fast_forward = 0xf050, - fa_fax = 0xf1ac, - fa_feed = 0xf09e, - fa_female = 0xf182, - fa_fighter_jet = 0xf0fb, - fa_file = 0xf15b, - fa_file_archive_o = 0xf1c6, - fa_file_audio_o = 0xf1c7, - fa_file_code_o = 0xf1c9, - fa_file_excel_o = 0xf1c3, - fa_file_image_o = 0xf1c5, - fa_file_movie_o = 0xf1c8, - fa_file_o = 0xf016, - fa_file_pdf_o = 0xf1c1, - fa_file_photo_o = 0xf1c5, - fa_file_picture_o = 0xf1c5, - fa_file_powerpoint_o = 0xf1c4, - fa_file_sound_o = 0xf1c7, - fa_file_text = 0xf15c, - fa_file_text_o = 0xf0f6, - fa_file_video_o = 0xf1c8, - fa_file_word_o = 0xf1c2, - fa_file_zip_o = 0xf1c6, - fa_files_o = 0xf0c5, - fa_film = 0xf008, - fa_filter = 0xf0b0, - fa_fire = 0xf06d, - fa_fire_extinguisher = 0xf134, - fa_firefox = 0xf269, - fa_first_order = 0xf2b0, - fa_flag = 0xf024, - fa_flag_checkered = 0xf11e, - fa_flag_o = 0xf11d, - fa_flash = 0xf0e7, - fa_flask = 0xf0c3, - fa_flickr = 0xf16e, - fa_floppy_o = 0xf0c7, - fa_folder = 0xf07b, - fa_folder_o = 0xf114, - fa_folder_open = 0xf07c, - fa_folder_open_o = 0xf115, - fa_font = 0xf031, - fa_font_awesome = 0xf2b4, - fa_fonticons = 0xf280, - fa_fort_awesome = 0xf286, - fa_forumbee = 0xf211, - fa_forward = 0xf04e, - fa_foursquare = 0xf180, - fa_free_code_camp = 0xf2c5, - fa_frown_o = 0xf119, - fa_futbol_o = 0xf1e3, - fa_gamepad = 0xf11b, - fa_gavel = 0xf0e3, - fa_gbp = 0xf154, - fa_ge = 0xf1d1, - fa_gear = 0xf013, - fa_gears = 0xf085, - fa_genderless = 0xf22d, - fa_get_pocket = 0xf265, - fa_gg = 0xf260, - fa_gg_circle = 0xf261, - fa_gift = 0xf06b, - fa_git = 0xf1d3, - fa_git_square = 0xf1d2, - fa_github = 0xf09b, - fa_github_alt = 0xf113, - fa_github_square = 0xf092, - fa_gitlab = 0xf296, - fa_gittip = 0xf184, - fa_glass = 0xf000, - fa_glide = 0xf2a5, - fa_glide_g = 0xf2a6, - fa_globe = 0xf0ac, - fa_google = 0xf1a0, - fa_google_plus = 0xf0d5, - fa_google_plus_circle = 0xf2b3, - fa_google_plus_official = 0xf2b3, - fa_google_plus_square = 0xf0d4, - fa_google_wallet = 0xf1ee, - fa_graduation_cap = 0xf19d, - fa_gratipay = 0xf184, - fa_grav = 0xf2d6, - fa_group = 0xf0c0, - fa_h_square = 0xf0fd, - fa_hacker_news = 0xf1d4, - fa_hand_grab_o = 0xf255, - fa_hand_lizard_o = 0xf258, - fa_hand_o_down = 0xf0a7, - fa_hand_o_left = 0xf0a5, - fa_hand_o_right = 0xf0a4, - fa_hand_o_up = 0xf0a6, - fa_hand_paper_o = 0xf256, - fa_hand_peace_o = 0xf25b, - fa_hand_pointer_o = 0xf25a, - fa_hand_rock_o = 0xf255, - fa_hand_scissors_o = 0xf257, - fa_hand_spock_o = 0xf259, - fa_hand_stop_o = 0xf256, - fa_handshake_o = 0xf2b5, - fa_hard_of_hearing = 0xf2a4, - fa_hashtag = 0xf292, - fa_hdd_o = 0xf0a0, - fa_header = 0xf1dc, - fa_headphones = 0xf025, - fa_heart = 0xf004, - fa_heart_o = 0xf08a, - fa_heartbeat = 0xf21e, - fa_history = 0xf1da, - fa_home = 0xf015, - fa_hospital_o = 0xf0f8, - fa_hotel = 0xf236, - fa_hourglass = 0xf254, - fa_hourglass_1 = 0xf251, - fa_hourglass_2 = 0xf252, - fa_hourglass_3 = 0xf253, - fa_hourglass_end = 0xf253, - fa_hourglass_half = 0xf252, - fa_hourglass_o = 0xf250, - fa_hourglass_start = 0xf251, - fa_houzz = 0xf27c, - fa_html5 = 0xf13b, - fa_i_cursor = 0xf246, - fa_id_badge = 0xf2c1, - fa_id_card = 0xf2c2, - fa_id_card_o = 0xf2c3, - fa_ils = 0xf20b, - fa_image = 0xf03e, - fa_imdb = 0xf2d8, - fa_inbox = 0xf01c, - fa_indent = 0xf03c, - fa_industry = 0xf275, - fa_info = 0xf129, - fa_info_circle = 0xf05a, - fa_inr = 0xf156, - fa_instagram = 0xf16d, - fa_institution = 0xf19c, - fa_internet_explorer = 0xf26b, - fa_intersex = 0xf224, - fa_ioxhost = 0xf208, - fa_italic = 0xf033, - fa_joomla = 0xf1aa, - fa_jpy = 0xf157, - fa_jsfiddle = 0xf1cc, - fa_key = 0xf084, - fa_keyboard_o = 0xf11c, - fa_krw = 0xf159, - fa_language = 0xf1ab, - fa_laptop = 0xf109, - fa_lastfm = 0xf202, - fa_lastfm_square = 0xf203, - fa_leaf = 0xf06c, - fa_leanpub = 0xf212, - fa_legal = 0xf0e3, - fa_lemon_o = 0xf094, - fa_level_down = 0xf149, - fa_level_up = 0xf148, - fa_life_bouy = 0xf1cd, - fa_life_buoy = 0xf1cd, - fa_life_ring = 0xf1cd, - fa_life_saver = 0xf1cd, - fa_lightbulb_o = 0xf0eb, - fa_line_chart = 0xf201, - fa_link = 0xf0c1, - fa_linkedin = 0xf0e1, - fa_linkedin_square = 0xf08c, - fa_linode = 0xf2b8, - fa_linux = 0xf17c, - fa_list = 0xf03a, - fa_list_alt = 0xf022, - fa_list_ol = 0xf0cb, - fa_list_ul = 0xf0ca, - fa_location_arrow = 0xf124, - fa_lock = 0xf023, - fa_long_arrow_down = 0xf175, - fa_long_arrow_left = 0xf177, - fa_long_arrow_right = 0xf178, - fa_long_arrow_up = 0xf176, - fa_low_vision = 0xf2a8, - fa_magic = 0xf0d0, - fa_magnet = 0xf076, - fa_mail_forward = 0xf064, - fa_mail_reply = 0xf112, - fa_mail_reply_all = 0xf122, - fa_male = 0xf183, - fa_map = 0xf279, - fa_map_marker = 0xf041, - fa_map_o = 0xf278, - fa_map_pin = 0xf276, - fa_map_signs = 0xf277, - fa_mars = 0xf222, - fa_mars_double = 0xf227, - fa_mars_stroke = 0xf229, - fa_mars_stroke_h = 0xf22b, - fa_mars_stroke_v = 0xf22a, - fa_maxcdn = 0xf136, - fa_meanpath = 0xf20c, - fa_medium = 0xf23a, - fa_medkit = 0xf0fa, - fa_meetup = 0xf2e0, - fa_meh_o = 0xf11a, - fa_mercury = 0xf223, - fa_microchip = 0xf2db, - fa_microphone = 0xf130, - fa_microphone_slash = 0xf131, - fa_minus = 0xf068, - fa_minus_circle = 0xf056, - fa_minus_square = 0xf146, - fa_minus_square_o = 0xf147, - fa_mixcloud = 0xf289, - fa_mobile = 0xf10b, - fa_mobile_phone = 0xf10b, - fa_modx = 0xf285, - fa_money = 0xf0d6, - fa_moon_o = 0xf186, - fa_mortar_board = 0xf19d, - fa_motorcycle = 0xf21c, - fa_mouse_pointer = 0xf245, - fa_music = 0xf001, - fa_navicon = 0xf0c9, - fa_neuter = 0xf22c, - fa_newspaper_o = 0xf1ea, - fa_object_group = 0xf247, - fa_object_ungroup = 0xf248, - fa_odnoklassniki = 0xf263, - fa_odnoklassniki_square = 0xf264, - fa_opencart = 0xf23d, - fa_openid = 0xf19b, - fa_opera = 0xf26a, - fa_optin_monster = 0xf23c, - fa_outdent = 0xf03b, - fa_pagelines = 0xf18c, - fa_paint_brush = 0xf1fc, - fa_paper_plane = 0xf1d8, - fa_paper_plane_o = 0xf1d9, - fa_paperclip = 0xf0c6, - fa_paragraph = 0xf1dd, - fa_paste = 0xf0ea, - fa_pause = 0xf04c, - fa_pause_circle = 0xf28b, - fa_pause_circle_o = 0xf28c, - fa_paw = 0xf1b0, - fa_paypal = 0xf1ed, - fa_pencil = 0xf040, - fa_pencil_square = 0xf14b, - fa_pencil_square_o = 0xf044, - fa_percent = 0xf295, - fa_phone = 0xf095, - fa_phone_square = 0xf098, - fa_photo = 0xf03e, - fa_picture_o = 0xf03e, - fa_pie_chart = 0xf200, - fa_pied_piper = 0xf2ae, - fa_pied_piper_alt = 0xf1a8, - fa_pied_piper_pp = 0xf1a7, - fa_pinterest = 0xf0d2, - fa_pinterest_p = 0xf231, - fa_pinterest_square = 0xf0d3, - fa_plane = 0xf072, - fa_play = 0xf04b, - fa_play_circle = 0xf144, - fa_play_circle_o = 0xf01d, - fa_plug = 0xf1e6, - fa_plus = 0xf067, - fa_plus_circle = 0xf055, - fa_plus_square = 0xf0fe, - fa_plus_square_o = 0xf196, - fa_podcast = 0xf2ce, - fa_power_off = 0xf011, - fa_print = 0xf02f, - fa_product_hunt = 0xf288, - fa_puzzle_piece = 0xf12e, - fa_qq = 0xf1d6, - fa_qrcode = 0xf029, - fa_question = 0xf128, - fa_question_circle = 0xf059, - fa_question_circle_o = 0xf29c, - fa_quora = 0xf2c4, - fa_quote_left = 0xf10d, - fa_quote_right = 0xf10e, - fa_ra = 0xf1d0, - fa_random = 0xf074, - fa_ravelry = 0xf2d9, - fa_rebel = 0xf1d0, - fa_recycle = 0xf1b8, - fa_reddit = 0xf1a1, - fa_reddit_alien = 0xf281, - fa_reddit_square = 0xf1a2, - fa_refresh = 0xf021, - fa_registered = 0xf25d, - fa_remove = 0xf00d, - fa_renren = 0xf18b, - fa_reorder = 0xf0c9, - fa_repeat = 0xf01e, - fa_reply = 0xf112, - fa_reply_all = 0xf122, - fa_resistance = 0xf1d0, - fa_retweet = 0xf079, - fa_rmb = 0xf157, - fa_road = 0xf018, - fa_rocket = 0xf135, - fa_rotate_left = 0xf0e2, - fa_rotate_right = 0xf01e, - fa_rouble = 0xf158, - fa_rss = 0xf09e, - fa_rss_square = 0xf143, - fa_rub = 0xf158, - fa_ruble = 0xf158, - fa_rupee = 0xf156, - fa_s15 = 0xf2cd, - fa_safari = 0xf267, - fa_save = 0xf0c7, - fa_scissors = 0xf0c4, - fa_scribd = 0xf28a, - fa_search = 0xf002, - fa_search_minus = 0xf010, - fa_search_plus = 0xf00e, - fa_sellsy = 0xf213, - fa_send = 0xf1d8, - fa_send_o = 0xf1d9, - fa_server = 0xf233, - fa_share = 0xf064, - fa_share_alt = 0xf1e0, - fa_share_alt_square = 0xf1e1, - fa_share_square = 0xf14d, - fa_share_square_o = 0xf045, - fa_shekel = 0xf20b, - fa_sheqel = 0xf20b, - fa_shield = 0xf132, - fa_ship = 0xf21a, - fa_shirtsinbulk = 0xf214, - fa_shopping_bag = 0xf290, - fa_shopping_basket = 0xf291, - fa_shopping_cart = 0xf07a, - fa_shower = 0xf2cc, - fa_sign_in = 0xf090, - fa_sign_language = 0xf2a7, - fa_sign_out = 0xf08b, - fa_signal = 0xf012, - fa_signing = 0xf2a7, - fa_simplybuilt = 0xf215, - fa_sitemap = 0xf0e8, - fa_skyatlas = 0xf216, - fa_skype = 0xf17e, - fa_slack = 0xf198, - fa_sliders = 0xf1de, - fa_slideshare = 0xf1e7, - fa_smile_o = 0xf118, - fa_snapchat = 0xf2ab, - fa_snapchat_ghost = 0xf2ac, - fa_snapchat_square = 0xf2ad, - fa_snowflake_o = 0xf2dc, - fa_soccer_ball_o = 0xf1e3, - fa_sort = 0xf0dc, - fa_sort_alpha_asc = 0xf15d, - fa_sort_alpha_desc = 0xf15e, - fa_sort_amount_asc = 0xf160, - fa_sort_amount_desc = 0xf161, - fa_sort_asc = 0xf0de, - fa_sort_desc = 0xf0dd, - fa_sort_down = 0xf0dd, - fa_sort_numeric_asc = 0xf162, - fa_sort_numeric_desc = 0xf163, - fa_sort_up = 0xf0de, - fa_soundcloud = 0xf1be, - fa_space_shuttle = 0xf197, - fa_spinner = 0xf110, - fa_spoon = 0xf1b1, - fa_spotify = 0xf1bc, - fa_square = 0xf0c8, - fa_square_o = 0xf096, - fa_stack_exchange = 0xf18d, - fa_stack_overflow = 0xf16c, - fa_star = 0xf005, - fa_star_half = 0xf089, - fa_star_half_empty = 0xf123, - fa_star_half_full = 0xf123, - fa_star_half_o = 0xf123, - fa_star_o = 0xf006, - fa_steam = 0xf1b6, - fa_steam_square = 0xf1b7, - fa_step_backward = 0xf048, - fa_step_forward = 0xf051, - fa_stethoscope = 0xf0f1, - fa_sticky_note = 0xf249, - fa_sticky_note_o = 0xf24a, - fa_stop = 0xf04d, - fa_stop_circle = 0xf28d, - fa_stop_circle_o = 0xf28e, - fa_street_view = 0xf21d, - fa_strikethrough = 0xf0cc, - fa_stumbleupon = 0xf1a4, - fa_stumbleupon_circle = 0xf1a3, - fa_subscript = 0xf12c, - fa_subway = 0xf239, - fa_suitcase = 0xf0f2, - fa_sun_o = 0xf185, - fa_superpowers = 0xf2dd, - fa_superscript = 0xf12b, - fa_support = 0xf1cd, - fa_table = 0xf0ce, - fa_tablet = 0xf10a, - fa_tachometer = 0xf0e4, - fa_tag = 0xf02b, - fa_tags = 0xf02c, - fa_tasks = 0xf0ae, - fa_taxi = 0xf1ba, - fa_telegram = 0xf2c6, - fa_television = 0xf26c, - fa_tencent_weibo = 0xf1d5, - fa_terminal = 0xf120, - fa_text_height = 0xf034, - fa_text_width = 0xf035, - fa_th = 0xf00a, - fa_th_large = 0xf009, - fa_th_list = 0xf00b, - fa_themeisle = 0xf2b2, - fa_thermometer = 0xf2c7, - fa_thermometer_0 = 0xf2cb, - fa_thermometer_1 = 0xf2ca, - fa_thermometer_2 = 0xf2c9, - fa_thermometer_3 = 0xf2c8, - fa_thermometer_4 = 0xf2c7, - fa_thermometer_empty = 0xf2cb, - fa_thermometer_full = 0xf2c7, - fa_thermometer_half = 0xf2c9, - fa_thermometer_quarter = 0xf2ca, - fa_thermometer_three_quarters = 0xf2c8, - fa_thumb_tack = 0xf08d, - fa_thumbs_down = 0xf165, - fa_thumbs_o_down = 0xf088, - fa_thumbs_o_up = 0xf087, - fa_thumbs_up = 0xf164, - fa_ticket = 0xf145, - fa_times = 0xf00d, - fa_times_circle = 0xf057, - fa_times_circle_o = 0xf05c, - fa_times_rectangle = 0xf2d3, - fa_times_rectangle_o = 0xf2d4, - fa_tint = 0xf043, - fa_toggle_down = 0xf150, - fa_toggle_left = 0xf191, - fa_toggle_off = 0xf204, - fa_toggle_on = 0xf205, - fa_toggle_right = 0xf152, - fa_toggle_up = 0xf151, - fa_trademark = 0xf25c, - fa_train = 0xf238, - fa_transgender = 0xf224, - fa_transgender_alt = 0xf225, - fa_trash = 0xf1f8, - fa_trash_o = 0xf014, - fa_tree = 0xf1bb, - fa_trello = 0xf181, - fa_tripadvisor = 0xf262, - fa_trophy = 0xf091, - fa_truck = 0xf0d1, - fa_try = 0xf195, - fa_tty = 0xf1e4, - fa_tumblr = 0xf173, - fa_tumblr_square = 0xf174, - fa_turkish_lira = 0xf195, - fa_tv = 0xf26c, - fa_twitch = 0xf1e8, - fa_twitter = 0xf099, - fa_twitter_square = 0xf081, - fa_umbrella = 0xf0e9, - fa_underline = 0xf0cd, - fa_undo = 0xf0e2, - fa_universal_access = 0xf29a, - fa_university = 0xf19c, - fa_unlink = 0xf127, - fa_unlock = 0xf09c, - fa_unlock_alt = 0xf13e, - fa_unsorted = 0xf0dc, - fa_upload = 0xf093, - fa_usb = 0xf287, - fa_usd = 0xf155, - fa_user = 0xf007, - fa_user_circle = 0xf2bd, - fa_user_circle_o = 0xf2be, - fa_user_md = 0xf0f0, - fa_user_o = 0xf2c0, - fa_user_plus = 0xf234, - fa_user_secret = 0xf21b, - fa_user_times = 0xf235, - fa_users = 0xf0c0, - fa_vcard = 0xf2bb, - fa_vcard_o = 0xf2bc, - fa_venus = 0xf221, - fa_venus_double = 0xf226, - fa_venus_mars = 0xf228, - fa_viacoin = 0xf237, - fa_viadeo = 0xf2a9, - fa_viadeo_square = 0xf2aa, - fa_video_camera = 0xf03d, - fa_vimeo = 0xf27d, - fa_vimeo_square = 0xf194, - fa_vine = 0xf1ca, - fa_vk = 0xf189, - fa_volume_control_phone = 0xf2a0, - fa_volume_down = 0xf027, - fa_volume_off = 0xf026, - fa_volume_up = 0xf028, - fa_warning = 0xf071, - fa_wechat = 0xf1d7, - fa_weibo = 0xf18a, - fa_weixin = 0xf1d7, - fa_whatsapp = 0xf232, - fa_wheelchair = 0xf193, - fa_wheelchair_alt = 0xf29b, - fa_wifi = 0xf1eb, - fa_wikipedia_w = 0xf266, - fa_window_close = 0xf2d3, - fa_window_close_o = 0xf2d4, - fa_window_maximize = 0xf2d0, - fa_window_minimize = 0xf2d1, - fa_window_restore = 0xf2d2, - fa_windows = 0xf17a, - fa_won = 0xf159, - fa_wordpress = 0xf19a, - fa_wpbeginner = 0xf297, - fa_wpexplorer = 0xf2de, - fa_wpforms = 0xf298, - fa_wrench = 0xf0ad, - fa_xing = 0xf168, - fa_xing_square = 0xf169, - fa_y_combinator = 0xf23b, - fa_y_combinator_square = 0xf1d4, - fa_yahoo = 0xf19e, - fa_yc = 0xf23b, - fa_yc_square = 0xf1d4, - fa_yelp = 0xf1e9, - fa_yen = 0xf157, - fa_yoast = 0xf2b1, - fa_youtube = 0xf167, - fa_youtube_play = 0xf16a, - fa_youtube_square = 0xf166, - - // ruleset icons in circles - fa_osu_osu_o = 0xe000, - fa_osu_mania_o = 0xe001, - fa_osu_fruits_o = 0xe002, - fa_osu_taiko_o = 0xe003, - - // ruleset icons without circles - fa_osu_filled_circle = 0xe004, - fa_osu_cross_o = 0xe005, - fa_osu_logo = 0xe006, - fa_osu_chevron_down_o = 0xe007, - fa_osu_edit_o = 0xe033, - fa_osu_left_o = 0xe034, - fa_osu_right_o = 0xe035, - fa_osu_charts = 0xe036, - fa_osu_solo = 0xe037, - fa_osu_multi = 0xe038, - fa_osu_gear = 0xe039, - - // misc icons - fa_osu_bat = 0xe008, - fa_osu_bubble = 0xe009, - fa_osu_bubble_pop = 0xe02e, - fa_osu_dice = 0xe011, - fa_osu_heart1 = 0xe02f, - fa_osu_heart1_break = 0xe030, - fa_osu_hot = 0xe031, - fa_osu_list_search = 0xe032, - - //osu! playstyles - fa_osu_playstyle_tablet = 0xe02a, - fa_osu_playstyle_mouse = 0xe029, - fa_osu_playstyle_keyboard = 0xe02b, - fa_osu_playstyle_touch = 0xe02c, - - // osu! difficulties - fa_osu_easy_osu = 0xe015, - fa_osu_normal_osu = 0xe016, - fa_osu_hard_osu = 0xe017, - fa_osu_insane_osu = 0xe018, - fa_osu_expert_osu = 0xe019, - - // taiko difficulties - fa_osu_easy_taiko = 0xe01a, - fa_osu_normal_taiko = 0xe01b, - fa_osu_hard_taiko = 0xe01c, - fa_osu_insane_taiko = 0xe01d, - fa_osu_expert_taiko = 0xe01e, - - // fruits difficulties - fa_osu_easy_fruits = 0xe01f, - fa_osu_normal_fruits = 0xe020, - fa_osu_hard_fruits = 0xe021, - fa_osu_insane_fruits = 0xe022, - fa_osu_expert_fruits = 0xe023, - - // mania difficulties - fa_osu_easy_mania = 0xe024, - fa_osu_normal_mania = 0xe025, - fa_osu_hard_mania = 0xe026, - fa_osu_insane_mania = 0xe027, - fa_osu_expert_mania = 0xe028, - - // mod icons - fa_osu_mod_perfect = 0xe049, - fa_osu_mod_autopilot = 0xe03a, - fa_osu_mod_auto = 0xe03b, - fa_osu_mod_cinema = 0xe03c, - fa_osu_mod_doubletime = 0xe03d, - fa_osu_mod_easy = 0xe03e, - fa_osu_mod_flashlight = 0xe03f, - fa_osu_mod_halftime = 0xe040, - fa_osu_mod_hardrock = 0xe041, - fa_osu_mod_hidden = 0xe042, - fa_osu_mod_nightcore = 0xe043, - fa_osu_mod_nofail = 0xe044, - fa_osu_mod_relax = 0xe045, - fa_osu_mod_spunout = 0xe046, - fa_osu_mod_suddendeath = 0xe047, - fa_osu_mod_target = 0xe048, - fa_osu_mod_bg = 0xe04a, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics; +using osu.Framework.IO.Stores; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Caching; + +namespace osu.Game.Graphics +{ + public class SpriteIcon : CompositeDrawable + { + private Sprite spriteShadow; + private Sprite spriteMain; + + private Cached layout = new Cached(); + private Container shadowVisibility; + + private FontStore store; + + [BackgroundDependencyLoader] + private void load(FontStore store) + { + this.store = store; + + InternalChildren = new Drawable[] + { + shadowVisibility = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Child = spriteShadow = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Y = 2, + Colour = new Color4(0f, 0f, 0f, 0.2f), + }, + Alpha = shadow ? 1 : 0, + }, + spriteMain = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit + }, + }; + + updateTexture(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateTexture(); + } + + private FontAwesome loadedIcon; + private void updateTexture() + { + var loadableIcon = icon; + + if (loadableIcon == loadedIcon) return; + + var texture = store?.Get(((char)loadableIcon).ToString()); + + spriteMain.Texture = texture; + spriteShadow.Texture = texture; + + if (Size == Vector2.Zero) + Size = new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0); + + loadedIcon = loadableIcon; + } + + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + if ((invalidation & Invalidation.Colour) > 0 && Shadow) + layout.Invalidate(); + return base.Invalidate(invalidation, source, shallPropagate); + } + + protected override void Update() + { + if (!layout.IsValid) + { + //adjust shadow alpha based on highest component intensity to avoid muddy display of darker text. + //squared result for quadratic fall-off seems to give the best result. + var avgColour = (Color4)DrawInfo.Colour.AverageColour; + + spriteShadow.Alpha = (float)Math.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2); + + layout.Validate(); + } + } + + private bool shadow; + public bool Shadow + { + get { return shadow; } + set + { + shadow = value; + if (shadowVisibility != null) + shadowVisibility.Alpha = value ? 1 : 0; + } + } + + private FontAwesome icon; + + public FontAwesome Icon + { + get + { + return icon; + } + + set + { + if (icon == value) return; + + icon = value; + if (IsLoaded) + updateTexture(); + } + } + } + + public enum FontAwesome + { + fa_500px = 0xf26e, + fa_address_book = 0xf2b9, + fa_address_book_o = 0xf2ba, + fa_address_card = 0xf2bb, + fa_address_card_o = 0xf2bc, + fa_adjust = 0xf042, + fa_adn = 0xf170, + fa_align_center = 0xf037, + fa_align_justify = 0xf039, + fa_align_left = 0xf036, + fa_align_right = 0xf038, + fa_amazon = 0xf270, + fa_ambulance = 0xf0f9, + fa_american_sign_language_interpreting = 0xf2a3, + fa_anchor = 0xf13d, + fa_android = 0xf17b, + fa_angellist = 0xf209, + fa_angle_double_down = 0xf103, + fa_angle_double_left = 0xf100, + fa_angle_double_right = 0xf101, + fa_angle_double_up = 0xf102, + fa_angle_down = 0xf107, + fa_angle_left = 0xf104, + fa_angle_right = 0xf105, + fa_angle_up = 0xf106, + fa_apple = 0xf179, + fa_archive = 0xf187, + fa_area_chart = 0xf1fe, + fa_arrow_circle_down = 0xf0ab, + fa_arrow_circle_left = 0xf0a8, + fa_arrow_circle_o_down = 0xf01a, + fa_arrow_circle_o_left = 0xf190, + fa_arrow_circle_o_right = 0xf18e, + fa_arrow_circle_o_up = 0xf01b, + fa_arrow_circle_right = 0xf0a9, + fa_arrow_circle_up = 0xf0aa, + fa_arrow_down = 0xf063, + fa_arrow_left = 0xf060, + fa_arrow_right = 0xf061, + fa_arrow_up = 0xf062, + fa_arrows = 0xf047, + fa_arrows_alt = 0xf0b2, + fa_arrows_h = 0xf07e, + fa_arrows_v = 0xf07d, + fa_asl_interpreting = 0xf2a3, + fa_assistive_listening_systems = 0xf2a2, + fa_asterisk = 0xf069, + fa_at = 0xf1fa, + fa_audio_description = 0xf29e, + fa_automobile = 0xf1b9, + fa_backward = 0xf04a, + fa_balance_scale = 0xf24e, + fa_ban = 0xf05e, + fa_bandcamp = 0xf2d5, + fa_bank = 0xf19c, + fa_bar_chart = 0xf080, + fa_bar_chart_o = 0xf080, + fa_barcode = 0xf02a, + fa_bars = 0xf0c9, + fa_bath = 0xf2cd, + fa_bathtub = 0xf2cd, + fa_battery = 0xf240, + fa_battery_0 = 0xf244, + fa_battery_1 = 0xf243, + fa_battery_2 = 0xf242, + fa_battery_3 = 0xf241, + fa_battery_4 = 0xf240, + fa_battery_empty = 0xf244, + fa_battery_full = 0xf240, + fa_battery_half = 0xf242, + fa_battery_quarter = 0xf243, + fa_battery_three_quarters = 0xf241, + fa_bed = 0xf236, + fa_beer = 0xf0fc, + fa_behance = 0xf1b4, + fa_behance_square = 0xf1b5, + fa_bell = 0xf0f3, + fa_bell_o = 0xf0a2, + fa_bell_slash = 0xf1f6, + fa_bell_slash_o = 0xf1f7, + fa_bicycle = 0xf206, + fa_binoculars = 0xf1e5, + fa_birthday_cake = 0xf1fd, + fa_bitbucket = 0xf171, + fa_bitbucket_square = 0xf172, + fa_bitcoin = 0xf15a, + fa_black_tie = 0xf27e, + fa_blind = 0xf29d, + fa_bluetooth = 0xf293, + fa_bluetooth_b = 0xf294, + fa_bold = 0xf032, + fa_bolt = 0xf0e7, + fa_bomb = 0xf1e2, + fa_book = 0xf02d, + fa_bookmark = 0xf02e, + fa_bookmark_o = 0xf097, + fa_braille = 0xf2a1, + fa_briefcase = 0xf0b1, + fa_btc = 0xf15a, + fa_bug = 0xf188, + fa_building = 0xf1ad, + fa_building_o = 0xf0f7, + fa_bullhorn = 0xf0a1, + fa_bullseye = 0xf140, + fa_bus = 0xf207, + fa_buysellads = 0xf20d, + fa_cab = 0xf1ba, + fa_calculator = 0xf1ec, + fa_calendar = 0xf073, + fa_calendar_check_o = 0xf274, + fa_calendar_minus_o = 0xf272, + fa_calendar_o = 0xf133, + fa_calendar_plus_o = 0xf271, + fa_calendar_times_o = 0xf273, + fa_camera = 0xf030, + fa_camera_retro = 0xf083, + fa_car = 0xf1b9, + fa_caret_down = 0xf0d7, + fa_caret_left = 0xf0d9, + fa_caret_right = 0xf0da, + fa_caret_square_o_down = 0xf150, + fa_caret_square_o_left = 0xf191, + fa_caret_square_o_right = 0xf152, + fa_caret_square_o_up = 0xf151, + fa_caret_up = 0xf0d8, + fa_cart_arrow_down = 0xf218, + fa_cart_plus = 0xf217, + fa_cc = 0xf20a, + fa_cc_amex = 0xf1f3, + fa_cc_diners_club = 0xf24c, + fa_cc_discover = 0xf1f2, + fa_cc_jcb = 0xf24b, + fa_cc_mastercard = 0xf1f1, + fa_cc_paypal = 0xf1f4, + fa_cc_stripe = 0xf1f5, + fa_cc_visa = 0xf1f0, + fa_certificate = 0xf0a3, + fa_chain = 0xf0c1, + fa_chain_broken = 0xf127, + fa_check = 0xf00c, + fa_check_circle = 0xf058, + fa_check_circle_o = 0xf05d, + fa_check_square = 0xf14a, + fa_check_square_o = 0xf046, + fa_chevron_circle_down = 0xf13a, + fa_chevron_circle_left = 0xf137, + fa_chevron_circle_right = 0xf138, + fa_chevron_circle_up = 0xf139, + fa_chevron_down = 0xf078, + fa_chevron_left = 0xf053, + fa_chevron_right = 0xf054, + fa_chevron_up = 0xf077, + fa_child = 0xf1ae, + fa_chrome = 0xf268, + fa_circle = 0xf111, + fa_circle_o = 0xf10c, + fa_circle_o_notch = 0xf1ce, + fa_circle_thin = 0xf1db, + fa_clipboard = 0xf0ea, + fa_clock_o = 0xf017, + fa_clone = 0xf24d, + fa_close = 0xf00d, + fa_cloud = 0xf0c2, + fa_cloud_download = 0xf0ed, + fa_cloud_upload = 0xf0ee, + fa_cny = 0xf157, + fa_code = 0xf121, + fa_code_fork = 0xf126, + fa_codepen = 0xf1cb, + fa_codiepie = 0xf284, + fa_coffee = 0xf0f4, + fa_cog = 0xf013, + fa_cogs = 0xf085, + fa_columns = 0xf0db, + fa_comment = 0xf075, + fa_comment_o = 0xf0e5, + fa_commenting = 0xf27a, + fa_commenting_o = 0xf27b, + fa_comments = 0xf086, + fa_comments_o = 0xf0e6, + fa_compass = 0xf14e, + fa_compress = 0xf066, + fa_connectdevelop = 0xf20e, + fa_contao = 0xf26d, + fa_copy = 0xf0c5, + fa_copyright = 0xf1f9, + fa_creative_commons = 0xf25e, + fa_credit_card = 0xf09d, + fa_credit_card_alt = 0xf283, + fa_crop = 0xf125, + fa_crosshairs = 0xf05b, + fa_css3 = 0xf13c, + fa_cube = 0xf1b2, + fa_cubes = 0xf1b3, + fa_cut = 0xf0c4, + fa_cutlery = 0xf0f5, + fa_dashboard = 0xf0e4, + fa_dashcube = 0xf210, + fa_database = 0xf1c0, + fa_deaf = 0xf2a4, + fa_deafness = 0xf2a4, + fa_dedent = 0xf03b, + fa_delicious = 0xf1a5, + fa_desktop = 0xf108, + fa_deviantart = 0xf1bd, + fa_diamond = 0xf219, + fa_digg = 0xf1a6, + fa_dollar = 0xf155, + fa_dot_circle_o = 0xf192, + fa_download = 0xf019, + fa_dribbble = 0xf17d, + fa_drivers_license = 0xf2c2, + fa_drivers_license_o = 0xf2c3, + fa_dropbox = 0xf16b, + fa_drupal = 0xf1a9, + fa_edge = 0xf282, + fa_edit = 0xf044, + fa_eercast = 0xf2da, + fa_eject = 0xf052, + fa_ellipsis_h = 0xf141, + fa_ellipsis_v = 0xf142, + fa_empire = 0xf1d1, + fa_envelope = 0xf0e0, + fa_envelope_o = 0xf003, + fa_envelope_open = 0xf2b6, + fa_envelope_open_o = 0xf2b7, + fa_envelope_square = 0xf199, + fa_envira = 0xf299, + fa_eraser = 0xf12d, + fa_etsy = 0xf2d7, + fa_eur = 0xf153, + fa_euro = 0xf153, + fa_exchange = 0xf0ec, + fa_exclamation = 0xf12a, + fa_exclamation_circle = 0xf06a, + fa_exclamation_triangle = 0xf071, + fa_expand = 0xf065, + fa_expeditedssl = 0xf23e, + fa_external_link = 0xf08e, + fa_external_link_square = 0xf14c, + fa_eye = 0xf06e, + fa_eye_slash = 0xf070, + fa_eyedropper = 0xf1fb, + fa_fa = 0xf2b4, + fa_facebook = 0xf09a, + fa_facebook_f = 0xf09a, + fa_facebook_official = 0xf230, + fa_facebook_square = 0xf082, + fa_fast_backward = 0xf049, + fa_fast_forward = 0xf050, + fa_fax = 0xf1ac, + fa_feed = 0xf09e, + fa_female = 0xf182, + fa_fighter_jet = 0xf0fb, + fa_file = 0xf15b, + fa_file_archive_o = 0xf1c6, + fa_file_audio_o = 0xf1c7, + fa_file_code_o = 0xf1c9, + fa_file_excel_o = 0xf1c3, + fa_file_image_o = 0xf1c5, + fa_file_movie_o = 0xf1c8, + fa_file_o = 0xf016, + fa_file_pdf_o = 0xf1c1, + fa_file_photo_o = 0xf1c5, + fa_file_picture_o = 0xf1c5, + fa_file_powerpoint_o = 0xf1c4, + fa_file_sound_o = 0xf1c7, + fa_file_text = 0xf15c, + fa_file_text_o = 0xf0f6, + fa_file_video_o = 0xf1c8, + fa_file_word_o = 0xf1c2, + fa_file_zip_o = 0xf1c6, + fa_files_o = 0xf0c5, + fa_film = 0xf008, + fa_filter = 0xf0b0, + fa_fire = 0xf06d, + fa_fire_extinguisher = 0xf134, + fa_firefox = 0xf269, + fa_first_order = 0xf2b0, + fa_flag = 0xf024, + fa_flag_checkered = 0xf11e, + fa_flag_o = 0xf11d, + fa_flash = 0xf0e7, + fa_flask = 0xf0c3, + fa_flickr = 0xf16e, + fa_floppy_o = 0xf0c7, + fa_folder = 0xf07b, + fa_folder_o = 0xf114, + fa_folder_open = 0xf07c, + fa_folder_open_o = 0xf115, + fa_font = 0xf031, + fa_font_awesome = 0xf2b4, + fa_fonticons = 0xf280, + fa_fort_awesome = 0xf286, + fa_forumbee = 0xf211, + fa_forward = 0xf04e, + fa_foursquare = 0xf180, + fa_free_code_camp = 0xf2c5, + fa_frown_o = 0xf119, + fa_futbol_o = 0xf1e3, + fa_gamepad = 0xf11b, + fa_gavel = 0xf0e3, + fa_gbp = 0xf154, + fa_ge = 0xf1d1, + fa_gear = 0xf013, + fa_gears = 0xf085, + fa_genderless = 0xf22d, + fa_get_pocket = 0xf265, + fa_gg = 0xf260, + fa_gg_circle = 0xf261, + fa_gift = 0xf06b, + fa_git = 0xf1d3, + fa_git_square = 0xf1d2, + fa_github = 0xf09b, + fa_github_alt = 0xf113, + fa_github_square = 0xf092, + fa_gitlab = 0xf296, + fa_gittip = 0xf184, + fa_glass = 0xf000, + fa_glide = 0xf2a5, + fa_glide_g = 0xf2a6, + fa_globe = 0xf0ac, + fa_google = 0xf1a0, + fa_google_plus = 0xf0d5, + fa_google_plus_circle = 0xf2b3, + fa_google_plus_official = 0xf2b3, + fa_google_plus_square = 0xf0d4, + fa_google_wallet = 0xf1ee, + fa_graduation_cap = 0xf19d, + fa_gratipay = 0xf184, + fa_grav = 0xf2d6, + fa_group = 0xf0c0, + fa_h_square = 0xf0fd, + fa_hacker_news = 0xf1d4, + fa_hand_grab_o = 0xf255, + fa_hand_lizard_o = 0xf258, + fa_hand_o_down = 0xf0a7, + fa_hand_o_left = 0xf0a5, + fa_hand_o_right = 0xf0a4, + fa_hand_o_up = 0xf0a6, + fa_hand_paper_o = 0xf256, + fa_hand_peace_o = 0xf25b, + fa_hand_pointer_o = 0xf25a, + fa_hand_rock_o = 0xf255, + fa_hand_scissors_o = 0xf257, + fa_hand_spock_o = 0xf259, + fa_hand_stop_o = 0xf256, + fa_handshake_o = 0xf2b5, + fa_hard_of_hearing = 0xf2a4, + fa_hashtag = 0xf292, + fa_hdd_o = 0xf0a0, + fa_header = 0xf1dc, + fa_headphones = 0xf025, + fa_heart = 0xf004, + fa_heart_o = 0xf08a, + fa_heartbeat = 0xf21e, + fa_history = 0xf1da, + fa_home = 0xf015, + fa_hospital_o = 0xf0f8, + fa_hotel = 0xf236, + fa_hourglass = 0xf254, + fa_hourglass_1 = 0xf251, + fa_hourglass_2 = 0xf252, + fa_hourglass_3 = 0xf253, + fa_hourglass_end = 0xf253, + fa_hourglass_half = 0xf252, + fa_hourglass_o = 0xf250, + fa_hourglass_start = 0xf251, + fa_houzz = 0xf27c, + fa_html5 = 0xf13b, + fa_i_cursor = 0xf246, + fa_id_badge = 0xf2c1, + fa_id_card = 0xf2c2, + fa_id_card_o = 0xf2c3, + fa_ils = 0xf20b, + fa_image = 0xf03e, + fa_imdb = 0xf2d8, + fa_inbox = 0xf01c, + fa_indent = 0xf03c, + fa_industry = 0xf275, + fa_info = 0xf129, + fa_info_circle = 0xf05a, + fa_inr = 0xf156, + fa_instagram = 0xf16d, + fa_institution = 0xf19c, + fa_internet_explorer = 0xf26b, + fa_intersex = 0xf224, + fa_ioxhost = 0xf208, + fa_italic = 0xf033, + fa_joomla = 0xf1aa, + fa_jpy = 0xf157, + fa_jsfiddle = 0xf1cc, + fa_key = 0xf084, + fa_keyboard_o = 0xf11c, + fa_krw = 0xf159, + fa_language = 0xf1ab, + fa_laptop = 0xf109, + fa_lastfm = 0xf202, + fa_lastfm_square = 0xf203, + fa_leaf = 0xf06c, + fa_leanpub = 0xf212, + fa_legal = 0xf0e3, + fa_lemon_o = 0xf094, + fa_level_down = 0xf149, + fa_level_up = 0xf148, + fa_life_bouy = 0xf1cd, + fa_life_buoy = 0xf1cd, + fa_life_ring = 0xf1cd, + fa_life_saver = 0xf1cd, + fa_lightbulb_o = 0xf0eb, + fa_line_chart = 0xf201, + fa_link = 0xf0c1, + fa_linkedin = 0xf0e1, + fa_linkedin_square = 0xf08c, + fa_linode = 0xf2b8, + fa_linux = 0xf17c, + fa_list = 0xf03a, + fa_list_alt = 0xf022, + fa_list_ol = 0xf0cb, + fa_list_ul = 0xf0ca, + fa_location_arrow = 0xf124, + fa_lock = 0xf023, + fa_long_arrow_down = 0xf175, + fa_long_arrow_left = 0xf177, + fa_long_arrow_right = 0xf178, + fa_long_arrow_up = 0xf176, + fa_low_vision = 0xf2a8, + fa_magic = 0xf0d0, + fa_magnet = 0xf076, + fa_mail_forward = 0xf064, + fa_mail_reply = 0xf112, + fa_mail_reply_all = 0xf122, + fa_male = 0xf183, + fa_map = 0xf279, + fa_map_marker = 0xf041, + fa_map_o = 0xf278, + fa_map_pin = 0xf276, + fa_map_signs = 0xf277, + fa_mars = 0xf222, + fa_mars_double = 0xf227, + fa_mars_stroke = 0xf229, + fa_mars_stroke_h = 0xf22b, + fa_mars_stroke_v = 0xf22a, + fa_maxcdn = 0xf136, + fa_meanpath = 0xf20c, + fa_medium = 0xf23a, + fa_medkit = 0xf0fa, + fa_meetup = 0xf2e0, + fa_meh_o = 0xf11a, + fa_mercury = 0xf223, + fa_microchip = 0xf2db, + fa_microphone = 0xf130, + fa_microphone_slash = 0xf131, + fa_minus = 0xf068, + fa_minus_circle = 0xf056, + fa_minus_square = 0xf146, + fa_minus_square_o = 0xf147, + fa_mixcloud = 0xf289, + fa_mobile = 0xf10b, + fa_mobile_phone = 0xf10b, + fa_modx = 0xf285, + fa_money = 0xf0d6, + fa_moon_o = 0xf186, + fa_mortar_board = 0xf19d, + fa_motorcycle = 0xf21c, + fa_mouse_pointer = 0xf245, + fa_music = 0xf001, + fa_navicon = 0xf0c9, + fa_neuter = 0xf22c, + fa_newspaper_o = 0xf1ea, + fa_object_group = 0xf247, + fa_object_ungroup = 0xf248, + fa_odnoklassniki = 0xf263, + fa_odnoklassniki_square = 0xf264, + fa_opencart = 0xf23d, + fa_openid = 0xf19b, + fa_opera = 0xf26a, + fa_optin_monster = 0xf23c, + fa_outdent = 0xf03b, + fa_pagelines = 0xf18c, + fa_paint_brush = 0xf1fc, + fa_paper_plane = 0xf1d8, + fa_paper_plane_o = 0xf1d9, + fa_paperclip = 0xf0c6, + fa_paragraph = 0xf1dd, + fa_paste = 0xf0ea, + fa_pause = 0xf04c, + fa_pause_circle = 0xf28b, + fa_pause_circle_o = 0xf28c, + fa_paw = 0xf1b0, + fa_paypal = 0xf1ed, + fa_pencil = 0xf040, + fa_pencil_square = 0xf14b, + fa_pencil_square_o = 0xf044, + fa_percent = 0xf295, + fa_phone = 0xf095, + fa_phone_square = 0xf098, + fa_photo = 0xf03e, + fa_picture_o = 0xf03e, + fa_pie_chart = 0xf200, + fa_pied_piper = 0xf2ae, + fa_pied_piper_alt = 0xf1a8, + fa_pied_piper_pp = 0xf1a7, + fa_pinterest = 0xf0d2, + fa_pinterest_p = 0xf231, + fa_pinterest_square = 0xf0d3, + fa_plane = 0xf072, + fa_play = 0xf04b, + fa_play_circle = 0xf144, + fa_play_circle_o = 0xf01d, + fa_plug = 0xf1e6, + fa_plus = 0xf067, + fa_plus_circle = 0xf055, + fa_plus_square = 0xf0fe, + fa_plus_square_o = 0xf196, + fa_podcast = 0xf2ce, + fa_power_off = 0xf011, + fa_print = 0xf02f, + fa_product_hunt = 0xf288, + fa_puzzle_piece = 0xf12e, + fa_qq = 0xf1d6, + fa_qrcode = 0xf029, + fa_question = 0xf128, + fa_question_circle = 0xf059, + fa_question_circle_o = 0xf29c, + fa_quora = 0xf2c4, + fa_quote_left = 0xf10d, + fa_quote_right = 0xf10e, + fa_ra = 0xf1d0, + fa_random = 0xf074, + fa_ravelry = 0xf2d9, + fa_rebel = 0xf1d0, + fa_recycle = 0xf1b8, + fa_reddit = 0xf1a1, + fa_reddit_alien = 0xf281, + fa_reddit_square = 0xf1a2, + fa_refresh = 0xf021, + fa_registered = 0xf25d, + fa_remove = 0xf00d, + fa_renren = 0xf18b, + fa_reorder = 0xf0c9, + fa_repeat = 0xf01e, + fa_reply = 0xf112, + fa_reply_all = 0xf122, + fa_resistance = 0xf1d0, + fa_retweet = 0xf079, + fa_rmb = 0xf157, + fa_road = 0xf018, + fa_rocket = 0xf135, + fa_rotate_left = 0xf0e2, + fa_rotate_right = 0xf01e, + fa_rouble = 0xf158, + fa_rss = 0xf09e, + fa_rss_square = 0xf143, + fa_rub = 0xf158, + fa_ruble = 0xf158, + fa_rupee = 0xf156, + fa_s15 = 0xf2cd, + fa_safari = 0xf267, + fa_save = 0xf0c7, + fa_scissors = 0xf0c4, + fa_scribd = 0xf28a, + fa_search = 0xf002, + fa_search_minus = 0xf010, + fa_search_plus = 0xf00e, + fa_sellsy = 0xf213, + fa_send = 0xf1d8, + fa_send_o = 0xf1d9, + fa_server = 0xf233, + fa_share = 0xf064, + fa_share_alt = 0xf1e0, + fa_share_alt_square = 0xf1e1, + fa_share_square = 0xf14d, + fa_share_square_o = 0xf045, + fa_shekel = 0xf20b, + fa_sheqel = 0xf20b, + fa_shield = 0xf132, + fa_ship = 0xf21a, + fa_shirtsinbulk = 0xf214, + fa_shopping_bag = 0xf290, + fa_shopping_basket = 0xf291, + fa_shopping_cart = 0xf07a, + fa_shower = 0xf2cc, + fa_sign_in = 0xf090, + fa_sign_language = 0xf2a7, + fa_sign_out = 0xf08b, + fa_signal = 0xf012, + fa_signing = 0xf2a7, + fa_simplybuilt = 0xf215, + fa_sitemap = 0xf0e8, + fa_skyatlas = 0xf216, + fa_skype = 0xf17e, + fa_slack = 0xf198, + fa_sliders = 0xf1de, + fa_slideshare = 0xf1e7, + fa_smile_o = 0xf118, + fa_snapchat = 0xf2ab, + fa_snapchat_ghost = 0xf2ac, + fa_snapchat_square = 0xf2ad, + fa_snowflake_o = 0xf2dc, + fa_soccer_ball_o = 0xf1e3, + fa_sort = 0xf0dc, + fa_sort_alpha_asc = 0xf15d, + fa_sort_alpha_desc = 0xf15e, + fa_sort_amount_asc = 0xf160, + fa_sort_amount_desc = 0xf161, + fa_sort_asc = 0xf0de, + fa_sort_desc = 0xf0dd, + fa_sort_down = 0xf0dd, + fa_sort_numeric_asc = 0xf162, + fa_sort_numeric_desc = 0xf163, + fa_sort_up = 0xf0de, + fa_soundcloud = 0xf1be, + fa_space_shuttle = 0xf197, + fa_spinner = 0xf110, + fa_spoon = 0xf1b1, + fa_spotify = 0xf1bc, + fa_square = 0xf0c8, + fa_square_o = 0xf096, + fa_stack_exchange = 0xf18d, + fa_stack_overflow = 0xf16c, + fa_star = 0xf005, + fa_star_half = 0xf089, + fa_star_half_empty = 0xf123, + fa_star_half_full = 0xf123, + fa_star_half_o = 0xf123, + fa_star_o = 0xf006, + fa_steam = 0xf1b6, + fa_steam_square = 0xf1b7, + fa_step_backward = 0xf048, + fa_step_forward = 0xf051, + fa_stethoscope = 0xf0f1, + fa_sticky_note = 0xf249, + fa_sticky_note_o = 0xf24a, + fa_stop = 0xf04d, + fa_stop_circle = 0xf28d, + fa_stop_circle_o = 0xf28e, + fa_street_view = 0xf21d, + fa_strikethrough = 0xf0cc, + fa_stumbleupon = 0xf1a4, + fa_stumbleupon_circle = 0xf1a3, + fa_subscript = 0xf12c, + fa_subway = 0xf239, + fa_suitcase = 0xf0f2, + fa_sun_o = 0xf185, + fa_superpowers = 0xf2dd, + fa_superscript = 0xf12b, + fa_support = 0xf1cd, + fa_table = 0xf0ce, + fa_tablet = 0xf10a, + fa_tachometer = 0xf0e4, + fa_tag = 0xf02b, + fa_tags = 0xf02c, + fa_tasks = 0xf0ae, + fa_taxi = 0xf1ba, + fa_telegram = 0xf2c6, + fa_television = 0xf26c, + fa_tencent_weibo = 0xf1d5, + fa_terminal = 0xf120, + fa_text_height = 0xf034, + fa_text_width = 0xf035, + fa_th = 0xf00a, + fa_th_large = 0xf009, + fa_th_list = 0xf00b, + fa_themeisle = 0xf2b2, + fa_thermometer = 0xf2c7, + fa_thermometer_0 = 0xf2cb, + fa_thermometer_1 = 0xf2ca, + fa_thermometer_2 = 0xf2c9, + fa_thermometer_3 = 0xf2c8, + fa_thermometer_4 = 0xf2c7, + fa_thermometer_empty = 0xf2cb, + fa_thermometer_full = 0xf2c7, + fa_thermometer_half = 0xf2c9, + fa_thermometer_quarter = 0xf2ca, + fa_thermometer_three_quarters = 0xf2c8, + fa_thumb_tack = 0xf08d, + fa_thumbs_down = 0xf165, + fa_thumbs_o_down = 0xf088, + fa_thumbs_o_up = 0xf087, + fa_thumbs_up = 0xf164, + fa_ticket = 0xf145, + fa_times = 0xf00d, + fa_times_circle = 0xf057, + fa_times_circle_o = 0xf05c, + fa_times_rectangle = 0xf2d3, + fa_times_rectangle_o = 0xf2d4, + fa_tint = 0xf043, + fa_toggle_down = 0xf150, + fa_toggle_left = 0xf191, + fa_toggle_off = 0xf204, + fa_toggle_on = 0xf205, + fa_toggle_right = 0xf152, + fa_toggle_up = 0xf151, + fa_trademark = 0xf25c, + fa_train = 0xf238, + fa_transgender = 0xf224, + fa_transgender_alt = 0xf225, + fa_trash = 0xf1f8, + fa_trash_o = 0xf014, + fa_tree = 0xf1bb, + fa_trello = 0xf181, + fa_tripadvisor = 0xf262, + fa_trophy = 0xf091, + fa_truck = 0xf0d1, + fa_try = 0xf195, + fa_tty = 0xf1e4, + fa_tumblr = 0xf173, + fa_tumblr_square = 0xf174, + fa_turkish_lira = 0xf195, + fa_tv = 0xf26c, + fa_twitch = 0xf1e8, + fa_twitter = 0xf099, + fa_twitter_square = 0xf081, + fa_umbrella = 0xf0e9, + fa_underline = 0xf0cd, + fa_undo = 0xf0e2, + fa_universal_access = 0xf29a, + fa_university = 0xf19c, + fa_unlink = 0xf127, + fa_unlock = 0xf09c, + fa_unlock_alt = 0xf13e, + fa_unsorted = 0xf0dc, + fa_upload = 0xf093, + fa_usb = 0xf287, + fa_usd = 0xf155, + fa_user = 0xf007, + fa_user_circle = 0xf2bd, + fa_user_circle_o = 0xf2be, + fa_user_md = 0xf0f0, + fa_user_o = 0xf2c0, + fa_user_plus = 0xf234, + fa_user_secret = 0xf21b, + fa_user_times = 0xf235, + fa_users = 0xf0c0, + fa_vcard = 0xf2bb, + fa_vcard_o = 0xf2bc, + fa_venus = 0xf221, + fa_venus_double = 0xf226, + fa_venus_mars = 0xf228, + fa_viacoin = 0xf237, + fa_viadeo = 0xf2a9, + fa_viadeo_square = 0xf2aa, + fa_video_camera = 0xf03d, + fa_vimeo = 0xf27d, + fa_vimeo_square = 0xf194, + fa_vine = 0xf1ca, + fa_vk = 0xf189, + fa_volume_control_phone = 0xf2a0, + fa_volume_down = 0xf027, + fa_volume_off = 0xf026, + fa_volume_up = 0xf028, + fa_warning = 0xf071, + fa_wechat = 0xf1d7, + fa_weibo = 0xf18a, + fa_weixin = 0xf1d7, + fa_whatsapp = 0xf232, + fa_wheelchair = 0xf193, + fa_wheelchair_alt = 0xf29b, + fa_wifi = 0xf1eb, + fa_wikipedia_w = 0xf266, + fa_window_close = 0xf2d3, + fa_window_close_o = 0xf2d4, + fa_window_maximize = 0xf2d0, + fa_window_minimize = 0xf2d1, + fa_window_restore = 0xf2d2, + fa_windows = 0xf17a, + fa_won = 0xf159, + fa_wordpress = 0xf19a, + fa_wpbeginner = 0xf297, + fa_wpexplorer = 0xf2de, + fa_wpforms = 0xf298, + fa_wrench = 0xf0ad, + fa_xing = 0xf168, + fa_xing_square = 0xf169, + fa_y_combinator = 0xf23b, + fa_y_combinator_square = 0xf1d4, + fa_yahoo = 0xf19e, + fa_yc = 0xf23b, + fa_yc_square = 0xf1d4, + fa_yelp = 0xf1e9, + fa_yen = 0xf157, + fa_yoast = 0xf2b1, + fa_youtube = 0xf167, + fa_youtube_play = 0xf16a, + fa_youtube_square = 0xf166, + + // ruleset icons in circles + fa_osu_osu_o = 0xe000, + fa_osu_mania_o = 0xe001, + fa_osu_fruits_o = 0xe002, + fa_osu_taiko_o = 0xe003, + + // ruleset icons without circles + fa_osu_filled_circle = 0xe004, + fa_osu_cross_o = 0xe005, + fa_osu_logo = 0xe006, + fa_osu_chevron_down_o = 0xe007, + fa_osu_edit_o = 0xe033, + fa_osu_left_o = 0xe034, + fa_osu_right_o = 0xe035, + fa_osu_charts = 0xe036, + fa_osu_solo = 0xe037, + fa_osu_multi = 0xe038, + fa_osu_gear = 0xe039, + + // misc icons + fa_osu_bat = 0xe008, + fa_osu_bubble = 0xe009, + fa_osu_bubble_pop = 0xe02e, + fa_osu_dice = 0xe011, + fa_osu_heart1 = 0xe02f, + fa_osu_heart1_break = 0xe030, + fa_osu_hot = 0xe031, + fa_osu_list_search = 0xe032, + + //osu! playstyles + fa_osu_playstyle_tablet = 0xe02a, + fa_osu_playstyle_mouse = 0xe029, + fa_osu_playstyle_keyboard = 0xe02b, + fa_osu_playstyle_touch = 0xe02c, + + // osu! difficulties + fa_osu_easy_osu = 0xe015, + fa_osu_normal_osu = 0xe016, + fa_osu_hard_osu = 0xe017, + fa_osu_insane_osu = 0xe018, + fa_osu_expert_osu = 0xe019, + + // taiko difficulties + fa_osu_easy_taiko = 0xe01a, + fa_osu_normal_taiko = 0xe01b, + fa_osu_hard_taiko = 0xe01c, + fa_osu_insane_taiko = 0xe01d, + fa_osu_expert_taiko = 0xe01e, + + // fruits difficulties + fa_osu_easy_fruits = 0xe01f, + fa_osu_normal_fruits = 0xe020, + fa_osu_hard_fruits = 0xe021, + fa_osu_insane_fruits = 0xe022, + fa_osu_expert_fruits = 0xe023, + + // mania difficulties + fa_osu_easy_mania = 0xe024, + fa_osu_normal_mania = 0xe025, + fa_osu_hard_mania = 0xe026, + fa_osu_insane_mania = 0xe027, + fa_osu_expert_mania = 0xe028, + + // mod icons + fa_osu_mod_perfect = 0xe049, + fa_osu_mod_autopilot = 0xe03a, + fa_osu_mod_auto = 0xe03b, + fa_osu_mod_cinema = 0xe03c, + fa_osu_mod_doubletime = 0xe03d, + fa_osu_mod_easy = 0xe03e, + fa_osu_mod_flashlight = 0xe03f, + fa_osu_mod_halftime = 0xe040, + fa_osu_mod_hardrock = 0xe041, + fa_osu_mod_hidden = 0xe042, + fa_osu_mod_nightcore = 0xe043, + fa_osu_mod_nofail = 0xe044, + fa_osu_mod_relax = 0xe045, + fa_osu_mod_spunout = 0xe046, + fa_osu_mod_suddendeath = 0xe047, + fa_osu_mod_target = 0xe048, + fa_osu_mod_bg = 0xe04a, + } +} diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs index 8c1c78906d..c9389bb9e2 100644 --- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs +++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs @@ -1,63 +1,63 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Transforms; - -namespace osu.Game.Graphics.Sprites -{ - public class OsuSpriteText : SpriteText - { - public const float FONT_SIZE = 16; - - public OsuSpriteText() - { - Shadow = true; - TextSize = FONT_SIZE; - } - - protected override Drawable CreateFallbackCharacterDrawable() - { - var tex = GetTextureForCharacter('?'); - - if (tex != null) - { - float adjust = (RNG.NextSingle() - 0.5f) * 2; - return new Sprite - { - Texture = tex, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Scale = new Vector2(1 + adjust * 0.2f), - Rotation = adjust * 15, - Colour = Color4.White, - }; - } - - return base.CreateFallbackCharacterDrawable(); - } - } - - public static class OsuSpriteTextTransformExtensions - { - /// - /// Sets to a new value after a duration. - /// - /// A to which further transforms can be added. - public static TransformSequence TransformTextTo(this T spriteText, string newText, double duration = 0, Easing easing = Easing.None) - where T : OsuSpriteText - => spriteText.TransformTo(nameof(OsuSpriteText.Text), newText, duration, easing); - - /// - /// Sets to a new value after a duration. - /// - /// A to which further transforms can be added. - public static TransformSequence TransformTextTo(this TransformSequence t, string newText, double duration = 0, Easing easing = Easing.None) - where T : OsuSpriteText - => t.Append(o => o.TransformTextTo(newText, duration, easing)); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.MathUtils; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Graphics.Sprites +{ + public class OsuSpriteText : SpriteText + { + public const float FONT_SIZE = 16; + + public OsuSpriteText() + { + Shadow = true; + TextSize = FONT_SIZE; + } + + protected override Drawable CreateFallbackCharacterDrawable() + { + var tex = GetTextureForCharacter('?'); + + if (tex != null) + { + float adjust = (RNG.NextSingle() - 0.5f) * 2; + return new Sprite + { + Texture = tex, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Scale = new Vector2(1 + adjust * 0.2f), + Rotation = adjust * 15, + Colour = Color4.White, + }; + } + + return base.CreateFallbackCharacterDrawable(); + } + } + + public static class OsuSpriteTextTransformExtensions + { + /// + /// Sets to a new value after a duration. + /// + /// A to which further transforms can be added. + public static TransformSequence TransformTextTo(this T spriteText, string newText, double duration = 0, Easing easing = Easing.None) + where T : OsuSpriteText + => spriteText.TransformTo(nameof(OsuSpriteText.Text), newText, duration, easing); + + /// + /// Sets to a new value after a duration. + /// + /// A to which further transforms can be added. + public static TransformSequence TransformTextTo(this TransformSequence t, string newText, double duration = 0, Easing easing = Easing.None) + where T : OsuSpriteText + => t.Append(o => o.TransformTextTo(newText, duration, easing)); + } +} diff --git a/osu.Game/Graphics/Textures/LargeTextureStore.cs b/osu.Game/Graphics/Textures/LargeTextureStore.cs index 17fa7d4e24..4dcbb1220d 100644 --- a/osu.Game/Graphics/Textures/LargeTextureStore.cs +++ b/osu.Game/Graphics/Textures/LargeTextureStore.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; - -namespace osu.Game.Graphics.Textures -{ - /// - /// A texture store that bypasses atlasing. - /// - public class LargeTextureStore : TextureStore - { - public LargeTextureStore(IResourceStore store = null) : base(store, false) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; + +namespace osu.Game.Graphics.Textures +{ + /// + /// A texture store that bypasses atlasing. + /// + public class LargeTextureStore : TextureStore + { + public LargeTextureStore(IResourceStore store = null) : base(store, false) + { + } + } +} diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index b5455d834a..19135b8550 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; - -namespace osu.Game.Graphics.UserInterface -{ - public class BackButton : TwoLayerButton - { - public BackButton() - { - Text = @"back"; - Icon = FontAwesome.fa_osu_left_o; - Anchor = Anchor.BottomLeft; - Origin = Anchor.BottomLeft; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Pink; - HoverColour = colours.PinkDark; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class BackButton : TwoLayerButton + { + public BackButton() + { + Text = @"back"; + Icon = FontAwesome.fa_osu_left_o; + Anchor = Anchor.BottomLeft; + Origin = Anchor.BottomLeft; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Pink; + HoverColour = colours.PinkDark; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/Bar.cs b/osu.Game/Graphics/UserInterface/Bar.cs index 6381c242fe..16c63a12f4 100644 --- a/osu.Game/Graphics/UserInterface/Bar.cs +++ b/osu.Game/Graphics/UserInterface/Bar.cs @@ -1,138 +1,138 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using System; - -namespace osu.Game.Graphics.UserInterface -{ - public class Bar : Container, IHasAccentColour - { - private readonly Box background; - private readonly Box bar; - - private const int resize_duration = 250; - - private const Easing easing = Easing.InOutCubic; - - private float length; - - /// - /// Length of the bar, ranges from 0 to 1 - /// - public float Length - { - get - { - return length; - } - set - { - length = MathHelper.Clamp(value, 0, 1); - updateBarLength(); - } - } - - public Color4 BackgroundColour - { - get - { - return background.Colour; - } - set - { - background.Colour = value; - } - } - - public Color4 AccentColour - { - get - { - return bar.Colour; - } - set - { - bar.Colour = value; - } - } - - private BarDirection direction = BarDirection.LeftToRight; - public BarDirection Direction - { - get - { - return direction; - } - set - { - direction = value; - updateBarLength(); - } - } - - public Bar() - { - Children = new[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(0, 0, 0, 0) - }, - bar = new Box - { - RelativeSizeAxes = Axes.Both, - Width = 0, - }, - }; - } - - private void updateBarLength() - { - switch (direction) - { - case BarDirection.LeftToRight: - case BarDirection.RightToLeft: - bar.ResizeTo(new Vector2(length, 1), resize_duration, easing); - break; - - case BarDirection.TopToBottom: - case BarDirection.BottomToTop: - bar.ResizeTo(new Vector2(1, length), resize_duration, easing); - break; - } - - switch (direction) - { - case BarDirection.LeftToRight: - case BarDirection.TopToBottom: - bar.Anchor = Anchor.TopLeft; - bar.Origin = Anchor.TopLeft; - break; - - case BarDirection.RightToLeft: - case BarDirection.BottomToTop: - bar.Anchor = Anchor.BottomRight; - bar.Origin = Anchor.BottomRight; - break; - } - } - } - - [Flags] - public enum BarDirection - { - LeftToRight = 1 << 0, - RightToLeft = 1 << 1, - TopToBottom = 1 << 2, - BottomToTop = 1 << 3, - - Vertical = TopToBottom | BottomToTop, - Horizontal = LeftToRight | RightToLeft, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using System; + +namespace osu.Game.Graphics.UserInterface +{ + public class Bar : Container, IHasAccentColour + { + private readonly Box background; + private readonly Box bar; + + private const int resize_duration = 250; + + private const Easing easing = Easing.InOutCubic; + + private float length; + + /// + /// Length of the bar, ranges from 0 to 1 + /// + public float Length + { + get + { + return length; + } + set + { + length = MathHelper.Clamp(value, 0, 1); + updateBarLength(); + } + } + + public Color4 BackgroundColour + { + get + { + return background.Colour; + } + set + { + background.Colour = value; + } + } + + public Color4 AccentColour + { + get + { + return bar.Colour; + } + set + { + bar.Colour = value; + } + } + + private BarDirection direction = BarDirection.LeftToRight; + public BarDirection Direction + { + get + { + return direction; + } + set + { + direction = value; + updateBarLength(); + } + } + + public Bar() + { + Children = new[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(0, 0, 0, 0) + }, + bar = new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0, + }, + }; + } + + private void updateBarLength() + { + switch (direction) + { + case BarDirection.LeftToRight: + case BarDirection.RightToLeft: + bar.ResizeTo(new Vector2(length, 1), resize_duration, easing); + break; + + case BarDirection.TopToBottom: + case BarDirection.BottomToTop: + bar.ResizeTo(new Vector2(1, length), resize_duration, easing); + break; + } + + switch (direction) + { + case BarDirection.LeftToRight: + case BarDirection.TopToBottom: + bar.Anchor = Anchor.TopLeft; + bar.Origin = Anchor.TopLeft; + break; + + case BarDirection.RightToLeft: + case BarDirection.BottomToTop: + bar.Anchor = Anchor.BottomRight; + bar.Origin = Anchor.BottomRight; + break; + } + } + } + + [Flags] + public enum BarDirection + { + LeftToRight = 1 << 0, + RightToLeft = 1 << 1, + TopToBottom = 1 << 2, + BottomToTop = 1 << 3, + + Vertical = TopToBottom | BottomToTop, + Horizontal = LeftToRight | RightToLeft, + } +} diff --git a/osu.Game/Graphics/UserInterface/BarGraph.cs b/osu.Game/Graphics/UserInterface/BarGraph.cs index 3bc1598191..d065ecdd5f 100644 --- a/osu.Game/Graphics/UserInterface/BarGraph.cs +++ b/osu.Game/Graphics/UserInterface/BarGraph.cs @@ -1,77 +1,77 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Graphics.UserInterface -{ - public class BarGraph : FillFlowContainer - { - /// - /// Manually sets the max value, if null is instead used - /// - public float? MaxValue { get; set; } - - private BarDirection direction = BarDirection.BottomToTop; - public new BarDirection Direction - { - get - { - return direction; - } - set - { - direction = value; - base.Direction = (direction & BarDirection.Horizontal) > 0 ? FillDirection.Vertical : FillDirection.Horizontal; - foreach (var bar in Children) - { - bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1); - bar.Direction = direction; - } - } - } - - /// - /// A list of floats that defines the length of each - /// - public IEnumerable Values - { - set - { - List bars = Children.ToList(); - foreach (var bar in value.Select((length, index) => new { Value = length, Bar = bars.Count > index ? bars[index] : null })) - { - float length = MaxValue ?? value.Max(); - if (length != 0) - length = bar.Value / length; - - float size = value.Count(); - if (size != 0) - size = 1.0f / size; - - if (bar.Bar != null) - { - bar.Bar.Length = length; - bar.Bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, size) : new Vector2(size, 1); - } - else - { - Add(new Bar - { - RelativeSizeAxes = Axes.Both, - Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, size) : new Vector2(size, 1), - Length = length, - Direction = Direction, - }); - } - } - //I'm using ToList() here because Where() returns an Enumerable which can change it's elements afterwards - RemoveRange(Children.Where((bar, index) => index >= value.Count()).ToList()); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Graphics.UserInterface +{ + public class BarGraph : FillFlowContainer + { + /// + /// Manually sets the max value, if null is instead used + /// + public float? MaxValue { get; set; } + + private BarDirection direction = BarDirection.BottomToTop; + public new BarDirection Direction + { + get + { + return direction; + } + set + { + direction = value; + base.Direction = (direction & BarDirection.Horizontal) > 0 ? FillDirection.Vertical : FillDirection.Horizontal; + foreach (var bar in Children) + { + bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1); + bar.Direction = direction; + } + } + } + + /// + /// A list of floats that defines the length of each + /// + public IEnumerable Values + { + set + { + List bars = Children.ToList(); + foreach (var bar in value.Select((length, index) => new { Value = length, Bar = bars.Count > index ? bars[index] : null })) + { + float length = MaxValue ?? value.Max(); + if (length != 0) + length = bar.Value / length; + + float size = value.Count(); + if (size != 0) + size = 1.0f / size; + + if (bar.Bar != null) + { + bar.Bar.Length = length; + bar.Bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, size) : new Vector2(size, 1); + } + else + { + Add(new Bar + { + RelativeSizeAxes = Axes.Both, + Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, size) : new Vector2(size, 1), + Length = length, + Direction = Direction, + }); + } + } + //I'm using ToList() here because Where() returns an Enumerable which can change it's elements afterwards + RemoveRange(Children.Where((bar, index) => index >= value.Count()).ToList()); + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index 7f30bd715a..3f59eeca97 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -1,94 +1,94 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using osu.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using System.Linq; - -namespace osu.Game.Graphics.UserInterface -{ - public class BreadcrumbControl : OsuTabControl - { - private const float padding = 10; - - protected override TabItem CreateTabItem(T value) => new BreadcrumbTabItem(value); - - protected override float StripWidth() => base.StripWidth() - (padding + 8); - - public BreadcrumbControl() - { - Height = 26; - TabContainer.Spacing = new Vector2(padding, 0f); - Current.ValueChanged += tab => - { - foreach (var t in TabContainer.Children.OfType()) - { - var tIndex = TabContainer.IndexOf(t); - var tabIndex = TabContainer.IndexOf(TabMap[tab]); - - t.State = tIndex < tabIndex ? Visibility.Hidden : Visibility.Visible; - t.Chevron.FadeTo(tIndex <= tabIndex ? 0f : 1f, 500, Easing.OutQuint); - } - }; - } - - private class BreadcrumbTabItem : OsuTabItem, IStateful - { - public event Action StateChanged; - - public readonly SpriteIcon Chevron; - - //don't allow clicking between transitions and don't make the chevron clickable - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos); - - public override bool HandleKeyboardInput => State == Visibility.Visible; - public override bool HandleMouseInput => State == Visibility.Visible; - - private Visibility state; - - public Visibility State - { - get { return state; } - set - { - if (value == state) return; - state = value; - - const float transition_duration = 500; - - if (State == Visibility.Visible) - { - this.FadeIn(transition_duration, Easing.OutQuint); - this.ScaleTo(new Vector2(1f), transition_duration, Easing.OutQuint); - } - else - { - this.FadeOut(transition_duration, Easing.OutQuint); - this.ScaleTo(new Vector2(0.8f, 1f), transition_duration, Easing.OutQuint); - } - - StateChanged?.Invoke(State); - } - } - - public BreadcrumbTabItem(T value) : base(value) - { - Text.TextSize = 16; - Padding = new MarginPadding { Right = padding + 8 }; //padding + chevron width - Add(Chevron = new SpriteIcon - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreLeft, - Size = new Vector2(12), - Icon = FontAwesome.fa_chevron_right, - Margin = new MarginPadding { Left = padding }, - Alpha = 0f, - }); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using osu.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using System.Linq; + +namespace osu.Game.Graphics.UserInterface +{ + public class BreadcrumbControl : OsuTabControl + { + private const float padding = 10; + + protected override TabItem CreateTabItem(T value) => new BreadcrumbTabItem(value); + + protected override float StripWidth() => base.StripWidth() - (padding + 8); + + public BreadcrumbControl() + { + Height = 26; + TabContainer.Spacing = new Vector2(padding, 0f); + Current.ValueChanged += tab => + { + foreach (var t in TabContainer.Children.OfType()) + { + var tIndex = TabContainer.IndexOf(t); + var tabIndex = TabContainer.IndexOf(TabMap[tab]); + + t.State = tIndex < tabIndex ? Visibility.Hidden : Visibility.Visible; + t.Chevron.FadeTo(tIndex <= tabIndex ? 0f : 1f, 500, Easing.OutQuint); + } + }; + } + + private class BreadcrumbTabItem : OsuTabItem, IStateful + { + public event Action StateChanged; + + public readonly SpriteIcon Chevron; + + //don't allow clicking between transitions and don't make the chevron clickable + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos); + + public override bool HandleKeyboardInput => State == Visibility.Visible; + public override bool HandleMouseInput => State == Visibility.Visible; + + private Visibility state; + + public Visibility State + { + get { return state; } + set + { + if (value == state) return; + state = value; + + const float transition_duration = 500; + + if (State == Visibility.Visible) + { + this.FadeIn(transition_duration, Easing.OutQuint); + this.ScaleTo(new Vector2(1f), transition_duration, Easing.OutQuint); + } + else + { + this.FadeOut(transition_duration, Easing.OutQuint); + this.ScaleTo(new Vector2(0.8f, 1f), transition_duration, Easing.OutQuint); + } + + StateChanged?.Invoke(State); + } + } + + public BreadcrumbTabItem(T value) : base(value) + { + Text.TextSize = 16; + Padding = new MarginPadding { Right = padding + 8 }; //padding + chevron width + Add(Chevron = new SpriteIcon + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreLeft, + Size = new Vector2(12), + Icon = FontAwesome.fa_chevron_right, + Margin = new MarginPadding { Left = padding }, + Alpha = 0f, + }); + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 99da8755bf..17d22587ad 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -1,284 +1,284 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; -using osu.Framework.Extensions.Color4Extensions; -using osu.Game.Graphics.Containers; -using osu.Framework.Configuration; -using osu.Framework.Input; - -namespace osu.Game.Graphics.UserInterface -{ - public class DialogButton : OsuClickableContainer - { - private const float hover_width = 0.9f; - private const float hover_duration = 500; - private const float glow_fade_duration = 250; - private const float click_duration = 200; - - public readonly BindableBool Selected = new BindableBool(); - - private readonly Container backgroundContainer; - private readonly Container colourContainer; - private readonly Container glowContainer; - private readonly Box leftGlow; - private readonly Box centerGlow; - private readonly Box rightGlow; - private readonly Box background; - private readonly SpriteText spriteText; - private Vector2 hoverSpacing => new Vector2(3f, 0f); - - public DialogButton() - { - RelativeSizeAxes = Axes.X; - - Children = new Drawable[] - { - backgroundContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Width = 1f, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = backgroundColour, - }, - }, - }, - glowContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Width = 1f, - Alpha = 0f, - Children = new Drawable[] - { - leftGlow = new Box - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Width = 0.125f, - }, - centerGlow = new Box - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Width = 0.75f, - }, - rightGlow = new Box - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Width = 0.125f, - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Masking = true, - Children = new Drawable[] - { - colourContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Width = 0.8f, - Masking = true, - MaskingSmoothness = 2, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.2f), - Radius = 5, - }, - Colour = ButtonColour, - Shear = new Vector2(0.2f, 0), - Children = new Drawable[] - { - new Box - { - EdgeSmoothness = new Vector2(2, 0), - RelativeSizeAxes = Axes.Both, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - MaskingSmoothness = 0, - Children = new[] - { - new Triangles - { - RelativeSizeAxes = Axes.Both, - TriangleScale = 4, - ColourDark = OsuColour.Gray(0.88f), - Shear = new Vector2(-0.2f, 0), - }, - }, - }, - }, - }, - }, - }, - spriteText = new OsuSpriteText - { - Text = Text, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextSize = 28, - Font = "Exo2.0-Bold", - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.1f), - Colour = Color4.White, - }, - }; - - updateGlow(); - - Selected.ValueChanged += selectionChanged; - } - - private Color4 buttonColour; - public Color4 ButtonColour - { - get - { - return buttonColour; - } - set - { - buttonColour = value; - updateGlow(); - colourContainer.Colour = value; - } - } - - private Color4 backgroundColour = OsuColour.Gray(34); - public Color4 BackgroundColour - { - get - { - return backgroundColour; - } - set - { - backgroundColour = value; - background.Colour = value; - } - } - - private string text; - public string Text - { - get - { - return text; - } - set - { - text = value; - spriteText.Text = Text; - } - } - - private float textSize = 28; - public float TextSize - { - get - { - return textSize; - } - set - { - textSize = value; - spriteText.TextSize = value; - } - } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos); - - protected override bool OnClick(InputState state) - { - colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); - flash(); - - this.Delay(click_duration).Schedule(delegate - { - colourContainer.ResizeTo(new Vector2(0.8f, 1f)); - spriteText.Spacing = Vector2.Zero; - glowContainer.FadeOut(); - }); - - return base.OnClick(state); - } - - protected override bool OnHover(InputState state) - { - base.OnHover(state); - - Selected.Value = true; - return true; - } - - protected override void OnHoverLost(InputState state) - { - base.OnHoverLost(state); - Selected.Value = false; - } - - private void selectionChanged(bool isSelected) - { - if (isSelected) - { - spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic); - colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic); - glowContainer.FadeIn(glow_fade_duration, Easing.Out); - } - else - { - colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic); - spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic); - glowContainer.FadeOut(glow_fade_duration, Easing.Out); - } - } - - private void flash() - { - var flash = new Box - { - RelativeSizeAxes = Axes.Both - }; - - colourContainer.Add(flash); - - flash.Colour = ButtonColour; - flash.Blending = BlendingMode.Additive; - flash.Alpha = 0.3f; - flash.FadeOutFromOne(click_duration); - flash.Expire(); - } - - private void updateGlow() - { - leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour); - centerGlow.Colour = ButtonColour; - rightGlow.Colour = ColourInfo.GradientHorizontal(ButtonColour, new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; +using osu.Framework.Configuration; +using osu.Framework.Input; + +namespace osu.Game.Graphics.UserInterface +{ + public class DialogButton : OsuClickableContainer + { + private const float hover_width = 0.9f; + private const float hover_duration = 500; + private const float glow_fade_duration = 250; + private const float click_duration = 200; + + public readonly BindableBool Selected = new BindableBool(); + + private readonly Container backgroundContainer; + private readonly Container colourContainer; + private readonly Container glowContainer; + private readonly Box leftGlow; + private readonly Box centerGlow; + private readonly Box rightGlow; + private readonly Box background; + private readonly SpriteText spriteText; + private Vector2 hoverSpacing => new Vector2(3f, 0f); + + public DialogButton() + { + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + backgroundContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Width = 1f, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = backgroundColour, + }, + }, + }, + glowContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Width = 1f, + Alpha = 0f, + Children = new Drawable[] + { + leftGlow = new Box + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + Width = 0.125f, + }, + centerGlow = new Box + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Width = 0.75f, + }, + rightGlow = new Box + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Width = 0.125f, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Masking = true, + Children = new Drawable[] + { + colourContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Width = 0.8f, + Masking = true, + MaskingSmoothness = 2, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Radius = 5, + }, + Colour = ButtonColour, + Shear = new Vector2(0.2f, 0), + Children = new Drawable[] + { + new Box + { + EdgeSmoothness = new Vector2(2, 0), + RelativeSizeAxes = Axes.Both, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + MaskingSmoothness = 0, + Children = new[] + { + new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 4, + ColourDark = OsuColour.Gray(0.88f), + Shear = new Vector2(-0.2f, 0), + }, + }, + }, + }, + }, + }, + }, + spriteText = new OsuSpriteText + { + Text = Text, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextSize = 28, + Font = "Exo2.0-Bold", + Shadow = true, + ShadowColour = new Color4(0, 0, 0, 0.1f), + Colour = Color4.White, + }, + }; + + updateGlow(); + + Selected.ValueChanged += selectionChanged; + } + + private Color4 buttonColour; + public Color4 ButtonColour + { + get + { + return buttonColour; + } + set + { + buttonColour = value; + updateGlow(); + colourContainer.Colour = value; + } + } + + private Color4 backgroundColour = OsuColour.Gray(34); + public Color4 BackgroundColour + { + get + { + return backgroundColour; + } + set + { + backgroundColour = value; + background.Colour = value; + } + } + + private string text; + public string Text + { + get + { + return text; + } + set + { + text = value; + spriteText.Text = Text; + } + } + + private float textSize = 28; + public float TextSize + { + get + { + return textSize; + } + set + { + textSize = value; + spriteText.TextSize = value; + } + } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos); + + protected override bool OnClick(InputState state) + { + colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); + flash(); + + this.Delay(click_duration).Schedule(delegate + { + colourContainer.ResizeTo(new Vector2(0.8f, 1f)); + spriteText.Spacing = Vector2.Zero; + glowContainer.FadeOut(); + }); + + return base.OnClick(state); + } + + protected override bool OnHover(InputState state) + { + base.OnHover(state); + + Selected.Value = true; + return true; + } + + protected override void OnHoverLost(InputState state) + { + base.OnHoverLost(state); + Selected.Value = false; + } + + private void selectionChanged(bool isSelected) + { + if (isSelected) + { + spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic); + colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic); + glowContainer.FadeIn(glow_fade_duration, Easing.Out); + } + else + { + colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic); + spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic); + glowContainer.FadeOut(glow_fade_duration, Easing.Out); + } + } + + private void flash() + { + var flash = new Box + { + RelativeSizeAxes = Axes.Both + }; + + colourContainer.Add(flash); + + flash.Colour = ButtonColour; + flash.Blending = BlendingMode.Additive; + flash.Alpha = 0.3f; + flash.FadeOutFromOne(click_duration); + flash.Expire(); + } + + private void updateGlow() + { + leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour); + centerGlow.Colour = ButtonColour; + rightGlow.Colour = ColourInfo.GradientHorizontal(ButtonColour, new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f)); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 33786252ab..6500578de3 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -1,58 +1,58 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Input; -using System; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// A textbox which holds focus eagerly. - /// - public class FocusedTextBox : OsuTextBox - { - protected override Color4 BackgroundUnfocused => new Color4(10, 10, 10, 255); - protected override Color4 BackgroundFocused => new Color4(10, 10, 10, 255); - - public Action Exit; - - private bool focus; - public bool HoldFocus - { - get { return focus; } - set - { - focus = value; - if (!focus && HasFocus) - GetContainingInputManager().ChangeFocus(null); - } - } - - // We may not be focused yet, but we need to handle keyboard input to be able to request focus - public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput; - - protected override void OnFocus(InputState state) - { - base.OnFocus(state); - BorderThickness = 0; - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (!args.Repeat && args.Key == Key.Escape) - { - if (Text.Length > 0) - Text = string.Empty; - else - Exit?.Invoke(); - return true; - } - - return base.OnKeyDown(state, args); - } - - public override bool RequestsFocus => HoldFocus; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Input; +using System; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A textbox which holds focus eagerly. + /// + public class FocusedTextBox : OsuTextBox + { + protected override Color4 BackgroundUnfocused => new Color4(10, 10, 10, 255); + protected override Color4 BackgroundFocused => new Color4(10, 10, 10, 255); + + public Action Exit; + + private bool focus; + public bool HoldFocus + { + get { return focus; } + set + { + focus = value; + if (!focus && HasFocus) + GetContainingInputManager().ChangeFocus(null); + } + } + + // We may not be focused yet, but we need to handle keyboard input to be able to request focus + public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput; + + protected override void OnFocus(InputState state) + { + base.OnFocus(state); + BorderThickness = 0; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!args.Repeat && args.Key == Key.Escape) + { + if (Text.Length > 0) + Text = string.Empty; + else + Exit?.Invoke(); + return true; + } + + return base.OnKeyDown(state, args); + } + + public override bool RequestsFocus => HoldFocus; + } +} diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 9fb0594524..1a9d124dfb 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -1,36 +1,36 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Extensions; -using osu.Framework.Input; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// Adds hover and click sounds to a drawable. - /// Does not draw anything. - /// - public class HoverClickSounds : HoverSounds - { - private SampleChannel sampleClick; - - public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) : base(sampleSet) - { - } - - protected override bool OnClick(InputState state) - { - sampleClick?.Play(); - return base.OnClick(state); - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; +using osu.Framework.Input; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Adds hover and click sounds to a drawable. + /// Does not draw anything. + /// + public class HoverClickSounds : HoverSounds + { + private SampleChannel sampleClick; + + public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) : base(sampleSet) + { + } + + protected override bool OnClick(InputState state) + { + sampleClick?.Play(); + return base.OnClick(state); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}"); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index fabb344872..dc35c75cd6 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -1,53 +1,53 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// Adds hover sounds to a drawable. - /// Does not draw anything. - /// - public class HoverSounds : CompositeDrawable - { - private SampleChannel sampleHover; - - protected readonly HoverSampleSet SampleSet; - - public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) - { - SampleSet = sampleSet; - RelativeSizeAxes = Axes.Both; - } - - protected override bool OnHover(InputState state) - { - sampleHover?.Play(); - return base.OnHover(state); - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); - } - } - - public enum HoverSampleSet - { - [Description("")] - Loud, - [Description("-soft")] - Normal, - [Description("-softer")] - Soft - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Adds hover sounds to a drawable. + /// Does not draw anything. + /// + public class HoverSounds : CompositeDrawable + { + private SampleChannel sampleHover; + + protected readonly HoverSampleSet SampleSet; + + public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) + { + SampleSet = sampleSet; + RelativeSizeAxes = Axes.Both; + } + + protected override bool OnHover(InputState state) + { + sampleHover?.Play(); + return base.OnHover(state); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); + } + } + + public enum HoverSampleSet + { + [Description("")] + Loud, + [Description("-soft")] + Normal, + [Description("-softer")] + Soft + } +} diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs index 5b266d9a59..5a25fe641d 100644 --- a/osu.Game/Graphics/UserInterface/IconButton.cs +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -1,180 +1,180 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Graphics.UserInterface -{ - public class IconButton : OsuClickableContainer - { - public const float BUTTON_SIZE = 30; - - private Color4? flashColour; - /// - /// The colour that should be flashed when the is clicked. - /// - public Color4 FlashColour - { - get { return flashColour ?? Color4.White; } - set { flashColour = value; } - } - - private Color4? iconColour; - /// - /// The icon colour. This does not affect . - /// - public Color4 IconColour - { - get { return iconColour ?? Color4.White; } - set - { - iconColour = value; - icon.Colour = value; - } - } - - private Color4? iconHoverColour; - /// - /// The icon colour while the is hovered. - /// - public Color4 IconHoverColour - { - get { return iconHoverColour ?? IconColour; } - set { iconHoverColour = value; } - } - - private Color4? hoverColour; - /// - /// The background colour of the while it is hovered. - /// - public Color4 HoverColour - { - get { return hoverColour ?? Color4.White; } - set - { - hoverColour = value; - hover.Colour = value; - } - } - - /// - /// The icon. - /// - public FontAwesome Icon - { - get { return icon.Icon; } - set { icon.Icon = value; } - } - - /// - /// The icon scale. This does not affect . - /// - public Vector2 IconScale - { - get { return icon.Scale; } - set { icon.Scale = value; } - } - - /// - /// The size of the while it is not being pressed. - /// - public Vector2 ButtonSize - { - get { return content.Size; } - set { content.Size = value; } - } - - private readonly Container content; - private readonly SpriteIcon icon; - private readonly Box hover; - - public IconButton() - { - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - content = new Container - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(BUTTON_SIZE), - CornerRadius = 5, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.04f), - Type = EdgeEffectType.Shadow, - Radius = 5, - }, - Children = new Drawable[] - { - hover = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - icon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(18), - } - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (hoverColour == null) - HoverColour = colours.Yellow.Opacity(0.6f); - - if (flashColour == null) - FlashColour = colours.Yellow; - - Enabled.ValueChanged += enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint); - } - - protected override bool OnHover(InputState state) - { - hover.FadeIn(500, Easing.OutQuint); - icon.FadeColour(IconHoverColour, 500, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - hover.FadeOut(500, Easing.OutQuint); - icon.FadeColour(IconColour, 500, Easing.OutQuint); - base.OnHoverLost(state); - } - - protected override bool OnClick(InputState state) - { - hover.FlashColour(FlashColour, 800, Easing.OutQuint); - return base.OnClick(state); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - content.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - content.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Graphics.UserInterface +{ + public class IconButton : OsuClickableContainer + { + public const float BUTTON_SIZE = 30; + + private Color4? flashColour; + /// + /// The colour that should be flashed when the is clicked. + /// + public Color4 FlashColour + { + get { return flashColour ?? Color4.White; } + set { flashColour = value; } + } + + private Color4? iconColour; + /// + /// The icon colour. This does not affect . + /// + public Color4 IconColour + { + get { return iconColour ?? Color4.White; } + set + { + iconColour = value; + icon.Colour = value; + } + } + + private Color4? iconHoverColour; + /// + /// The icon colour while the is hovered. + /// + public Color4 IconHoverColour + { + get { return iconHoverColour ?? IconColour; } + set { iconHoverColour = value; } + } + + private Color4? hoverColour; + /// + /// The background colour of the while it is hovered. + /// + public Color4 HoverColour + { + get { return hoverColour ?? Color4.White; } + set + { + hoverColour = value; + hover.Colour = value; + } + } + + /// + /// The icon. + /// + public FontAwesome Icon + { + get { return icon.Icon; } + set { icon.Icon = value; } + } + + /// + /// The icon scale. This does not affect . + /// + public Vector2 IconScale + { + get { return icon.Scale; } + set { icon.Scale = value; } + } + + /// + /// The size of the while it is not being pressed. + /// + public Vector2 ButtonSize + { + get { return content.Size; } + set { content.Size = value; } + } + + private readonly Container content; + private readonly SpriteIcon icon; + private readonly Box hover; + + public IconButton() + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + content = new Container + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(BUTTON_SIZE), + CornerRadius = 5, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.04f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Children = new Drawable[] + { + hover = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(18), + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (hoverColour == null) + HoverColour = colours.Yellow.Opacity(0.6f); + + if (flashColour == null) + FlashColour = colours.Yellow; + + Enabled.ValueChanged += enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint); + } + + protected override bool OnHover(InputState state) + { + hover.FadeIn(500, Easing.OutQuint); + icon.FadeColour(IconHoverColour, 500, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + hover.FadeOut(500, Easing.OutQuint); + icon.FadeColour(IconColour, 500, Easing.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + hover.FlashColour(FlashColour, 800, Easing.OutQuint); + return base.OnClick(state); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + content.ScaleTo(0.75f, 2000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + content.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs index 366c063a0c..3cb2446acc 100644 --- a/osu.Game/Graphics/UserInterface/LineGraph.cs +++ b/osu.Game/Graphics/UserInterface/LineGraph.cs @@ -1,118 +1,118 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Caching; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Lines; - -namespace osu.Game.Graphics.UserInterface -{ - public class LineGraph : Container - { - /// - /// Manually set the max value, otherwise will be used. - /// - public float? MaxValue { get; set; } - - /// - /// Manually set the min value, otherwise will be used. - /// - public float? MinValue { get; set; } - - public float ActualMaxValue { get; private set; } = float.NaN; - public float ActualMinValue { get; private set; } = float.NaN; - - private const double transform_duration = 1500; - - /// - /// Hold an empty area if values are less. - /// - public int DefaultValueCount; - - private readonly Container maskingContainer; - private readonly Path path; - - private float[] values; - - /// - /// A list of floats decides position of each line node. - /// - public IEnumerable Values - { - get { return values; } - set - { - values = value.ToArray(); - - float max = values.Max(), min = values.Min(); - if (MaxValue > max) max = MaxValue.Value; - if (MinValue < min) min = MinValue.Value; - - ActualMaxValue = max; - ActualMinValue = min; - - pathCached.Invalidate(); - - maskingContainer.Width = 0; - maskingContainer.ResizeWidthTo(1, transform_duration, Easing.OutQuint); - } - } - - public LineGraph() - { - Add(maskingContainer = new Container - { - Masking = true, - RelativeSizeAxes = Axes.Both, - Child = path = new Path { RelativeSizeAxes = Axes.Both, PathWidth = 1 } - }); - } - - public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) - { - if ((invalidation & Invalidation.DrawSize) > 0) - pathCached.Invalidate(); - - return base.Invalidate(invalidation, source, shallPropagate); - } - - private Cached pathCached = new Cached(); - - protected override void Update() - { - base.Update(); - if (!pathCached.IsValid) - { - applyPath(); - pathCached.Validate(); - } - } - - private void applyPath() - { - path.ClearVertices(); - if (values == null) return; - - int count = Math.Max(values.Length, DefaultValueCount); - - for (int i = 0; i < values.Length; i++) - { - float x = (i + count - values.Length) / (float)(count - 1) * DrawWidth - 1; - float y = GetYPosition(values[i]) * DrawHeight - 1; - // the -1 is for inner offset in path (actually -PathWidth) - path.AddVertex(new Vector2(x, y)); - } - } - - protected float GetYPosition(float value) - { - if (ActualMaxValue == ActualMinValue) return 0; - return (ActualMaxValue - value) / (ActualMaxValue - ActualMinValue); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Caching; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; + +namespace osu.Game.Graphics.UserInterface +{ + public class LineGraph : Container + { + /// + /// Manually set the max value, otherwise will be used. + /// + public float? MaxValue { get; set; } + + /// + /// Manually set the min value, otherwise will be used. + /// + public float? MinValue { get; set; } + + public float ActualMaxValue { get; private set; } = float.NaN; + public float ActualMinValue { get; private set; } = float.NaN; + + private const double transform_duration = 1500; + + /// + /// Hold an empty area if values are less. + /// + public int DefaultValueCount; + + private readonly Container maskingContainer; + private readonly Path path; + + private float[] values; + + /// + /// A list of floats decides position of each line node. + /// + public IEnumerable Values + { + get { return values; } + set + { + values = value.ToArray(); + + float max = values.Max(), min = values.Min(); + if (MaxValue > max) max = MaxValue.Value; + if (MinValue < min) min = MinValue.Value; + + ActualMaxValue = max; + ActualMinValue = min; + + pathCached.Invalidate(); + + maskingContainer.Width = 0; + maskingContainer.ResizeWidthTo(1, transform_duration, Easing.OutQuint); + } + } + + public LineGraph() + { + Add(maskingContainer = new Container + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Child = path = new Path { RelativeSizeAxes = Axes.Both, PathWidth = 1 } + }); + } + + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + if ((invalidation & Invalidation.DrawSize) > 0) + pathCached.Invalidate(); + + return base.Invalidate(invalidation, source, shallPropagate); + } + + private Cached pathCached = new Cached(); + + protected override void Update() + { + base.Update(); + if (!pathCached.IsValid) + { + applyPath(); + pathCached.Validate(); + } + } + + private void applyPath() + { + path.ClearVertices(); + if (values == null) return; + + int count = Math.Max(values.Length, DefaultValueCount); + + for (int i = 0; i < values.Length; i++) + { + float x = (i + count - values.Length) / (float)(count - 1) * DrawWidth - 1; + float y = GetYPosition(values[i]) * DrawHeight - 1; + // the -1 is for inner offset in path (actually -PathWidth) + path.AddVertex(new Vector2(x, y)); + } + } + + protected float GetYPosition(float value) + { + if (ActualMaxValue == ActualMinValue) return 0; + return (ActualMaxValue - value) / (ActualMaxValue - ActualMinValue); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/LoadingAnimation.cs b/osu.Game/Graphics/UserInterface/LoadingAnimation.cs index 54f818e307..5ea6bce432 100644 --- a/osu.Game/Graphics/UserInterface/LoadingAnimation.cs +++ b/osu.Game/Graphics/UserInterface/LoadingAnimation.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Graphics.UserInterface -{ - public class LoadingAnimation : VisibilityContainer - { - private readonly SpriteIcon spinner; - - public LoadingAnimation() - { - Size = new Vector2(20); - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - Children = new Drawable[] - { - spinner = new SpriteIcon - { - Size = new Vector2(20), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.fa_spinner - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - spinner.Spin(2000, RotationDirection.Clockwise); - } - - private const float transition_duration = 500; - - protected override void PopIn() => this.FadeIn(transition_duration * 5, Easing.OutQuint); - - protected override void PopOut() => this.FadeOut(transition_duration, Easing.OutQuint); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class LoadingAnimation : VisibilityContainer + { + private readonly SpriteIcon spinner; + + public LoadingAnimation() + { + Size = new Vector2(20); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Children = new Drawable[] + { + spinner = new SpriteIcon + { + Size = new Vector2(20), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_spinner + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + spinner.Spin(2000, RotationDirection.Clockwise); + } + + private const float transition_duration = 500; + + protected override void PopIn() => this.FadeIn(transition_duration * 5, Easing.OutQuint); + + protected override void PopOut() => this.FadeOut(transition_duration, Easing.OutQuint); + } +} diff --git a/osu.Game/Graphics/UserInterface/MenuItemType.cs b/osu.Game/Graphics/UserInterface/MenuItemType.cs index 21c743ecd3..42bade1439 100644 --- a/osu.Game/Graphics/UserInterface/MenuItemType.cs +++ b/osu.Game/Graphics/UserInterface/MenuItemType.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Graphics.UserInterface -{ - public enum MenuItemType - { - Standard, - Highlighted, - Destructive, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Graphics.UserInterface +{ + public enum MenuItemType + { + Standard, + Highlighted, + Destructive, + } +} diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index ed2be25feb..84b7fd9dae 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -1,142 +1,142 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; - -namespace osu.Game.Graphics.UserInterface -{ - public class Nub : CircularContainer, IHasCurrentValue, IHasAccentColour - { - public const float COLLAPSED_SIZE = 20; - public const float EXPANDED_SIZE = 40; - - private const float border_width = 3; - - public Nub() - { - Box fill; - - Size = new Vector2(COLLAPSED_SIZE, 12); - - BorderColour = Color4.White; - BorderThickness = border_width; - - Masking = true; - - Children = new[] - { - fill = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - }; - - Current.ValueChanged += newValue => - { - if (newValue) - fill.FadeIn(200, Easing.OutQuint); - else - fill.FadeTo(0.01f, 200, Easing.OutQuint); //todo: remove once we figure why containers aren't drawing at all times - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Pink; - GlowingAccentColour = colours.PinkLighter; - GlowColour = colours.PinkDarker; - - EdgeEffect = new EdgeEffectParameters - { - Colour = GlowColour, - Type = EdgeEffectType.Glow, - Radius = 10, - Roundness = 8, - }; - } - - protected override void LoadComplete() - { - FadeEdgeEffectTo(0); - } - - private bool glowing; - public bool Glowing - { - get { return glowing; } - set - { - glowing = value; - - if (value) - { - this.FadeColour(GlowingAccentColour, 500, Easing.OutQuint); - FadeEdgeEffectTo(1, 500, Easing.OutQuint); - } - else - { - FadeEdgeEffectTo(0, 500); - this.FadeColour(AccentColour, 500); - } - } - } - - public bool Expanded - { - set - { - this.ResizeTo(new Vector2(value ? EXPANDED_SIZE : COLLAPSED_SIZE, 12), 500, Easing.OutQuint); - } - } - - public Bindable Current { get; } = new Bindable(); - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - if (!Glowing) - Colour = value; - } - } - - private Color4 glowingAccentColour; - public Color4 GlowingAccentColour - { - get { return glowingAccentColour; } - set - { - glowingAccentColour = value; - if (Glowing) - Colour = value; - } - } - - private Color4 glowColour; - public Color4 GlowColour - { - get { return glowColour; } - set - { - glowColour = value; - - var effect = EdgeEffect; - effect.Colour = value; - EdgeEffect = effect; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; + +namespace osu.Game.Graphics.UserInterface +{ + public class Nub : CircularContainer, IHasCurrentValue, IHasAccentColour + { + public const float COLLAPSED_SIZE = 20; + public const float EXPANDED_SIZE = 40; + + private const float border_width = 3; + + public Nub() + { + Box fill; + + Size = new Vector2(COLLAPSED_SIZE, 12); + + BorderColour = Color4.White; + BorderThickness = border_width; + + Masking = true; + + Children = new[] + { + fill = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + }; + + Current.ValueChanged += newValue => + { + if (newValue) + fill.FadeIn(200, Easing.OutQuint); + else + fill.FadeTo(0.01f, 200, Easing.OutQuint); //todo: remove once we figure why containers aren't drawing at all times + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Pink; + GlowingAccentColour = colours.PinkLighter; + GlowColour = colours.PinkDarker; + + EdgeEffect = new EdgeEffectParameters + { + Colour = GlowColour, + Type = EdgeEffectType.Glow, + Radius = 10, + Roundness = 8, + }; + } + + protected override void LoadComplete() + { + FadeEdgeEffectTo(0); + } + + private bool glowing; + public bool Glowing + { + get { return glowing; } + set + { + glowing = value; + + if (value) + { + this.FadeColour(GlowingAccentColour, 500, Easing.OutQuint); + FadeEdgeEffectTo(1, 500, Easing.OutQuint); + } + else + { + FadeEdgeEffectTo(0, 500); + this.FadeColour(AccentColour, 500); + } + } + } + + public bool Expanded + { + set + { + this.ResizeTo(new Vector2(value ? EXPANDED_SIZE : COLLAPSED_SIZE, 12), 500, Easing.OutQuint); + } + } + + public Bindable Current { get; } = new Bindable(); + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + if (!Glowing) + Colour = value; + } + } + + private Color4 glowingAccentColour; + public Color4 GlowingAccentColour + { + get { return glowingAccentColour; } + set + { + glowingAccentColour = value; + if (Glowing) + Colour = value; + } + } + + private Color4 glowColour; + public Color4 GlowColour + { + get { return glowColour; } + set + { + glowColour = value; + + var effect = EdgeEffect; + effect.Colour = value; + EdgeEffect = effect; + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index be487bc062..bf3805a44d 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.UserInterface; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// A button with added default sound effects. - /// - public class OsuButton : Button - { - public OsuButton() - { - Add(new HoverClickSounds(HoverSampleSet.Loud)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.UserInterface; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A button with added default sound effects. + /// + public class OsuButton : Button + { + public OsuButton() + { + Add(new HoverClickSounds(HoverSampleSet.Loud)); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index f06313c261..ea337d5f42 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -1,119 +1,119 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics.Sprites; -using OpenTK.Graphics; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuCheckbox : Checkbox - { - private Bindable bindable; - - public Bindable Bindable - { - set - { - bindable = value; - Current.BindTo(bindable); - } - } - - public Color4 CheckedColor { get; set; } = Color4.Cyan; - public Color4 UncheckedColor { get; set; } = Color4.White; - public int FadeDuration { get; set; } - - public string LabelText - { - get { return labelSpriteText?.Text; } - set - { - if (labelSpriteText != null) - labelSpriteText.Text = value; - } - } - - public MarginPadding LabelPadding - { - get { return labelSpriteText?.Padding ?? new MarginPadding(); } - set - { - if (labelSpriteText != null) - labelSpriteText.Padding = value; - } - } - - protected readonly Nub Nub; - - private readonly SpriteText labelSpriteText; - private SampleChannel sampleChecked; - private SampleChannel sampleUnchecked; - - public OsuCheckbox() - { - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - - Children = new Drawable[] - { - labelSpriteText = new OsuSpriteText(), - Nub = new Nub - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding { Right = 5 }, - }, - new HoverClickSounds() - }; - - Nub.Current.BindTo(Current); - - Current.DisabledChanged += disabled => - { - Alpha = disabled ? 0.3f : 1; - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Current.ValueChanged += newValue => - { - if (newValue) - sampleChecked?.Play(); - else - sampleUnchecked?.Play(); - }; - } - - protected override bool OnHover(InputState state) - { - Nub.Glowing = true; - Nub.Expanded = true; - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - Nub.Glowing = false; - Nub.Expanded = false; - base.OnHoverLost(state); - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleChecked = audio.Sample.Get(@"UI/check-on"); - sampleUnchecked = audio.Sample.Get(@"UI/check-off"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuCheckbox : Checkbox + { + private Bindable bindable; + + public Bindable Bindable + { + set + { + bindable = value; + Current.BindTo(bindable); + } + } + + public Color4 CheckedColor { get; set; } = Color4.Cyan; + public Color4 UncheckedColor { get; set; } = Color4.White; + public int FadeDuration { get; set; } + + public string LabelText + { + get { return labelSpriteText?.Text; } + set + { + if (labelSpriteText != null) + labelSpriteText.Text = value; + } + } + + public MarginPadding LabelPadding + { + get { return labelSpriteText?.Padding ?? new MarginPadding(); } + set + { + if (labelSpriteText != null) + labelSpriteText.Padding = value; + } + } + + protected readonly Nub Nub; + + private readonly SpriteText labelSpriteText; + private SampleChannel sampleChecked; + private SampleChannel sampleUnchecked; + + public OsuCheckbox() + { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + labelSpriteText = new OsuSpriteText(), + Nub = new Nub + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding { Right = 5 }, + }, + new HoverClickSounds() + }; + + Nub.Current.BindTo(Current); + + Current.DisabledChanged += disabled => + { + Alpha = disabled ? 0.3f : 1; + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.ValueChanged += newValue => + { + if (newValue) + sampleChecked?.Play(); + else + sampleUnchecked?.Play(); + }; + } + + protected override bool OnHover(InputState state) + { + Nub.Glowing = true; + Nub.Expanded = true; + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + Nub.Glowing = false; + Nub.Expanded = false; + base.OnHoverLost(state); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleChecked = audio.Sample.Get(@"UI/check-on"); + sampleUnchecked = audio.Sample.Get(@"UI/check-off"); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs index 5335f20bf9..9b697f291f 100644 --- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs @@ -1,39 +1,39 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuContextMenu : OsuMenu - { - private const int fade_duration = 250; - - public OsuContextMenu() - : base(Direction.Vertical) - { - MaskingContainer.CornerRadius = 5; - MaskingContainer.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.1f), - Radius = 4, - }; - - ItemsContainer.Padding = new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.ContextMenuGray; - } - - protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint); - protected override void AnimateClose() => this.FadeOut(fade_duration, Easing.OutQuint); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuContextMenu : OsuMenu + { + private const int fade_duration = 250; + + public OsuContextMenu() + : base(Direction.Vertical) + { + MaskingContainer.CornerRadius = 5; + MaskingContainer.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.1f), + Radius = 4, + }; + + ItemsContainer.Padding = new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.ContextMenuGray; + } + + protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint); + protected override void AnimateClose() => this.FadeOut(fade_duration, Easing.OutQuint); + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index b230aa4260..830bde9dac 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -1,255 +1,255 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.Sprites; -using OpenTK; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuDropdown : Dropdown, IHasAccentColour - { - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - updateAccentColour(); - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (accentColour == default(Color4)) - accentColour = colours.PinkDarker; - updateAccentColour(); - } - - private void updateAccentColour() - { - var header = Header as IHasAccentColour; - if (header != null) header.AccentColour = accentColour; - - var menu = Menu as IHasAccentColour; - if (menu != null) menu.AccentColour = accentColour; - } - - protected override DropdownHeader CreateHeader() => new OsuDropdownHeader(); - - protected override DropdownMenu CreateMenu() => new OsuDropdownMenu(); - - #region OsuDropdownMenu - protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour - { - // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring - public OsuDropdownMenu() - { - CornerRadius = 4; - BackgroundColour = Color4.Black.Opacity(0.5f); - - // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring - ItemsContainer.Padding = new MarginPadding(5); - } - - // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring - protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint); - protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint); - - // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring - protected override void UpdateSize(Vector2 newSize) - { - if (Direction == Direction.Vertical) - { - Width = newSize.X; - this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint); - } - else - { - Height = newSize.Y; - this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint); - } - } - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - foreach (var c in Children.OfType()) - c.AccentColour = value; - } - } - - protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour }; - - #region DrawableOsuDropdownMenuItem - public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour - { - private Color4? accentColour; - public Color4 AccentColour - { - get { return accentColour ?? nonAccentSelectedColour; } - set - { - accentColour = value; - updateColours(); - } - } - - private void updateColours() - { - BackgroundColourHover = accentColour ?? nonAccentHoverColour; - BackgroundColourSelected = accentColour ?? nonAccentSelectedColour; - UpdateBackgroundColour(); - UpdateForegroundColour(); - } - - private Color4 nonAccentHoverColour; - private Color4 nonAccentSelectedColour; - - public DrawableOsuDropdownMenuItem(MenuItem item) - : base(item) - { - Foreground.Padding = new MarginPadding(2); - - Masking = true; - CornerRadius = 6; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = Color4.Transparent; - - nonAccentHoverColour = colours.PinkDarker; - nonAccentSelectedColour = Color4.Black.Opacity(0.5f); - updateColours(); - - AddInternal(new HoverClickSounds(HoverSampleSet.Soft)); - } - - protected override void UpdateForegroundColour() - { - base.UpdateForegroundColour(); - - var content = Foreground.Children.FirstOrDefault() as Content; - if (content != null) content.Chevron.Alpha = IsHovered ? 1 : 0; - } - - protected override Drawable CreateContent() => new Content(); - - protected new class Content : FillFlowContainer, IHasText - { - public string Text - { - get { return Label.Text; } - set { Label.Text = value; } - } - - public readonly OsuSpriteText Label; - public readonly SpriteIcon Chevron; - - public Content() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Horizontal; - - Children = new Drawable[] - { - Chevron = new SpriteIcon - { - AlwaysPresent = true, - Icon = FontAwesome.fa_chevron_right, - Colour = Color4.Black, - Alpha = 0.5f, - Size = new Vector2(8), - Margin = new MarginPadding { Left = 3, Right = 3 }, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - Label = new OsuSpriteText - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - }; - } - } - } - #endregion - } - #endregion - - public class OsuDropdownHeader : DropdownHeader, IHasAccentColour - { - protected readonly SpriteText Text; - protected override string Label - { - get { return Text.Text; } - set { Text.Text = value; } - } - - protected readonly SpriteIcon Icon; - - private Color4 accentColour; - public virtual Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - BackgroundColourHover = accentColour; - } - } - - public OsuDropdownHeader() - { - Foreground.Padding = new MarginPadding(4); - - AutoSizeAxes = Axes.None; - Margin = new MarginPadding { Bottom = 4 }; - CornerRadius = 4; - Height = 40; - - Foreground.Children = new Drawable[] - { - Text = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - Icon = new SpriteIcon - { - Icon = FontAwesome.fa_chevron_down, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding { Right = 4 }, - Size = new Vector2(20), - }, - }; - - AddInternal(new HoverClickSounds()); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = Color4.Black.Opacity(0.5f); - BackgroundColourHover = colours.PinkDarker; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; +using OpenTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuDropdown : Dropdown, IHasAccentColour + { + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + updateAccentColour(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (accentColour == default(Color4)) + accentColour = colours.PinkDarker; + updateAccentColour(); + } + + private void updateAccentColour() + { + var header = Header as IHasAccentColour; + if (header != null) header.AccentColour = accentColour; + + var menu = Menu as IHasAccentColour; + if (menu != null) menu.AccentColour = accentColour; + } + + protected override DropdownHeader CreateHeader() => new OsuDropdownHeader(); + + protected override DropdownMenu CreateMenu() => new OsuDropdownMenu(); + + #region OsuDropdownMenu + protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour + { + // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring + public OsuDropdownMenu() + { + CornerRadius = 4; + BackgroundColour = Color4.Black.Opacity(0.5f); + + // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring + ItemsContainer.Padding = new MarginPadding(5); + } + + // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring + protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint); + protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint); + + // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring + protected override void UpdateSize(Vector2 newSize) + { + if (Direction == Direction.Vertical) + { + Width = newSize.X; + this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint); + } + else + { + Height = newSize.Y; + this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint); + } + } + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + foreach (var c in Children.OfType()) + c.AccentColour = value; + } + } + + protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour }; + + #region DrawableOsuDropdownMenuItem + public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour + { + private Color4? accentColour; + public Color4 AccentColour + { + get { return accentColour ?? nonAccentSelectedColour; } + set + { + accentColour = value; + updateColours(); + } + } + + private void updateColours() + { + BackgroundColourHover = accentColour ?? nonAccentHoverColour; + BackgroundColourSelected = accentColour ?? nonAccentSelectedColour; + UpdateBackgroundColour(); + UpdateForegroundColour(); + } + + private Color4 nonAccentHoverColour; + private Color4 nonAccentSelectedColour; + + public DrawableOsuDropdownMenuItem(MenuItem item) + : base(item) + { + Foreground.Padding = new MarginPadding(2); + + Masking = true; + CornerRadius = 6; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = Color4.Transparent; + + nonAccentHoverColour = colours.PinkDarker; + nonAccentSelectedColour = Color4.Black.Opacity(0.5f); + updateColours(); + + AddInternal(new HoverClickSounds(HoverSampleSet.Soft)); + } + + protected override void UpdateForegroundColour() + { + base.UpdateForegroundColour(); + + var content = Foreground.Children.FirstOrDefault() as Content; + if (content != null) content.Chevron.Alpha = IsHovered ? 1 : 0; + } + + protected override Drawable CreateContent() => new Content(); + + protected new class Content : FillFlowContainer, IHasText + { + public string Text + { + get { return Label.Text; } + set { Label.Text = value; } + } + + public readonly OsuSpriteText Label; + public readonly SpriteIcon Chevron; + + public Content() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Horizontal; + + Children = new Drawable[] + { + Chevron = new SpriteIcon + { + AlwaysPresent = true, + Icon = FontAwesome.fa_chevron_right, + Colour = Color4.Black, + Alpha = 0.5f, + Size = new Vector2(8), + Margin = new MarginPadding { Left = 3, Right = 3 }, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + }, + Label = new OsuSpriteText + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + }, + }; + } + } + } + #endregion + } + #endregion + + public class OsuDropdownHeader : DropdownHeader, IHasAccentColour + { + protected readonly SpriteText Text; + protected override string Label + { + get { return Text.Text; } + set { Text.Text = value; } + } + + protected readonly SpriteIcon Icon; + + private Color4 accentColour; + public virtual Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + BackgroundColourHover = accentColour; + } + } + + public OsuDropdownHeader() + { + Foreground.Padding = new MarginPadding(4); + + AutoSizeAxes = Axes.None; + Margin = new MarginPadding { Bottom = 4 }; + CornerRadius = 4; + Height = 40; + + Foreground.Children = new Drawable[] + { + Text = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + Icon = new SpriteIcon + { + Icon = FontAwesome.fa_chevron_down, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding { Right = 4 }, + Size = new Vector2(20), + }, + }; + + AddInternal(new HoverClickSounds()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = Color4.Black.Opacity(0.5f); + BackgroundColourHover = colours.PinkDarker; + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs index b0711a6667..502f468ec9 100644 --- a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs @@ -1,32 +1,32 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.ComponentModel; -using System.Reflection; -using System.Collections.Generic; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuEnumDropdown : OsuDropdown - { - public OsuEnumDropdown() - { - if (!typeof(T).IsEnum) - throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument"); - - List> items = new List>(); - foreach (var val in (T[])Enum.GetValues(typeof(T))) - { - var field = typeof(T).GetField(Enum.GetName(typeof(T), val)); - items.Add( - new KeyValuePair( - field.GetCustomAttribute()?.Description ?? Enum.GetName(typeof(T), val), - val - ) - ); - } - Items = items; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.ComponentModel; +using System.Reflection; +using System.Collections.Generic; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuEnumDropdown : OsuDropdown + { + public OsuEnumDropdown() + { + if (!typeof(T).IsEnum) + throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument"); + + List> items = new List>(); + foreach (var val in (T[])Enum.GetValues(typeof(T))) + { + var field = typeof(T).GetField(Enum.GetName(typeof(T), val)); + items.Add( + new KeyValuePair( + field.GetCustomAttribute()?.Description ?? Enum.GetName(typeof(T), val), + val + ) + ); + } + Items = items; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index 968c0eed98..41aeb534f0 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -1,170 +1,170 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics.Sprites; -using OpenTK; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuMenu : Menu - { - public OsuMenu(Direction direction, bool topLevelMenu = false) - : base(direction, topLevelMenu) - { - BackgroundColour = Color4.Black.Opacity(0.5f); - - MaskingContainer.CornerRadius = 4; - ItemsContainer.Padding = new MarginPadding(5); - } - - protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint); - protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint); - - protected override void UpdateSize(Vector2 newSize) - { - if (Direction == Direction.Vertical) - { - Width = newSize.X; - this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint); - } - else - { - Height = newSize.Y; - this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint); - } - } - - protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuMenuItem(item); - - protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical) - { - Anchor = Direction == Direction.Horizontal ? Anchor.BottomLeft : Anchor.TopRight - }; - - protected class DrawableOsuMenuItem : DrawableMenuItem - { - private const int margin_horizontal = 17; - private const int text_size = 17; - private const int transition_length = 80; - public const int MARGIN_VERTICAL = 4; - - private SampleChannel sampleClick; - private SampleChannel sampleHover; - - private TextContainer text; - - public DrawableOsuMenuItem(MenuItem item) - : base(item) - { - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleHover = audio.Sample.Get(@"UI/generic-hover"); - sampleClick = audio.Sample.Get(@"UI/generic-select"); - - BackgroundColour = Color4.Transparent; - BackgroundColourHover = OsuColour.FromHex(@"172023"); - - updateTextColour(); - } - - private void updateTextColour() - { - switch ((Item as OsuMenuItem)?.Type) - { - default: - case MenuItemType.Standard: - text.Colour = Color4.White; - break; - case MenuItemType.Destructive: - text.Colour = Color4.Red; - break; - case MenuItemType.Highlighted: - text.Colour = OsuColour.FromHex(@"ffcc22"); - break; - } - } - - protected override bool OnHover(InputState state) - { - sampleHover.Play(); - text.BoldText.FadeIn(transition_length, Easing.OutQuint); - text.NormalText.FadeOut(transition_length, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - text.BoldText.FadeOut(transition_length, Easing.OutQuint); - text.NormalText.FadeIn(transition_length, Easing.OutQuint); - base.OnHoverLost(state); - } - - protected override bool OnClick(InputState state) - { - sampleClick.Play(); - return base.OnClick(state); - } - - protected sealed override Drawable CreateContent() => text = CreateTextContainer(); - protected virtual TextContainer CreateTextContainer() => new TextContainer(); - - protected class TextContainer : Container, IHasText - { - public string Text - { - get { return NormalText.Text; } - set - { - NormalText.Text = value; - BoldText.Text = value; - } - } - - public readonly SpriteText NormalText; - public readonly SpriteText BoldText; - - public TextContainer() - { - Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; - - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - NormalText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - TextSize = text_size, - Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, - }, - BoldText = new OsuSpriteText - { - AlwaysPresent = true, - Alpha = 0, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - TextSize = text_size, - Font = @"Exo2.0-Bold", - Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, - } - }; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; +using OpenTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuMenu : Menu + { + public OsuMenu(Direction direction, bool topLevelMenu = false) + : base(direction, topLevelMenu) + { + BackgroundColour = Color4.Black.Opacity(0.5f); + + MaskingContainer.CornerRadius = 4; + ItemsContainer.Padding = new MarginPadding(5); + } + + protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint); + protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint); + + protected override void UpdateSize(Vector2 newSize) + { + if (Direction == Direction.Vertical) + { + Width = newSize.X; + this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint); + } + else + { + Height = newSize.Y; + this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint); + } + } + + protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuMenuItem(item); + + protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical) + { + Anchor = Direction == Direction.Horizontal ? Anchor.BottomLeft : Anchor.TopRight + }; + + protected class DrawableOsuMenuItem : DrawableMenuItem + { + private const int margin_horizontal = 17; + private const int text_size = 17; + private const int transition_length = 80; + public const int MARGIN_VERTICAL = 4; + + private SampleChannel sampleClick; + private SampleChannel sampleHover; + + private TextContainer text; + + public DrawableOsuMenuItem(MenuItem item) + : base(item) + { + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get(@"UI/generic-hover"); + sampleClick = audio.Sample.Get(@"UI/generic-select"); + + BackgroundColour = Color4.Transparent; + BackgroundColourHover = OsuColour.FromHex(@"172023"); + + updateTextColour(); + } + + private void updateTextColour() + { + switch ((Item as OsuMenuItem)?.Type) + { + default: + case MenuItemType.Standard: + text.Colour = Color4.White; + break; + case MenuItemType.Destructive: + text.Colour = Color4.Red; + break; + case MenuItemType.Highlighted: + text.Colour = OsuColour.FromHex(@"ffcc22"); + break; + } + } + + protected override bool OnHover(InputState state) + { + sampleHover.Play(); + text.BoldText.FadeIn(transition_length, Easing.OutQuint); + text.NormalText.FadeOut(transition_length, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + text.BoldText.FadeOut(transition_length, Easing.OutQuint); + text.NormalText.FadeIn(transition_length, Easing.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + sampleClick.Play(); + return base.OnClick(state); + } + + protected sealed override Drawable CreateContent() => text = CreateTextContainer(); + protected virtual TextContainer CreateTextContainer() => new TextContainer(); + + protected class TextContainer : Container, IHasText + { + public string Text + { + get { return NormalText.Text; } + set + { + NormalText.Text = value; + BoldText.Text = value; + } + } + + public readonly SpriteText NormalText; + public readonly SpriteText BoldText; + + public TextContainer() + { + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + NormalText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = text_size, + Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, + }, + BoldText = new OsuSpriteText + { + AlwaysPresent = true, + Alpha = 0, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = text_size, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, + } + }; + } + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs index 9ccbea2479..02452523a8 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics.UserInterface; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuMenuItem : MenuItem - { - public readonly MenuItemType Type; - - public OsuMenuItem(string text, MenuItemType type = MenuItemType.Standard) - : base(text) - { - Type = type; - } - - public OsuMenuItem(string text, MenuItemType type, Action action) - : base(text, action) - { - Type = type; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics.UserInterface; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuMenuItem : MenuItem + { + public readonly MenuItemType Type; + + public OsuMenuItem(string text, MenuItemType type = MenuItemType.Standard) + : base(text) + { + Type = type; + } + + public OsuMenuItem(string text, MenuItemType type, Action action) + : base(text, action) + { + Type = type; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index 8c519e5371..d34d2b2a7c 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -1,119 +1,119 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Framework.Platform; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuPasswordTextBox : OsuTextBox - { - protected override Drawable GetDrawableCharacter(char c) => new PasswordMaskChar(CalculatedTextSize); - - public override bool AllowClipboardExport => false; - - private readonly CapsWarning warning; - - private GameHost host; - - public OsuPasswordTextBox() - { - Add(warning = new CapsWarning - { - Size = new Vector2(20), - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Margin = new MarginPadding { Right = 10 }, - Alpha = 0, - }); - } - - [BackgroundDependencyLoader] - private void load(GameHost host) - { - this.host = host; - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (args.Key == Key.CapsLock) - updateCapsWarning(host.CapsLockEnabled); - return base.OnKeyDown(state, args); - } - - protected override void OnFocus(InputState state) - { - updateCapsWarning(host.CapsLockEnabled); - base.OnFocus(state); - } - - protected override void OnFocusLost(InputState state) - { - updateCapsWarning(false); - base.OnFocusLost(state); - } - - private void updateCapsWarning(bool visible) => warning.FadeTo(visible ? 1 : 0, 250, Easing.OutQuint); - - public class PasswordMaskChar : Container - { - private readonly CircularContainer circle; - - public PasswordMaskChar(float size) - { - Size = new Vector2(size / 2, size); - Children = new[] - { - circle = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Masking = true, - Alpha = 0, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.8f, 0), - Children = new[] - { - new Box - { - Colour = Color4.White, - RelativeSizeAxes = Axes.Both, - } - }, - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - circle.FadeIn(500, Easing.OutQuint); - circle.ResizeTo(new Vector2(0.8f), 500, Easing.OutQuint); - } - } - - private class CapsWarning : SpriteIcon, IHasTooltip - { - public string TooltipText => @"Caps lock is active"; - - public CapsWarning() - { - Icon = FontAwesome.fa_warning; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - Colour = colour.YellowLight; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.Platform; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuPasswordTextBox : OsuTextBox + { + protected override Drawable GetDrawableCharacter(char c) => new PasswordMaskChar(CalculatedTextSize); + + public override bool AllowClipboardExport => false; + + private readonly CapsWarning warning; + + private GameHost host; + + public OsuPasswordTextBox() + { + Add(warning = new CapsWarning + { + Size = new Vector2(20), + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Margin = new MarginPadding { Right = 10 }, + Alpha = 0, + }); + } + + [BackgroundDependencyLoader] + private void load(GameHost host) + { + this.host = host; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Key == Key.CapsLock) + updateCapsWarning(host.CapsLockEnabled); + return base.OnKeyDown(state, args); + } + + protected override void OnFocus(InputState state) + { + updateCapsWarning(host.CapsLockEnabled); + base.OnFocus(state); + } + + protected override void OnFocusLost(InputState state) + { + updateCapsWarning(false); + base.OnFocusLost(state); + } + + private void updateCapsWarning(bool visible) => warning.FadeTo(visible ? 1 : 0, 250, Easing.OutQuint); + + public class PasswordMaskChar : Container + { + private readonly CircularContainer circle; + + public PasswordMaskChar(float size) + { + Size = new Vector2(size / 2, size); + Children = new[] + { + circle = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.8f, 0), + Children = new[] + { + new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + } + }, + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + circle.FadeIn(500, Easing.OutQuint); + circle.ResizeTo(new Vector2(0.8f), 500, Easing.OutQuint); + } + } + + private class CapsWarning : SpriteIcon, IHasTooltip + { + public string TooltipText => @"Caps lock is active"; + + public CapsWarning() + { + Icon = FontAwesome.fa_warning; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + Colour = colour.YellowLight; + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 8f375d9885..7604009aab 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -1,218 +1,218 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Globalization; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuSliderBar : SliderBar, IHasTooltip, IHasAccentColour - where T : struct, IEquatable, IComparable, IConvertible - { - /// - /// Maximum number of decimal digits to be displayed in the tooltip. - /// - private const int max_decimal_digits = 5; - - private SampleChannel sample; - private double lastSampleTime; - private T lastSampleValue; - - protected readonly Nub Nub; - private readonly Box leftBox; - private readonly Box rightBox; - - public virtual string TooltipText - { - get - { - var bindableDouble = CurrentNumber as BindableNumber; - var bindableFloat = CurrentNumber as BindableNumber; - var floatValue = bindableDouble?.Value ?? bindableFloat?.Value; - var floatPrecision = bindableDouble?.Precision ?? bindableFloat?.Precision; - - if (floatValue != null) - { - var floatMinValue = bindableDouble?.MinValue ?? bindableFloat.MinValue; - var floatMaxValue = bindableDouble?.MaxValue ?? bindableFloat.MaxValue; - - if (floatMaxValue == 1 && (floatMinValue == 0 || floatMinValue == -1)) - return floatValue.Value.ToString("P0"); - - var decimalPrecision = normalise((decimal)floatPrecision, max_decimal_digits); - - // Find the number of significant digits (we could have less than 5 after normalize()) - var significantDigits = findPrecision(decimalPrecision); - - return floatValue.Value.ToString($"N{significantDigits}"); - } - - var bindableInt = CurrentNumber as BindableNumber; - if (bindableInt != null) - return bindableInt.Value.ToString("N0"); - - return Current.Value.ToString(CultureInfo.InvariantCulture); - } - } - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - leftBox.Colour = value; - rightBox.Colour = value; - } - } - - public OsuSliderBar() - { - Height = 12; - RangePadding = 20; - Children = new Drawable[] - { - leftBox = new Box - { - Height = 2, - EdgeSmoothness = new Vector2(0, 0.5f), - Position = new Vector2(2, 0), - RelativeSizeAxes = Axes.None, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - rightBox = new Box - { - Height = 2, - EdgeSmoothness = new Vector2(0, 0.5f), - Position = new Vector2(-2, 0), - RelativeSizeAxes = Axes.None, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Alpha = 0.5f, - }, - Nub = new Nub - { - Origin = Anchor.TopCentre, - Expanded = true, - }, - new HoverClickSounds() - }; - - Current.DisabledChanged += disabled => - { - Alpha = disabled ? 0.3f : 1; - }; - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuColour colours) - { - sample = audio.Sample.Get(@"UI/sliderbar-notch"); - AccentColour = colours.Pink; - } - - protected override bool OnHover(InputState state) - { - Nub.Glowing = true; - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - Nub.Glowing = false; - base.OnHoverLost(state); - } - - protected override void OnUserChange() - { - base.OnUserChange(); - playSample(); - } - - private void playSample() - { - if (Clock == null || Clock.CurrentTime - lastSampleTime <= 50) - return; - - if (Current.Value.Equals(lastSampleValue)) - return; - - lastSampleValue = Current.Value; - - lastSampleTime = Clock.CurrentTime; - sample.Frequency.Value = 1 + NormalizedValue * 0.2f; - - if (NormalizedValue == 0) - sample.Frequency.Value -= 0.4f; - else if (NormalizedValue == 1) - sample.Frequency.Value += 0.4f; - - sample.Play(); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - Nub.Current.Value = true; - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - Nub.Current.Value = false; - return base.OnMouseUp(state, args); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - leftBox.Scale = new Vector2(MathHelper.Clamp( - Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); - rightBox.Scale = new Vector2(MathHelper.Clamp( - DrawWidth - Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); - } - - protected override void UpdateValue(float value) - { - Nub.MoveToX(RangePadding + UsableWidth * value, 250, Easing.OutQuint); - } - - /// - /// Removes all non-significant digits, keeping at most a requested number of decimal digits. - /// - /// The decimal to normalize. - /// The maximum number of decimal digits to keep. The final result may have fewer decimal digits than this value. - /// The normalised decimal. - private decimal normalise(decimal d, int sd) - => decimal.Parse(Math.Round(d, sd).ToString(string.Concat("0.", new string('#', sd)), CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - - /// - /// Finds the number of digits after the decimal. - /// - /// The value to find the number of decimal digits for. - /// The number decimal digits. - private int findPrecision(decimal d) - { - int precision = 0; - while (d != Math.Round(d)) - { - d *= 10; - precision++; - } - - return precision; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Globalization; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuSliderBar : SliderBar, IHasTooltip, IHasAccentColour + where T : struct, IEquatable, IComparable, IConvertible + { + /// + /// Maximum number of decimal digits to be displayed in the tooltip. + /// + private const int max_decimal_digits = 5; + + private SampleChannel sample; + private double lastSampleTime; + private T lastSampleValue; + + protected readonly Nub Nub; + private readonly Box leftBox; + private readonly Box rightBox; + + public virtual string TooltipText + { + get + { + var bindableDouble = CurrentNumber as BindableNumber; + var bindableFloat = CurrentNumber as BindableNumber; + var floatValue = bindableDouble?.Value ?? bindableFloat?.Value; + var floatPrecision = bindableDouble?.Precision ?? bindableFloat?.Precision; + + if (floatValue != null) + { + var floatMinValue = bindableDouble?.MinValue ?? bindableFloat.MinValue; + var floatMaxValue = bindableDouble?.MaxValue ?? bindableFloat.MaxValue; + + if (floatMaxValue == 1 && (floatMinValue == 0 || floatMinValue == -1)) + return floatValue.Value.ToString("P0"); + + var decimalPrecision = normalise((decimal)floatPrecision, max_decimal_digits); + + // Find the number of significant digits (we could have less than 5 after normalize()) + var significantDigits = findPrecision(decimalPrecision); + + return floatValue.Value.ToString($"N{significantDigits}"); + } + + var bindableInt = CurrentNumber as BindableNumber; + if (bindableInt != null) + return bindableInt.Value.ToString("N0"); + + return Current.Value.ToString(CultureInfo.InvariantCulture); + } + } + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + leftBox.Colour = value; + rightBox.Colour = value; + } + } + + public OsuSliderBar() + { + Height = 12; + RangePadding = 20; + Children = new Drawable[] + { + leftBox = new Box + { + Height = 2, + EdgeSmoothness = new Vector2(0, 0.5f), + Position = new Vector2(2, 0), + RelativeSizeAxes = Axes.None, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + rightBox = new Box + { + Height = 2, + EdgeSmoothness = new Vector2(0, 0.5f), + Position = new Vector2(-2, 0), + RelativeSizeAxes = Axes.None, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Alpha = 0.5f, + }, + Nub = new Nub + { + Origin = Anchor.TopCentre, + Expanded = true, + }, + new HoverClickSounds() + }; + + Current.DisabledChanged += disabled => + { + Alpha = disabled ? 0.3f : 1; + }; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio, OsuColour colours) + { + sample = audio.Sample.Get(@"UI/sliderbar-notch"); + AccentColour = colours.Pink; + } + + protected override bool OnHover(InputState state) + { + Nub.Glowing = true; + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + Nub.Glowing = false; + base.OnHoverLost(state); + } + + protected override void OnUserChange() + { + base.OnUserChange(); + playSample(); + } + + private void playSample() + { + if (Clock == null || Clock.CurrentTime - lastSampleTime <= 50) + return; + + if (Current.Value.Equals(lastSampleValue)) + return; + + lastSampleValue = Current.Value; + + lastSampleTime = Clock.CurrentTime; + sample.Frequency.Value = 1 + NormalizedValue * 0.2f; + + if (NormalizedValue == 0) + sample.Frequency.Value -= 0.4f; + else if (NormalizedValue == 1) + sample.Frequency.Value += 0.4f; + + sample.Play(); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + Nub.Current.Value = true; + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + Nub.Current.Value = false; + return base.OnMouseUp(state, args); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + leftBox.Scale = new Vector2(MathHelper.Clamp( + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); + rightBox.Scale = new Vector2(MathHelper.Clamp( + DrawWidth - Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); + } + + protected override void UpdateValue(float value) + { + Nub.MoveToX(RangePadding + UsableWidth * value, 250, Easing.OutQuint); + } + + /// + /// Removes all non-significant digits, keeping at most a requested number of decimal digits. + /// + /// The decimal to normalize. + /// The maximum number of decimal digits to keep. The final result may have fewer decimal digits than this value. + /// The normalised decimal. + private decimal normalise(decimal d, int sd) + => decimal.Parse(Math.Round(d, sd).ToString(string.Concat("0.", new string('#', sd)), CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + /// + /// Finds the number of digits after the decimal. + /// + /// The value to find the number of decimal digits for. + /// The number decimal digits. + private int findPrecision(decimal d) + { + int precision = 0; + while (d != Math.Round(d)) + { + d *= 10; + precision++; + } + + return precision; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 8b692f32bc..fc14a9c6ba 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -1,286 +1,286 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.Extensions.Color4Extensions; -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.Framework.Input; -using osu.Framework.MathUtils; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuTabControl : TabControl - { - private readonly Box strip; - - protected override Dropdown CreateDropdown() => new OsuTabDropdown(); - - protected override TabItem CreateTabItem(T value) => new OsuTabItem(value); - - protected virtual float StripWidth() => TabContainer.Children.Sum(c => c.IsPresent ? c.DrawWidth + TabContainer.Spacing.X : 0) - TabContainer.Spacing.X; - protected virtual float StripHeight() => 1; - - private static bool isEnumType => typeof(T).IsEnum; - - public OsuTabControl() - { - TabContainer.Spacing = new Vector2(10f, 0f); - - Add(strip = new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Height = StripHeight(), - Colour = Color4.White.Opacity(0), - }); - - if (isEnumType) - foreach (var val in (T[])Enum.GetValues(typeof(T))) - AddItem(val); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (accentColour == default(Color4)) - AccentColour = colours.Blue; - } - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - var dropdown = Dropdown as IHasAccentColour; - if (dropdown != null) - dropdown.AccentColour = value; - foreach (var i in TabContainer.Children.OfType()) - i.AccentColour = value; - } - } - - public Color4 StripColour - { - get => strip.Colour; - set => strip.Colour = value; - } - - protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer - { - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.Both, - Depth = -1, - Masking = true - }; - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - // dont bother calculating if the strip is invisible - if (strip.Colour.MaxAlpha > 0) - strip.Width = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), strip.Width, StripWidth(), 0, 500, Easing.OutQuint); - } - - public class OsuTabItem : TabItem, IHasAccentColour - { - protected readonly SpriteText Text; - protected readonly Box Bar; - - private Color4 accentColour; - public Color4 AccentColour - { - get { return accentColour; } - set - { - accentColour = value; - if (!Active) - Text.Colour = value; - } - } - - private const float transition_length = 500; - - private void fadeActive() - { - Bar.FadeIn(transition_length, Easing.OutQuint); - Text.FadeColour(Color4.White, transition_length, Easing.OutQuint); - } - - private void fadeInactive() - { - Bar.FadeOut(transition_length, Easing.OutQuint); - Text.FadeColour(AccentColour, transition_length, Easing.OutQuint); - } - - protected override bool OnHover(InputState state) - { - if (!Active) - fadeActive(); - return true; - } - - protected override void OnHoverLost(InputState state) - { - if (!Active) - fadeInactive(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (accentColour == default(Color4)) - AccentColour = colours.Blue; - } - - public OsuTabItem(T value) : base(value) - { - AutoSizeAxes = Axes.X; - RelativeSizeAxes = Axes.Y; - - Children = new Drawable[] - { - Text = new OsuSpriteText - { - Margin = new MarginPadding { Top = 5, Bottom = 5 }, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Text = (value as Enum)?.GetDescription() ?? value.ToString(), - TextSize = 14, - Font = @"Exo2.0-Bold", // Font should only turn bold when active? - }, - Bar = new Box - { - RelativeSizeAxes = Axes.X, - Height = 1, - Alpha = 0, - Colour = Color4.White, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - }, - new HoverClickSounds() - }; - } - - protected override void OnActivated() => fadeActive(); - - protected override void OnDeactivated() => fadeInactive(); - } - - // todo: this needs to go - private class OsuTabDropdown : OsuDropdown - { - public OsuTabDropdown() - { - RelativeSizeAxes = Axes.X; - } - - protected override DropdownMenu CreateMenu() => new OsuTabDropdownMenu(); - - protected override DropdownHeader CreateHeader() => new OsuTabDropdownHeader - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }; - - private class OsuTabDropdownMenu : OsuDropdownMenu - { - public OsuTabDropdownMenu() - { - Anchor = Anchor.TopRight; - Origin = Anchor.TopRight; - - BackgroundColour = Color4.Black.Opacity(0.7f); - MaxHeight = 400; - } - - protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuTabDropdownMenuItem(item) { AccentColour = AccentColour }; - - private class DrawableOsuTabDropdownMenuItem : DrawableOsuDropdownMenuItem - { - public DrawableOsuTabDropdownMenuItem(MenuItem item) - : base(item) - { - ForegroundColourHover = Color4.Black; - } - } - } - - protected class OsuTabDropdownHeader : OsuDropdownHeader - { - public override Color4 AccentColour - { - get - { - return base.AccentColour; - } - - set - { - base.AccentColour = value; - Foreground.Colour = value; - } - } - - public OsuTabDropdownHeader() - { - RelativeSizeAxes = Axes.None; - AutoSizeAxes = Axes.X; - - BackgroundColour = Color4.Black.Opacity(0.5f); - - Background.Height = 0.5f; - Background.CornerRadius = 5; - Background.Masking = true; - - Foreground.RelativeSizeAxes = Axes.None; - Foreground.AutoSizeAxes = Axes.X; - Foreground.RelativeSizeAxes = Axes.Y; - Foreground.Margin = new MarginPadding(5); - - Foreground.Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_ellipsis_h, - Size = new Vector2(14), - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - }; - - Padding = new MarginPadding { Left = 5, Right = 5 }; - } - - protected override bool OnHover(InputState state) - { - Foreground.Colour = BackgroundColour; - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - Foreground.Colour = BackgroundColourHover; - base.OnHoverLost(state); - } - } - } - - private class OsuTabFillFlowContainer : TabFillFlowContainer - { - protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Extensions.Color4Extensions; +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.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuTabControl : TabControl + { + private readonly Box strip; + + protected override Dropdown CreateDropdown() => new OsuTabDropdown(); + + protected override TabItem CreateTabItem(T value) => new OsuTabItem(value); + + protected virtual float StripWidth() => TabContainer.Children.Sum(c => c.IsPresent ? c.DrawWidth + TabContainer.Spacing.X : 0) - TabContainer.Spacing.X; + protected virtual float StripHeight() => 1; + + private static bool isEnumType => typeof(T).IsEnum; + + public OsuTabControl() + { + TabContainer.Spacing = new Vector2(10f, 0f); + + Add(strip = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Height = StripHeight(), + Colour = Color4.White.Opacity(0), + }); + + if (isEnumType) + foreach (var val in (T[])Enum.GetValues(typeof(T))) + AddItem(val); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (accentColour == default(Color4)) + AccentColour = colours.Blue; + } + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + var dropdown = Dropdown as IHasAccentColour; + if (dropdown != null) + dropdown.AccentColour = value; + foreach (var i in TabContainer.Children.OfType()) + i.AccentColour = value; + } + } + + public Color4 StripColour + { + get => strip.Colour; + set => strip.Colour = value; + } + + protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.Both, + Depth = -1, + Masking = true + }; + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // dont bother calculating if the strip is invisible + if (strip.Colour.MaxAlpha > 0) + strip.Width = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), strip.Width, StripWidth(), 0, 500, Easing.OutQuint); + } + + public class OsuTabItem : TabItem, IHasAccentColour + { + protected readonly SpriteText Text; + protected readonly Box Bar; + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + if (!Active) + Text.Colour = value; + } + } + + private const float transition_length = 500; + + private void fadeActive() + { + Bar.FadeIn(transition_length, Easing.OutQuint); + Text.FadeColour(Color4.White, transition_length, Easing.OutQuint); + } + + private void fadeInactive() + { + Bar.FadeOut(transition_length, Easing.OutQuint); + Text.FadeColour(AccentColour, transition_length, Easing.OutQuint); + } + + protected override bool OnHover(InputState state) + { + if (!Active) + fadeActive(); + return true; + } + + protected override void OnHoverLost(InputState state) + { + if (!Active) + fadeInactive(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (accentColour == default(Color4)) + AccentColour = colours.Blue; + } + + public OsuTabItem(T value) : base(value) + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + Children = new Drawable[] + { + Text = new OsuSpriteText + { + Margin = new MarginPadding { Top = 5, Bottom = 5 }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Text = (value as Enum)?.GetDescription() ?? value.ToString(), + TextSize = 14, + Font = @"Exo2.0-Bold", // Font should only turn bold when active? + }, + Bar = new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + Alpha = 0, + Colour = Color4.White, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + }, + new HoverClickSounds() + }; + } + + protected override void OnActivated() => fadeActive(); + + protected override void OnDeactivated() => fadeInactive(); + } + + // todo: this needs to go + private class OsuTabDropdown : OsuDropdown + { + public OsuTabDropdown() + { + RelativeSizeAxes = Axes.X; + } + + protected override DropdownMenu CreateMenu() => new OsuTabDropdownMenu(); + + protected override DropdownHeader CreateHeader() => new OsuTabDropdownHeader + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }; + + private class OsuTabDropdownMenu : OsuDropdownMenu + { + public OsuTabDropdownMenu() + { + Anchor = Anchor.TopRight; + Origin = Anchor.TopRight; + + BackgroundColour = Color4.Black.Opacity(0.7f); + MaxHeight = 400; + } + + protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuTabDropdownMenuItem(item) { AccentColour = AccentColour }; + + private class DrawableOsuTabDropdownMenuItem : DrawableOsuDropdownMenuItem + { + public DrawableOsuTabDropdownMenuItem(MenuItem item) + : base(item) + { + ForegroundColourHover = Color4.Black; + } + } + } + + protected class OsuTabDropdownHeader : OsuDropdownHeader + { + public override Color4 AccentColour + { + get + { + return base.AccentColour; + } + + set + { + base.AccentColour = value; + Foreground.Colour = value; + } + } + + public OsuTabDropdownHeader() + { + RelativeSizeAxes = Axes.None; + AutoSizeAxes = Axes.X; + + BackgroundColour = Color4.Black.Opacity(0.5f); + + Background.Height = 0.5f; + Background.CornerRadius = 5; + Background.Masking = true; + + Foreground.RelativeSizeAxes = Axes.None; + Foreground.AutoSizeAxes = Axes.X; + Foreground.RelativeSizeAxes = Axes.Y; + Foreground.Margin = new MarginPadding(5); + + Foreground.Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_ellipsis_h, + Size = new Vector2(14), + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + } + }; + + Padding = new MarginPadding { Left = 5, Right = 5 }; + } + + protected override bool OnHover(InputState state) + { + Foreground.Colour = BackgroundColour; + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + Foreground.Colour = BackgroundColourHover; + base.OnHoverLost(state); + } + } + } + + private class OsuTabFillFlowContainer : TabFillFlowContainer + { + protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 3fd7760802..13740c935f 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -1,136 +1,136 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Sprites; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// A Checkbox styled to be placed in line with an - /// - public class OsuTabControlCheckbox : Checkbox - { - private readonly Box box; - private readonly SpriteText text; - private readonly SpriteIcon icon; - - private Color4? accentColour; - public Color4 AccentColour - { - get { return accentColour.GetValueOrDefault(); } - set - { - accentColour = value; - - if (Current) - { - text.Colour = AccentColour; - icon.Colour = AccentColour; - } - } - } - - public string Text - { - get { return text.Text; } - set { text.Text = value; } - } - - private const float transition_length = 500; - - private void fadeIn() - { - box.FadeIn(transition_length, Easing.OutQuint); - text.FadeColour(Color4.White, transition_length, Easing.OutQuint); - } - - private void fadeOut() - { - box.FadeOut(transition_length, Easing.OutQuint); - text.FadeColour(AccentColour, transition_length, Easing.OutQuint); - } - - protected override bool OnHover(InputState state) - { - fadeIn(); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - if (!Current) - fadeOut(); - - base.OnHoverLost(state); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (accentColour == null) - AccentColour = colours.Blue; - } - - public OsuTabControlCheckbox() - { - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 5, Bottom = 5, }, - Spacing = new Vector2(5f, 0f), - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - text = new OsuSpriteText - { - TextSize = 14, - Font = @"Exo2.0-Bold", - }, - icon = new SpriteIcon - { - Size = new Vector2(14), - Icon = FontAwesome.fa_circle_o, - Shadow = true, - }, - }, - }, - box = new Box - { - RelativeSizeAxes = Axes.X, - Height = 1, - Alpha = 0, - Colour = Color4.White, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - } - }; - - Current.ValueChanged += v => - { - if (v) - { - fadeIn(); - icon.Icon = FontAwesome.fa_check_circle_o; - } - else - { - fadeOut(); - icon.Icon = FontAwesome.fa_circle_o; - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A Checkbox styled to be placed in line with an + /// + public class OsuTabControlCheckbox : Checkbox + { + private readonly Box box; + private readonly SpriteText text; + private readonly SpriteIcon icon; + + private Color4? accentColour; + public Color4 AccentColour + { + get { return accentColour.GetValueOrDefault(); } + set + { + accentColour = value; + + if (Current) + { + text.Colour = AccentColour; + icon.Colour = AccentColour; + } + } + } + + public string Text + { + get { return text.Text; } + set { text.Text = value; } + } + + private const float transition_length = 500; + + private void fadeIn() + { + box.FadeIn(transition_length, Easing.OutQuint); + text.FadeColour(Color4.White, transition_length, Easing.OutQuint); + } + + private void fadeOut() + { + box.FadeOut(transition_length, Easing.OutQuint); + text.FadeColour(AccentColour, transition_length, Easing.OutQuint); + } + + protected override bool OnHover(InputState state) + { + fadeIn(); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (!Current) + fadeOut(); + + base.OnHoverLost(state); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (accentColour == null) + AccentColour = colours.Blue; + } + + public OsuTabControlCheckbox() + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 5, Bottom = 5, }, + Spacing = new Vector2(5f, 0f), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + text = new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-Bold", + }, + icon = new SpriteIcon + { + Size = new Vector2(14), + Icon = FontAwesome.fa_circle_o, + Shadow = true, + }, + }, + }, + box = new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + Alpha = 0, + Colour = Color4.White, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + } + }; + + Current.ValueChanged += v => + { + if (v) + { + fadeIn(); + icon.Icon = FontAwesome.fa_check_circle_o; + } + else + { + fadeOut(); + icon.Icon = FontAwesome.fa_circle_o; + } + }; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index 31093f1429..6021af2028 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -1,63 +1,63 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics.Sprites; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; - -namespace osu.Game.Graphics.UserInterface -{ - public class OsuTextBox : TextBox - { - protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.5f); - protected override Color4 BackgroundFocused => OsuColour.Gray(0.3f).Opacity(0.8f); - protected override Color4 BackgroundCommit => BorderColour; - - protected override float LeftRightPadding => 10; - - protected override SpriteText CreatePlaceholder() => new OsuSpriteText - { - Font = @"Exo2.0-MediumItalic", - Colour = new Color4(180, 180, 180, 255), - Margin = new MarginPadding { Left = 2 }, - }; - - public OsuTextBox() - { - Height = 40; - TextContainer.Height = 0.5f; - CornerRadius = 5; - - Current.DisabledChanged += disabled => - { - Alpha = disabled ? 0.3f : 1; - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - BorderColour = colour.Yellow; - } - - protected override void OnFocus(InputState state) - { - BorderThickness = 3; - base.OnFocus(state); - } - - protected override void OnFocusLost(InputState state) - { - BorderThickness = 0; - - base.OnFocusLost(state); - } - - protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), TextSize = CalculatedTextSize }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuTextBox : TextBox + { + protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.5f); + protected override Color4 BackgroundFocused => OsuColour.Gray(0.3f).Opacity(0.8f); + protected override Color4 BackgroundCommit => BorderColour; + + protected override float LeftRightPadding => 10; + + protected override SpriteText CreatePlaceholder() => new OsuSpriteText + { + Font = @"Exo2.0-MediumItalic", + Colour = new Color4(180, 180, 180, 255), + Margin = new MarginPadding { Left = 2 }, + }; + + public OsuTextBox() + { + Height = 40; + TextContainer.Height = 0.5f; + CornerRadius = 5; + + Current.DisabledChanged += disabled => + { + Alpha = disabled ? 0.3f : 1; + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + BorderColour = colour.Yellow; + } + + protected override void OnFocus(InputState state) + { + BorderThickness = 3; + base.OnFocus(state); + } + + protected override void OnFocusLost(InputState state) + { + BorderThickness = 0; + + base.OnFocusLost(state); + } + + protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), TextSize = CalculatedTextSize }; + } +} diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index 57ee334f6b..3a8c72725e 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -1,98 +1,98 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Graphics.UserInterface -{ - public class PageTabControl : OsuTabControl - { - protected override TabItem CreateTabItem(T value) => new PageTabItem(value); - - public PageTabControl() - { - Height = 30; - } - - public class PageTabItem : TabItem - { - private const float transition_duration = 100; - - private readonly Box box; - - protected readonly SpriteText Text; - - public PageTabItem(T value) : base(value) - { - AutoSizeAxes = Axes.X; - RelativeSizeAxes = Axes.Y; - - Children = new Drawable[] - { - Text = new OsuSpriteText - { - Margin = new MarginPadding { Top = 8, Bottom = 8 }, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Text = (value as Enum)?.GetDescription() ?? value.ToString(), - TextSize = 14, - Font = @"Exo2.0-Bold", - }, - box = new Box - { - RelativeSizeAxes = Axes.X, - Height = 5, - Scale = new Vector2(1f, 0f), - Colour = Color4.White, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - }, - new HoverClickSounds() - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - box.Colour = colours.Yellow; - } - - protected override bool OnHover(InputState state) - { - if (!Active) - slideActive(); - return true; - } - - protected override void OnHoverLost(InputState state) - { - if (!Active) - slideInactive(); - } - - private void slideActive() - { - box.ScaleTo(new Vector2(1f), transition_duration); - } - - private void slideInactive() - { - box.ScaleTo(new Vector2(1f, 0f), transition_duration); - } - - protected override void OnActivated() => slideActive(); - - protected override void OnDeactivated() => slideInactive(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + public class PageTabControl : OsuTabControl + { + protected override TabItem CreateTabItem(T value) => new PageTabItem(value); + + public PageTabControl() + { + Height = 30; + } + + public class PageTabItem : TabItem + { + private const float transition_duration = 100; + + private readonly Box box; + + protected readonly SpriteText Text; + + public PageTabItem(T value) : base(value) + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + Children = new Drawable[] + { + Text = new OsuSpriteText + { + Margin = new MarginPadding { Top = 8, Bottom = 8 }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Text = (value as Enum)?.GetDescription() ?? value.ToString(), + TextSize = 14, + Font = @"Exo2.0-Bold", + }, + box = new Box + { + RelativeSizeAxes = Axes.X, + Height = 5, + Scale = new Vector2(1f, 0f), + Colour = Color4.White, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + }, + new HoverClickSounds() + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + box.Colour = colours.Yellow; + } + + protected override bool OnHover(InputState state) + { + if (!Active) + slideActive(); + return true; + } + + protected override void OnHoverLost(InputState state) + { + if (!Active) + slideInactive(); + } + + private void slideActive() + { + box.ScaleTo(new Vector2(1f), transition_duration); + } + + private void slideInactive() + { + box.ScaleTo(new Vector2(1f, 0f), transition_duration); + } + + protected override void OnActivated() => slideActive(); + + protected override void OnDeactivated() => slideInactive(); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index 376f7a46ee..ef3fc156e7 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// Used as an accuracy counter. Represented visually as a percentage. - /// - public class PercentageCounter : RollingCounter - { - protected override double RollingDuration => 750; - - private float epsilon => 1e-10f; - - public void SetFraction(float numerator, float denominator) - { - Current.Value = Math.Abs(denominator) < epsilon ? 1.0f : numerator / denominator; - } - - public PercentageCounter() - { - DisplayedCountSpriteText.FixedWidth = true; - Current.Value = DisplayedCount = 1.0f; - } - - protected override string FormatCount(double count) - { - return $@"{count:P2}"; - } - - protected override double GetProportionalDuration(double currentValue, double newValue) - { - return Math.Abs(currentValue - newValue) * RollingDuration * 100.0f; - } - - public override void Increment(double amount) - { - Current.Value = Current + amount; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Used as an accuracy counter. Represented visually as a percentage. + /// + public class PercentageCounter : RollingCounter + { + protected override double RollingDuration => 750; + + private float epsilon => 1e-10f; + + public void SetFraction(float numerator, float denominator) + { + Current.Value = Math.Abs(denominator) < epsilon ? 1.0f : numerator / denominator; + } + + public PercentageCounter() + { + DisplayedCountSpriteText.FixedWidth = true; + Current.Value = DisplayedCount = 1.0f; + } + + protected override string FormatCount(double count) + { + return $@"{count:P2}"; + } + + protected override double GetProportionalDuration(double currentValue, double newValue) + { + return Math.Abs(currentValue - newValue) * RollingDuration * 100.0f; + } + + public override void Increment(double amount) + { + Current.Value = Current + amount; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ProgressBar.cs b/osu.Game/Graphics/UserInterface/ProgressBar.cs index 6021cf08be..beb1d0055e 100644 --- a/osu.Game/Graphics/UserInterface/ProgressBar.cs +++ b/osu.Game/Graphics/UserInterface/ProgressBar.cs @@ -1,67 +1,67 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using OpenTK.Graphics; - -namespace osu.Game.Graphics.UserInterface -{ - public class ProgressBar : SliderBar - { - public Action OnSeek; - - private readonly Box fill; - private readonly Box background; - - public Color4 FillColour - { - set { fill.FadeColour(value, 150, Easing.OutQuint); } - } - - public Color4 BackgroundColour - { - set - { - background.Alpha = 1; - background.Colour = value; - } - } - - public double EndTime - { - set { CurrentNumber.MaxValue = value; } - } - - public double CurrentTime - { - set { CurrentNumber.Value = value; } - } - - public ProgressBar() - { - CurrentNumber.MinValue = 0; - CurrentNumber.MaxValue = 1; - RelativeSizeAxes = Axes.X; - - Children = new Drawable[] - { - background = new Box - { - Alpha = 0, - RelativeSizeAxes = Axes.Both - }, - fill = new Box { RelativeSizeAxes = Axes.Y } - }; - } - - protected override void UpdateValue(float value) - { - fill.Width = value * UsableWidth; - } - - protected override void OnUserChange() => OnSeek?.Invoke(Current); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using OpenTK.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class ProgressBar : SliderBar + { + public Action OnSeek; + + private readonly Box fill; + private readonly Box background; + + public Color4 FillColour + { + set { fill.FadeColour(value, 150, Easing.OutQuint); } + } + + public Color4 BackgroundColour + { + set + { + background.Alpha = 1; + background.Colour = value; + } + } + + public double EndTime + { + set { CurrentNumber.MaxValue = value; } + } + + public double CurrentTime + { + set { CurrentNumber.Value = value; } + } + + public ProgressBar() + { + CurrentNumber.MinValue = 0; + CurrentNumber.MaxValue = 1; + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + background = new Box + { + Alpha = 0, + RelativeSizeAxes = Axes.Both + }, + fill = new Box { RelativeSizeAxes = Axes.Y } + }; + } + + protected override void UpdateValue(float value) + { + fill.Width = value * UsableWidth; + } + + protected override void OnUserChange() => OnSeek?.Invoke(Current); + } +} diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index 3eeba02154..f84404a911 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -1,182 +1,182 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Sprites; -using System; -using System.Collections.Generic; -using OpenTK.Graphics; - -namespace osu.Game.Graphics.UserInterface -{ - public abstract class RollingCounter : Container, IHasAccentColour - where T : struct, IEquatable - { - /// - /// The current value. - /// - public Bindable Current = new Bindable(); - - protected SpriteText DisplayedCountSpriteText; - - /// - /// If true, the roll-up duration will be proportional to change in value. - /// - protected virtual bool IsRollingProportional => false; - - /// - /// If IsRollingProportional = false, duration in milliseconds for the counter roll-up animation for each - /// element; else duration in milliseconds for the counter roll-up animation in total. - /// - protected virtual double RollingDuration => 0; - - /// - /// Easing for the counter rollover animation. - /// - protected virtual Easing RollingEasing => Easing.OutQuint; - - private T displayedCount; - - /// - /// Value shown at the current moment. - /// - public virtual T DisplayedCount - { - get - { - return displayedCount; - } - - set - { - if (EqualityComparer.Default.Equals(displayedCount, value)) - return; - displayedCount = value; - DisplayedCountSpriteText.Text = FormatCount(value); - } - } - - public abstract void Increment(T amount); - - private float textSize; - - public float TextSize - { - get { return textSize; } - set - { - textSize = value; - DisplayedCountSpriteText.TextSize = value; - } - } - - public Color4 AccentColour - { - get { return DisplayedCountSpriteText.Colour; } - set { DisplayedCountSpriteText.Colour = value; } - } - - /// - /// Skeleton of a numeric counter which value rolls over time. - /// - protected RollingCounter() - { - Children = new Drawable[] - { - DisplayedCountSpriteText = new OsuSpriteText - { - Font = @"Venera" - }, - }; - - TextSize = 40; - AutoSizeAxes = Axes.Both; - - DisplayedCount = Current; - - Current.ValueChanged += newValue => - { - if (IsLoaded) TransformCount(displayedCount, newValue); - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - DisplayedCountSpriteText.Text = FormatCount(Current); - } - - /// - /// Sets count value, bypassing rollover animation. - /// - /// New count value. - public virtual void SetCountWithoutRolling(T count) - { - Current.Value = count; - StopRolling(); - } - - /// - /// Stops rollover animation, forcing the displayed count to be the actual count. - /// - public virtual void StopRolling() - { - FinishTransforms(false, nameof(DisplayedCount)); - DisplayedCount = Current; - } - - /// - /// Resets count to default value. - /// - public virtual void ResetCount() - { - SetCountWithoutRolling(default(T)); - } - - /// - /// Calculates the duration of the roll-up animation by using the difference between the current visible value - /// and the new final value. - /// - /// - /// To be used in conjunction with IsRollingProportional = true. - /// Unless a derived class needs to have a proportional rolling, it is not necessary to override this function. - /// - /// Current visible value. - /// New final value. - /// Calculated rollover duration in milliseconds. - protected virtual double GetProportionalDuration(T currentValue, T newValue) - { - return RollingDuration; - } - - /// - /// Used to format counts. - /// - /// Count to format. - /// Count formatted as a string. - protected virtual string FormatCount(T count) - { - return count.ToString(); - } - - /// - /// Called when the count is updated to add a transformer that changes the value of the visible count (i.e. - /// implement the rollover animation). - /// - /// Count value before modification. - /// Expected count value after modification. - protected virtual void TransformCount(T currentValue, T newValue) - { - double rollingTotalDuration = - IsRollingProportional - ? GetProportionalDuration(currentValue, newValue) - : RollingDuration; - - this.TransformTo(nameof(DisplayedCount), newValue, rollingTotalDuration, RollingEasing); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Sprites; +using System; +using System.Collections.Generic; +using OpenTK.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public abstract class RollingCounter : Container, IHasAccentColour + where T : struct, IEquatable + { + /// + /// The current value. + /// + public Bindable Current = new Bindable(); + + protected SpriteText DisplayedCountSpriteText; + + /// + /// If true, the roll-up duration will be proportional to change in value. + /// + protected virtual bool IsRollingProportional => false; + + /// + /// If IsRollingProportional = false, duration in milliseconds for the counter roll-up animation for each + /// element; else duration in milliseconds for the counter roll-up animation in total. + /// + protected virtual double RollingDuration => 0; + + /// + /// Easing for the counter rollover animation. + /// + protected virtual Easing RollingEasing => Easing.OutQuint; + + private T displayedCount; + + /// + /// Value shown at the current moment. + /// + public virtual T DisplayedCount + { + get + { + return displayedCount; + } + + set + { + if (EqualityComparer.Default.Equals(displayedCount, value)) + return; + displayedCount = value; + DisplayedCountSpriteText.Text = FormatCount(value); + } + } + + public abstract void Increment(T amount); + + private float textSize; + + public float TextSize + { + get { return textSize; } + set + { + textSize = value; + DisplayedCountSpriteText.TextSize = value; + } + } + + public Color4 AccentColour + { + get { return DisplayedCountSpriteText.Colour; } + set { DisplayedCountSpriteText.Colour = value; } + } + + /// + /// Skeleton of a numeric counter which value rolls over time. + /// + protected RollingCounter() + { + Children = new Drawable[] + { + DisplayedCountSpriteText = new OsuSpriteText + { + Font = @"Venera" + }, + }; + + TextSize = 40; + AutoSizeAxes = Axes.Both; + + DisplayedCount = Current; + + Current.ValueChanged += newValue => + { + if (IsLoaded) TransformCount(displayedCount, newValue); + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + DisplayedCountSpriteText.Text = FormatCount(Current); + } + + /// + /// Sets count value, bypassing rollover animation. + /// + /// New count value. + public virtual void SetCountWithoutRolling(T count) + { + Current.Value = count; + StopRolling(); + } + + /// + /// Stops rollover animation, forcing the displayed count to be the actual count. + /// + public virtual void StopRolling() + { + FinishTransforms(false, nameof(DisplayedCount)); + DisplayedCount = Current; + } + + /// + /// Resets count to default value. + /// + public virtual void ResetCount() + { + SetCountWithoutRolling(default(T)); + } + + /// + /// Calculates the duration of the roll-up animation by using the difference between the current visible value + /// and the new final value. + /// + /// + /// To be used in conjunction with IsRollingProportional = true. + /// Unless a derived class needs to have a proportional rolling, it is not necessary to override this function. + /// + /// Current visible value. + /// New final value. + /// Calculated rollover duration in milliseconds. + protected virtual double GetProportionalDuration(T currentValue, T newValue) + { + return RollingDuration; + } + + /// + /// Used to format counts. + /// + /// Count to format. + /// Count formatted as a string. + protected virtual string FormatCount(T count) + { + return count.ToString(); + } + + /// + /// Called when the count is updated to add a transformer that changes the value of the visible count (i.e. + /// implement the rollover animation). + /// + /// Count value before modification. + /// Expected count value after modification. + protected virtual void TransformCount(T currentValue, T newValue) + { + double rollingTotalDuration = + IsRollingProportional + ? GetProportionalDuration(currentValue, newValue) + : RollingDuration; + + this.TransformTo(nameof(DisplayedCount), newValue, rollingTotalDuration, RollingEasing); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 8fd9f41211..df26f81629 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -1,54 +1,54 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; - -namespace osu.Game.Graphics.UserInterface -{ - public class ScoreCounter : RollingCounter - { - protected override double RollingDuration => 1000; - protected override Easing RollingEasing => Easing.Out; - - public bool UseCommaSeparator; - - /// - /// How many leading zeroes the counter has. - /// - public uint LeadingZeroes - { - get; - protected set; - } - - /// - /// Displays score. - /// - /// How many leading zeroes the counter will have. - public ScoreCounter(uint leading = 0) - { - DisplayedCountSpriteText.FixedWidth = true; - LeadingZeroes = leading; - } - - protected override double GetProportionalDuration(double currentValue, double newValue) - { - return currentValue > newValue ? currentValue - newValue : newValue - currentValue; - } - - protected override string FormatCount(double count) - { - string format = new string('0', (int)LeadingZeroes); - if (UseCommaSeparator) - for (int i = format.Length - 3; i > 0; i -= 3) - format = format.Insert(i, @","); - - return ((long)count).ToString(format); - } - - public override void Increment(double amount) - { - Current.Value = Current + amount; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class ScoreCounter : RollingCounter + { + protected override double RollingDuration => 1000; + protected override Easing RollingEasing => Easing.Out; + + public bool UseCommaSeparator; + + /// + /// How many leading zeroes the counter has. + /// + public uint LeadingZeroes + { + get; + protected set; + } + + /// + /// Displays score. + /// + /// How many leading zeroes the counter will have. + public ScoreCounter(uint leading = 0) + { + DisplayedCountSpriteText.FixedWidth = true; + LeadingZeroes = leading; + } + + protected override double GetProportionalDuration(double currentValue, double newValue) + { + return currentValue > newValue ? currentValue - newValue : newValue - currentValue; + } + + protected override string FormatCount(double count) + { + string format = new string('0', (int)LeadingZeroes); + if (UseCommaSeparator) + for (int i = format.Length - 3; i > 0; i -= 3) + format = format.Insert(i, @","); + + return ((long)count).ToString(format); + } + + public override void Increment(double amount) + { + Current.Value = Current + amount; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 28d33bbacd..e50539e120 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -1,73 +1,73 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Input; -using OpenTK; -using OpenTK.Input; - -namespace osu.Game.Graphics.UserInterface -{ - public class SearchTextBox : FocusedTextBox - { - protected virtual bool AllowCommit => false; - - public override bool HandleLeftRightArrows => false; - - public SearchTextBox() - { - Height = 35; - AddRange(new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_search, - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Margin = new MarginPadding { Right = 10 }, - Size = new Vector2(20), - } - }); - - PlaceholderText = "type to search"; - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (HandlePendingText(state)) return true; - - if (!state.Keyboard.ControlPressed && !state.Keyboard.ShiftPressed) - { - switch (args.Key) - { - case Key.Left: - case Key.Right: - case Key.Up: - case Key.Down: - return false; - } - } - - if (!AllowCommit) - { - switch (args.Key) - { - case Key.KeypadEnter: - case Key.Enter: - return false; - } - } - - if (state.Keyboard.ShiftPressed) - { - switch (args.Key) - { - case Key.Delete: - return false; - } - } - - return base.OnKeyDown(state, args); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Input; +using OpenTK; +using OpenTK.Input; + +namespace osu.Game.Graphics.UserInterface +{ + public class SearchTextBox : FocusedTextBox + { + protected virtual bool AllowCommit => false; + + public override bool HandleLeftRightArrows => false; + + public SearchTextBox() + { + Height = 35; + AddRange(new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_search, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Margin = new MarginPadding { Right = 10 }, + Size = new Vector2(20), + } + }); + + PlaceholderText = "type to search"; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (HandlePendingText(state)) return true; + + if (!state.Keyboard.ControlPressed && !state.Keyboard.ShiftPressed) + { + switch (args.Key) + { + case Key.Left: + case Key.Right: + case Key.Up: + case Key.Down: + return false; + } + } + + if (!AllowCommit) + { + switch (args.Key) + { + case Key.KeypadEnter: + case Key.Enter: + return false; + } + } + + if (state.Keyboard.ShiftPressed) + { + switch (args.Key) + { + case Key.Delete: + return false; + } + } + + return base.OnKeyDown(state, args); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs b/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs index cf09609516..cf55240e71 100644 --- a/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs +++ b/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// Used as an accuracy counter. Represented visually as a percentage. - /// - public class SimpleComboCounter : RollingCounter - { - protected override double RollingDuration => 750; - - public SimpleComboCounter() - { - Current.Value = DisplayedCount = 0; - } - - protected override string FormatCount(int count) - { - return $@"{count}x"; - } - - protected override double GetProportionalDuration(int currentValue, int newValue) - { - return Math.Abs(currentValue - newValue) * RollingDuration * 100.0f; - } - - public override void Increment(int amount) - { - Current.Value = Current + amount; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Used as an accuracy counter. Represented visually as a percentage. + /// + public class SimpleComboCounter : RollingCounter + { + protected override double RollingDuration => 750; + + public SimpleComboCounter() + { + Current.Value = DisplayedCount = 0; + } + + protected override string FormatCount(int count) + { + return $@"{count}x"; + } + + protected override double GetProportionalDuration(int currentValue, int newValue) + { + return Math.Abs(currentValue - newValue) * RollingDuration * 100.0f; + } + + public override void Increment(int amount) + { + Current.Value = Current + amount; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index cefa4b3e28..621b5dcf11 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -1,154 +1,154 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; -using System; -using System.Linq; - -namespace osu.Game.Graphics.UserInterface -{ - public class StarCounter : Container - { - private readonly Container stars; - - /// - /// Maximum amount of stars displayed. - /// - /// - /// This does not limit the counter value, but the amount of stars displayed. - /// - public int StarCount { get; } - - private double animationDelay => 80; - - private double scalingDuration => 1000; - private Easing scalingEasing => Easing.OutElasticHalf; - private float minStarScale => 0.4f; - - private double fadingDuration => 100; - private float minStarAlpha => 0.5f; - - private const float star_size = 20; - private const float star_spacing = 4; - - private float countStars; - - /// - /// Amount of stars represented. - /// - public float CountStars - { - get - { - return countStars; - } - - set - { - if (countStars == value) return; - - if (IsLoaded) - transformCount(value); - countStars = value; - } - } - - /// - /// Shows a float count as stars. Used as star difficulty display. - /// - /// Maximum amount of stars to display. - public StarCounter(int starCount = 10) - { - StarCount = Math.Max(starCount, 0); - - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - stars = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(star_spacing), - ChildrenEnumerable = Enumerable.Range(0, StarCount).Select(i => new Star { Alpha = minStarAlpha }) - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // Animate initial state from zero. - ReplayAnimation(); - } - - public void ResetCount() - { - countStars = 0; - StopAnimation(); - } - - public void ReplayAnimation() - { - var t = countStars; - ResetCount(); - CountStars = t; - } - - public void StopAnimation() - { - int i = 0; - foreach (var star in stars.Children) - { - star.ClearTransforms(true); - star.FadeTo(i < countStars ? 1.0f : minStarAlpha); - star.Icon.ScaleTo(getStarScale(i, countStars)); - i++; - } - } - - private float getStarScale(int i, float value) - { - if (value <= i) - return minStarScale; - - return i + 1 <= value ? 1.0f : Interpolation.ValueAt(value, minStarScale, 1.0f, i, i + 1); - } - - private void transformCount(float newValue) - { - int i = 0; - foreach (var star in stars.Children) - { - star.ClearTransforms(true); - - var delay = (countStars <= newValue ? Math.Max(i - countStars, 0) : Math.Max(countStars - 1 - i, 0)) * animationDelay; - star.Delay(delay).FadeTo(i < newValue ? 1.0f : minStarAlpha, fadingDuration); - star.Icon.Delay(delay).ScaleTo(getStarScale(i, newValue), scalingDuration, scalingEasing); - - i++; - } - } - - private class Star : Container - { - public readonly SpriteIcon Icon; - public Star() - { - Size = new Vector2(star_size); - - Child = Icon = new SpriteIcon - { - Size = new Vector2(star_size), - Icon = FontAwesome.fa_star, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.MathUtils; +using System; +using System.Linq; + +namespace osu.Game.Graphics.UserInterface +{ + public class StarCounter : Container + { + private readonly Container stars; + + /// + /// Maximum amount of stars displayed. + /// + /// + /// This does not limit the counter value, but the amount of stars displayed. + /// + public int StarCount { get; } + + private double animationDelay => 80; + + private double scalingDuration => 1000; + private Easing scalingEasing => Easing.OutElasticHalf; + private float minStarScale => 0.4f; + + private double fadingDuration => 100; + private float minStarAlpha => 0.5f; + + private const float star_size = 20; + private const float star_spacing = 4; + + private float countStars; + + /// + /// Amount of stars represented. + /// + public float CountStars + { + get + { + return countStars; + } + + set + { + if (countStars == value) return; + + if (IsLoaded) + transformCount(value); + countStars = value; + } + } + + /// + /// Shows a float count as stars. Used as star difficulty display. + /// + /// Maximum amount of stars to display. + public StarCounter(int starCount = 10) + { + StarCount = Math.Max(starCount, 0); + + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + stars = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(star_spacing), + ChildrenEnumerable = Enumerable.Range(0, StarCount).Select(i => new Star { Alpha = minStarAlpha }) + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // Animate initial state from zero. + ReplayAnimation(); + } + + public void ResetCount() + { + countStars = 0; + StopAnimation(); + } + + public void ReplayAnimation() + { + var t = countStars; + ResetCount(); + CountStars = t; + } + + public void StopAnimation() + { + int i = 0; + foreach (var star in stars.Children) + { + star.ClearTransforms(true); + star.FadeTo(i < countStars ? 1.0f : minStarAlpha); + star.Icon.ScaleTo(getStarScale(i, countStars)); + i++; + } + } + + private float getStarScale(int i, float value) + { + if (value <= i) + return minStarScale; + + return i + 1 <= value ? 1.0f : Interpolation.ValueAt(value, minStarScale, 1.0f, i, i + 1); + } + + private void transformCount(float newValue) + { + int i = 0; + foreach (var star in stars.Children) + { + star.ClearTransforms(true); + + var delay = (countStars <= newValue ? Math.Max(i - countStars, 0) : Math.Max(countStars - 1 - i, 0)) * animationDelay; + star.Delay(delay).FadeTo(i < newValue ? 1.0f : minStarAlpha, fadingDuration); + star.Icon.Delay(delay).ScaleTo(getStarScale(i, newValue), scalingDuration, scalingEasing); + + i++; + } + } + + private class Star : Container + { + public readonly SpriteIcon Icon; + public Star() + { + Size = new Vector2(star_size); + + Child = Icon = new SpriteIcon + { + Size = new Vector2(star_size), + Icon = FontAwesome.fa_star, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs index c87d81641f..a6492ddd63 100644 --- a/osu.Game/Graphics/UserInterface/TriangleButton.cs +++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs @@ -1,108 +1,108 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Graphics.UserInterface -{ - /// - /// A button with moving triangles in the background. - /// - public class TriangleButton : OsuButton, IFilterable - { - private Box hover; - - protected Triangles Triangles; - - public TriangleButton() - { - Height = 40; - } - - protected override SpriteText CreateText() => new OsuSpriteText - { - Depth = -1, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Font = @"Exo2.0-Bold", - }; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.BlueDark; - - Content.Masking = true; - Content.CornerRadius = 5; - - AddRange(new Drawable[] - { - Triangles = new Triangles - { - RelativeSizeAxes = Axes.Both, - ColourDark = colours.BlueDarker, - ColourLight = colours.Blue, - }, - hover = new Box - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Colour = Color4.White.Opacity(0.1f), - Alpha = 0, - }, - }); - - Enabled.ValueChanged += enabled_ValueChanged; - Enabled.TriggerChange(); - } - - private void enabled_ValueChanged(bool enabled) - { - this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); - } - - protected override bool OnHover(InputState state) - { - hover.FadeIn(200); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - hover.FadeOut(200); - base.OnHoverLost(state); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - Content.ScaleTo(0.9f, 4000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - Content.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - - public IEnumerable FilterTerms => new[] { Text }; - - public bool MatchingFilter - { - set - { - this.FadeTo(value ? 1 : 0); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A button with moving triangles in the background. + /// + public class TriangleButton : OsuButton, IFilterable + { + private Box hover; + + protected Triangles Triangles; + + public TriangleButton() + { + Height = 40; + } + + protected override SpriteText CreateText() => new OsuSpriteText + { + Depth = -1, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Font = @"Exo2.0-Bold", + }; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.BlueDark; + + Content.Masking = true; + Content.CornerRadius = 5; + + AddRange(new Drawable[] + { + Triangles = new Triangles + { + RelativeSizeAxes = Axes.Both, + ColourDark = colours.BlueDarker, + ColourLight = colours.Blue, + }, + hover = new Box + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Colour = Color4.White.Opacity(0.1f), + Alpha = 0, + }, + }); + + Enabled.ValueChanged += enabled_ValueChanged; + Enabled.TriggerChange(); + } + + private void enabled_ValueChanged(bool enabled) + { + this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); + } + + protected override bool OnHover(InputState state) + { + hover.FadeIn(200); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + hover.FadeOut(200); + base.OnHoverLost(state); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + Content.ScaleTo(0.9f, 4000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + Content.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + + public IEnumerable FilterTerms => new[] { Text }; + + public bool MatchingFilter + { + set + { + this.FadeTo(value ? 1 : 0); + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 751f4809dc..f490306acf 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -1,254 +1,254 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Framework.Extensions.Color4Extensions; -using osu.Game.Graphics.Containers; -using osu.Game.Beatmaps.ControlPoints; -using osu.Framework.Audio.Track; -using System; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Graphics.UserInterface -{ - public class TwoLayerButton : OsuClickableContainer - { - private readonly BouncingIcon bouncingIcon; - - public Box IconLayer; - public Box TextLayer; - - private const int transform_time = 600; - private const int pulse_length = 250; - - private const float shear = 0.1f; - - public static readonly Vector2 SIZE_EXTENDED = new Vector2(140, 50); - public static readonly Vector2 SIZE_RETRACTED = new Vector2(100, 50); - private readonly SpriteText text; - - public Color4 HoverColour; - private readonly Container c1; - private readonly Container c2; - - public Color4 BackgroundColour - { - set - { - TextLayer.Colour = value; - IconLayer.Colour = value; - } - } - - public override Anchor Origin - { - get - { - return base.Origin; - } - - set - { - base.Origin = value; - c1.Origin = c1.Anchor = (value & Anchor.x2) > 0 ? Anchor.TopLeft : Anchor.TopRight; - c2.Origin = c2.Anchor = (value & Anchor.x2) > 0 ? Anchor.TopRight : Anchor.TopLeft; - - X = (value & Anchor.x2) > 0 ? SIZE_RETRACTED.X * shear * 0.5f : 0; - - Remove(c1); - Remove(c2); - c1.Depth = (value & Anchor.x2) > 0 ? 0 : 1; - c2.Depth = (value & Anchor.x2) > 0 ? 1 : 0; - Add(c1); - Add(c2); - } - } - - public TwoLayerButton() - { - Size = SIZE_RETRACTED; - - Children = new Drawable[] - { - c2 = new Container - { - RelativeSizeAxes = Axes.Both, - Width = 0.4f, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Shear = new Vector2(shear, 0), - Masking = true, - MaskingSmoothness = 2, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.2f), - Offset = new Vector2(2, 0), - Radius = 2, - }, - Children = new[] - { - IconLayer = new Box - { - RelativeSizeAxes = Axes.Both, - EdgeSmoothness = new Vector2(2, 0), - }, - } - }, - bouncingIcon = new BouncingIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - } - }, - c1 = new Container - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - RelativeSizeAxes = Axes.Both, - Width = 0.6f, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Shear = new Vector2(shear, 0), - Masking = true, - MaskingSmoothness = 2, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.2f), - Offset = new Vector2(2, 0), - Radius = 2, - }, - Children = new[] - { - TextLayer = new Box - { - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - RelativeSizeAxes = Axes.Both, - EdgeSmoothness = new Vector2(2, 0), - }, - } - }, - text = new OsuSpriteText - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - } - }, - }; - } - - public FontAwesome Icon - { - set - { - bouncingIcon.Icon = value; - } - } - - public string Text - { - set - { - text.Text = value; - } - } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => IconLayer.ReceiveMouseInputAt(screenSpacePos) || TextLayer.ReceiveMouseInputAt(screenSpacePos); - - protected override bool OnHover(InputState state) - { - this.ResizeTo(SIZE_EXTENDED, transform_time, Easing.OutElastic); - IconLayer.FadeColour(HoverColour, transform_time, Easing.OutElastic); - - bouncingIcon.ScaleTo(1.1f, transform_time, Easing.OutElastic); - - return true; - } - - protected override void OnHoverLost(InputState state) - { - this.ResizeTo(SIZE_RETRACTED, transform_time, Easing.OutElastic); - IconLayer.FadeColour(TextLayer.Colour, transform_time, Easing.OutElastic); - - bouncingIcon.ScaleTo(1, transform_time, Easing.OutElastic); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - return true; - } - - protected override bool OnClick(InputState state) - { - var flash = new Box - { - RelativeSizeAxes = Axes.Both, - Shear = new Vector2(shear, 0), - Colour = Color4.White.Opacity(0.5f), - }; - Add(flash); - - flash.Alpha = 1; - flash.FadeOut(500, Easing.OutQuint); - flash.Expire(); - - return base.OnClick(state); - } - - private class BouncingIcon : BeatSyncedContainer - { - private const double beat_in_time = 60; - - private readonly SpriteIcon icon; - - public FontAwesome Icon { set { icon.Icon = value; } } - - public BouncingIcon() - { - EarlyActivationMilliseconds = beat_in_time; - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(25), - } - }; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - var beatLength = timingPoint.BeatLength; - - float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum); - - if (beatIndex < 0) return; - - icon.ScaleTo(1 - 0.1f * amplitudeAdjust, beat_in_time, Easing.Out) - .Then() - .ScaleTo(1, beatLength * 2, Easing.OutQuint); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; +using osu.Game.Beatmaps.ControlPoints; +using osu.Framework.Audio.Track; +using System; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Graphics.UserInterface +{ + public class TwoLayerButton : OsuClickableContainer + { + private readonly BouncingIcon bouncingIcon; + + public Box IconLayer; + public Box TextLayer; + + private const int transform_time = 600; + private const int pulse_length = 250; + + private const float shear = 0.1f; + + public static readonly Vector2 SIZE_EXTENDED = new Vector2(140, 50); + public static readonly Vector2 SIZE_RETRACTED = new Vector2(100, 50); + private readonly SpriteText text; + + public Color4 HoverColour; + private readonly Container c1; + private readonly Container c2; + + public Color4 BackgroundColour + { + set + { + TextLayer.Colour = value; + IconLayer.Colour = value; + } + } + + public override Anchor Origin + { + get + { + return base.Origin; + } + + set + { + base.Origin = value; + c1.Origin = c1.Anchor = (value & Anchor.x2) > 0 ? Anchor.TopLeft : Anchor.TopRight; + c2.Origin = c2.Anchor = (value & Anchor.x2) > 0 ? Anchor.TopRight : Anchor.TopLeft; + + X = (value & Anchor.x2) > 0 ? SIZE_RETRACTED.X * shear * 0.5f : 0; + + Remove(c1); + Remove(c2); + c1.Depth = (value & Anchor.x2) > 0 ? 0 : 1; + c2.Depth = (value & Anchor.x2) > 0 ? 1 : 0; + Add(c1); + Add(c2); + } + } + + public TwoLayerButton() + { + Size = SIZE_RETRACTED; + + Children = new Drawable[] + { + c2 = new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.4f, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(shear, 0), + Masking = true, + MaskingSmoothness = 2, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Offset = new Vector2(2, 0), + Radius = 2, + }, + Children = new[] + { + IconLayer = new Box + { + RelativeSizeAxes = Axes.Both, + EdgeSmoothness = new Vector2(2, 0), + }, + } + }, + bouncingIcon = new BouncingIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } + }, + c1 = new Container + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Both, + Width = 0.6f, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(shear, 0), + Masking = true, + MaskingSmoothness = 2, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Offset = new Vector2(2, 0), + Radius = 2, + }, + Children = new[] + { + TextLayer = new Box + { + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + RelativeSizeAxes = Axes.Both, + EdgeSmoothness = new Vector2(2, 0), + }, + } + }, + text = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + } + } + }, + }; + } + + public FontAwesome Icon + { + set + { + bouncingIcon.Icon = value; + } + } + + public string Text + { + set + { + text.Text = value; + } + } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => IconLayer.ReceiveMouseInputAt(screenSpacePos) || TextLayer.ReceiveMouseInputAt(screenSpacePos); + + protected override bool OnHover(InputState state) + { + this.ResizeTo(SIZE_EXTENDED, transform_time, Easing.OutElastic); + IconLayer.FadeColour(HoverColour, transform_time, Easing.OutElastic); + + bouncingIcon.ScaleTo(1.1f, transform_time, Easing.OutElastic); + + return true; + } + + protected override void OnHoverLost(InputState state) + { + this.ResizeTo(SIZE_RETRACTED, transform_time, Easing.OutElastic); + IconLayer.FadeColour(TextLayer.Colour, transform_time, Easing.OutElastic); + + bouncingIcon.ScaleTo(1, transform_time, Easing.OutElastic); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + return true; + } + + protected override bool OnClick(InputState state) + { + var flash = new Box + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(shear, 0), + Colour = Color4.White.Opacity(0.5f), + }; + Add(flash); + + flash.Alpha = 1; + flash.FadeOut(500, Easing.OutQuint); + flash.Expire(); + + return base.OnClick(state); + } + + private class BouncingIcon : BeatSyncedContainer + { + private const double beat_in_time = 60; + + private readonly SpriteIcon icon; + + public FontAwesome Icon { set { icon.Icon = value; } } + + public BouncingIcon() + { + EarlyActivationMilliseconds = beat_in_time; + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(25), + } + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + var beatLength = timingPoint.BeatLength; + + float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum); + + if (beatIndex < 0) return; + + icon.ScaleTo(1 - 0.1f * amplitudeAdjust, beat_in_time, Easing.Out) + .Then() + .ScaleTo(1, beatLength * 2, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs index 351a6dff39..d14080de5b 100644 --- a/osu.Game/IO/Archives/ArchiveReader.cs +++ b/osu.Game/IO/Archives/ArchiveReader.cs @@ -1,47 +1,47 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using osu.Framework.IO.Stores; - -namespace osu.Game.IO.Archives -{ - public abstract class ArchiveReader : IDisposable, IResourceStore - { - /// - /// Opens a stream for reading a specific file from this archive. - /// - public abstract Stream GetStream(string name); - - public abstract void Dispose(); - - /// - /// The name of this archive (usually the containing filename). - /// - public readonly string Name; - - protected ArchiveReader(string name) - { - Name = name; - } - - public abstract IEnumerable Filenames { get; } - - public virtual byte[] Get(string name) - { - using (Stream input = GetStream(name)) - { - if (input == null) - return null; - - byte[] buffer = new byte[input.Length]; - input.Read(buffer, 0, buffer.Length); - return buffer; - } - } - - public abstract Stream GetUnderlyingStream(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using osu.Framework.IO.Stores; + +namespace osu.Game.IO.Archives +{ + public abstract class ArchiveReader : IDisposable, IResourceStore + { + /// + /// Opens a stream for reading a specific file from this archive. + /// + public abstract Stream GetStream(string name); + + public abstract void Dispose(); + + /// + /// The name of this archive (usually the containing filename). + /// + public readonly string Name; + + protected ArchiveReader(string name) + { + Name = name; + } + + public abstract IEnumerable Filenames { get; } + + public virtual byte[] Get(string name) + { + using (Stream input = GetStream(name)) + { + if (input == null) + return null; + + byte[] buffer = new byte[input.Length]; + input.Read(buffer, 0, buffer.Length); + return buffer; + } + } + + public abstract Stream GetUnderlyingStream(); + } +} diff --git a/osu.Game/IO/Archives/LegacyFilesystemReader.cs b/osu.Game/IO/Archives/LegacyFilesystemReader.cs index d6d80783db..7871f1439a 100644 --- a/osu.Game/IO/Archives/LegacyFilesystemReader.cs +++ b/osu.Game/IO/Archives/LegacyFilesystemReader.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.IO; -using System.Linq; -using osu.Framework.IO.File; - -namespace osu.Game.IO.Archives -{ - /// - /// Reads an extracted legacy beatmap from disk. - /// - public class LegacyFilesystemReader : ArchiveReader - { - private readonly string path; - - public LegacyFilesystemReader(string path) : base(Path.GetFileName(path)) - { - this.path = path; - } - - public override Stream GetStream(string name) => File.OpenRead(Path.Combine(path, name)); - - public override void Dispose() - { - // no-op - } - - public override IEnumerable Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => FileSafety.GetRelativePath(f, path)).ToArray(); - - public override Stream GetUnderlyingStream() => null; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using osu.Framework.IO.File; + +namespace osu.Game.IO.Archives +{ + /// + /// Reads an extracted legacy beatmap from disk. + /// + public class LegacyFilesystemReader : ArchiveReader + { + private readonly string path; + + public LegacyFilesystemReader(string path) : base(Path.GetFileName(path)) + { + this.path = path; + } + + public override Stream GetStream(string name) => File.OpenRead(Path.Combine(path, name)); + + public override void Dispose() + { + // no-op + } + + public override IEnumerable Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => FileSafety.GetRelativePath(f, path)).ToArray(); + + public override Stream GetUnderlyingStream() => null; + } +} diff --git a/osu.Game/IO/Archives/ZipArchiveReader.cs b/osu.Game/IO/Archives/ZipArchiveReader.cs index dbf236e835..2ea35835fa 100644 --- a/osu.Game/IO/Archives/ZipArchiveReader.cs +++ b/osu.Game/IO/Archives/ZipArchiveReader.cs @@ -1,50 +1,50 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.IO; -using System.Linq; -using SharpCompress.Archives.Zip; - -namespace osu.Game.IO.Archives -{ - public sealed class ZipArchiveReader : ArchiveReader - { - private readonly Stream archiveStream; - private readonly ZipArchive archive; - - public ZipArchiveReader(Stream archiveStream, string name = null) - : base(name) - { - this.archiveStream = archiveStream; - archive = ZipArchive.Open(archiveStream); - } - - public override Stream GetStream(string name) - { - ZipArchiveEntry entry = archive.Entries.SingleOrDefault(e => e.Key == name); - if (entry == null) - throw new FileNotFoundException(); - - // allow seeking - MemoryStream copy = new MemoryStream(); - - using (Stream s = entry.OpenEntryStream()) - s.CopyTo(copy); - - copy.Position = 0; - - return copy; - } - - public override void Dispose() - { - archive.Dispose(); - archiveStream.Dispose(); - } - - public override IEnumerable Filenames => archive.Entries.Select(e => e.Key).ToArray(); - - public override Stream GetUnderlyingStream() => archiveStream; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SharpCompress.Archives.Zip; + +namespace osu.Game.IO.Archives +{ + public sealed class ZipArchiveReader : ArchiveReader + { + private readonly Stream archiveStream; + private readonly ZipArchive archive; + + public ZipArchiveReader(Stream archiveStream, string name = null) + : base(name) + { + this.archiveStream = archiveStream; + archive = ZipArchive.Open(archiveStream); + } + + public override Stream GetStream(string name) + { + ZipArchiveEntry entry = archive.Entries.SingleOrDefault(e => e.Key == name); + if (entry == null) + throw new FileNotFoundException(); + + // allow seeking + MemoryStream copy = new MemoryStream(); + + using (Stream s = entry.OpenEntryStream()) + s.CopyTo(copy); + + copy.Position = 0; + + return copy; + } + + public override void Dispose() + { + archive.Dispose(); + archiveStream.Dispose(); + } + + public override IEnumerable Filenames => archive.Entries.Select(e => e.Key).ToArray(); + + public override Stream GetUnderlyingStream() => archiveStream; + } +} diff --git a/osu.Game/IO/FileInfo.cs b/osu.Game/IO/FileInfo.cs index 94a3d455d4..1804be90a0 100644 --- a/osu.Game/IO/FileInfo.cs +++ b/osu.Game/IO/FileInfo.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel.DataAnnotations.Schema; -using System.IO; - -namespace osu.Game.IO -{ - public class FileInfo - { - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int ID { get; set; } - - public string Hash { get; set; } - - public string StoragePath => Path.Combine(Hash.Remove(1), Hash.Remove(2), Hash); - - public int ReferenceCount { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel.DataAnnotations.Schema; +using System.IO; + +namespace osu.Game.IO +{ + public class FileInfo + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ID { get; set; } + + public string Hash { get; set; } + + public string StoragePath => Path.Combine(Hash.Remove(1), Hash.Remove(2), Hash); + + public int ReferenceCount { get; set; } + } +} diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs index ab81ba4851..050fccb98a 100644 --- a/osu.Game/IO/FileStore.cs +++ b/osu.Game/IO/FileStore.cs @@ -1,114 +1,114 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using System.Linq; -using osu.Framework.Extensions; -using osu.Framework.IO.Stores; -using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Game.Database; - -namespace osu.Game.IO -{ - /// - /// Handles the Store and retrieval of Files/FileSets to the database backing - /// - public class FileStore : DatabaseBackedStore - { - public readonly IResourceStore Store; - - public new Storage Storage => base.Storage; - - public FileStore(IDatabaseContextFactory contextFactory, Storage storage) : base(contextFactory, storage.GetStorageForDirectory(@"files")) - { - Store = new StorageBackedResourceStore(Storage); - } - - public FileInfo Add(Stream data, bool reference = true) - { - using (var usage = ContextFactory.GetForWrite()) - { - string hash = data.ComputeSHA2Hash(); - - var existing = usage.Context.FileInfo.FirstOrDefault(f => f.Hash == hash); - - var info = existing ?? new FileInfo { Hash = hash }; - - string path = info.StoragePath; - - // we may be re-adding a file to fix missing store entries. - if (!Storage.Exists(path)) - { - data.Seek(0, SeekOrigin.Begin); - - using (var output = Storage.GetStream(path, FileAccess.Write)) - data.CopyTo(output); - - data.Seek(0, SeekOrigin.Begin); - } - - if (reference || existing == null) - Reference(info); - - return info; - } - } - - public void Reference(params FileInfo[] files) - { - if (files.Length == 0) return; - - using (var usage = ContextFactory.GetForWrite()) - { - var context = usage.Context; - - foreach (var f in files.GroupBy(f => f.ID)) - { - var refetch = context.Find(f.First().ID) ?? f.First(); - refetch.ReferenceCount += f.Count(); - context.FileInfo.Update(refetch); - } - } - } - - public void Dereference(params FileInfo[] files) - { - if (files.Length == 0) return; - - using (var usage = ContextFactory.GetForWrite()) - { - var context = usage.Context; - - foreach (var f in files.GroupBy(f => f.ID)) - { - var refetch = context.FileInfo.Find(f.Key); - refetch.ReferenceCount -= f.Count(); - context.FileInfo.Update(refetch); - } - } - } - - public override void Cleanup() - { - using (var usage = ContextFactory.GetForWrite()) - { - var context = usage.Context; - - foreach (var f in context.FileInfo.Where(f => f.ReferenceCount < 1)) - { - try - { - Storage.Delete(f.StoragePath); - context.FileInfo.Remove(f); - } - catch (Exception e) - { - Logger.Error(e, $@"Could not delete beatmap {f}"); - } - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using System.Linq; +using osu.Framework.Extensions; +using osu.Framework.IO.Stores; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Database; + +namespace osu.Game.IO +{ + /// + /// Handles the Store and retrieval of Files/FileSets to the database backing + /// + public class FileStore : DatabaseBackedStore + { + public readonly IResourceStore Store; + + public new Storage Storage => base.Storage; + + public FileStore(IDatabaseContextFactory contextFactory, Storage storage) : base(contextFactory, storage.GetStorageForDirectory(@"files")) + { + Store = new StorageBackedResourceStore(Storage); + } + + public FileInfo Add(Stream data, bool reference = true) + { + using (var usage = ContextFactory.GetForWrite()) + { + string hash = data.ComputeSHA2Hash(); + + var existing = usage.Context.FileInfo.FirstOrDefault(f => f.Hash == hash); + + var info = existing ?? new FileInfo { Hash = hash }; + + string path = info.StoragePath; + + // we may be re-adding a file to fix missing store entries. + if (!Storage.Exists(path)) + { + data.Seek(0, SeekOrigin.Begin); + + using (var output = Storage.GetStream(path, FileAccess.Write)) + data.CopyTo(output); + + data.Seek(0, SeekOrigin.Begin); + } + + if (reference || existing == null) + Reference(info); + + return info; + } + } + + public void Reference(params FileInfo[] files) + { + if (files.Length == 0) return; + + using (var usage = ContextFactory.GetForWrite()) + { + var context = usage.Context; + + foreach (var f in files.GroupBy(f => f.ID)) + { + var refetch = context.Find(f.First().ID) ?? f.First(); + refetch.ReferenceCount += f.Count(); + context.FileInfo.Update(refetch); + } + } + } + + public void Dereference(params FileInfo[] files) + { + if (files.Length == 0) return; + + using (var usage = ContextFactory.GetForWrite()) + { + var context = usage.Context; + + foreach (var f in files.GroupBy(f => f.ID)) + { + var refetch = context.FileInfo.Find(f.Key); + refetch.ReferenceCount -= f.Count(); + context.FileInfo.Update(refetch); + } + } + } + + public override void Cleanup() + { + using (var usage = ContextFactory.GetForWrite()) + { + var context = usage.Context; + + foreach (var f in context.FileInfo.Where(f => f.ReferenceCount < 1)) + { + try + { + Storage.Delete(f.StoragePath); + context.FileInfo.Remove(f); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete beatmap {f}"); + } + } + } + } + } +} diff --git a/osu.Game/IO/Legacy/ILegacySerializable.cs b/osu.Game/IO/Legacy/ILegacySerializable.cs index 43c12a631a..1bf33d8bd7 100644 --- a/osu.Game/IO/Legacy/ILegacySerializable.cs +++ b/osu.Game/IO/Legacy/ILegacySerializable.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.IO.Legacy -{ - public interface ILegacySerializable - { - void ReadFromStream(SerializationReader sr); - void WriteToStream(SerializationWriter sw); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.IO.Legacy +{ + public interface ILegacySerializable + { + void ReadFromStream(SerializationReader sr); + void WriteToStream(SerializationWriter sw); + } +} diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index 75cbef80a5..8fd18b9640 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -1,277 +1,277 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; -using System.Text; - -namespace osu.Game.IO.Legacy -{ - /// SerializationReader. Extends BinaryReader to add additional data types, - /// handle null strings and simplify use with ISerializable. - public class SerializationReader : BinaryReader - { - private readonly Stream stream; - - public SerializationReader(Stream s) - : base(s, Encoding.UTF8) - { - stream = s; - } - - public int RemainingBytes => (int)(stream.Length - stream.Position); - - /// Static method to take a SerializationInfo object (an input to an ISerializable constructor) - /// and produce a SerializationReader from which serialized objects can be read . - public static SerializationReader GetReader(SerializationInfo info) - { - byte[] byteArray = (byte[])info.GetValue("X", typeof(byte[])); - MemoryStream ms = new MemoryStream(byteArray); - return new SerializationReader(ms); - } - - /// Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. - public override string ReadString() - { - if (0 == ReadByte()) return null; - return base.ReadString(); - } - - /// Reads a byte array from the buffer, handling nulls and the array length. - public byte[] ReadByteArray() - { - int len = ReadInt32(); - if (len > 0) return ReadBytes(len); - if (len < 0) return null; - return Array.Empty(); - } - - /// Reads a char array from the buffer, handling nulls and the array length. - public char[] ReadCharArray() - { - int len = ReadInt32(); - if (len > 0) return ReadChars(len); - if (len < 0) return null; - return Array.Empty(); - } - - /// Reads a DateTime from the buffer. - public DateTime ReadDateTime() - { - long ticks = ReadInt64(); - if (ticks < 0) throw new IOException("Bad ticks count read!"); - return new DateTime(ticks, DateTimeKind.Utc); - } - - /// Reads a generic list from the buffer. - public IList ReadBList(bool skipErrors = false) where T : ILegacySerializable, new() - { - int count = ReadInt32(); - if (count < 0) return null; - IList d = new List(count); - - SerializationReader sr = new SerializationReader(BaseStream); - - for (int i = 0; i < count; i++) - { - T obj = new T(); - try - { - obj.ReadFromStream(sr); - } - catch (Exception) - { - if (skipErrors) - continue; - throw; - } - - d.Add(obj); - } - - return d; - } - - /// Reads a generic list from the buffer. - public IList ReadList() - { - int count = ReadInt32(); - if (count < 0) return null; - IList d = new List(count); - for (int i = 0; i < count; i++) d.Add((T)ReadObject()); - return d; - } - - /// Reads a generic Dictionary from the buffer. - public IDictionary ReadDictionary() - { - int count = ReadInt32(); - if (count < 0) return null; - IDictionary d = new Dictionary(); - for (int i = 0; i < count; i++) d[(T)ReadObject()] = (U)ReadObject(); - return d; - } - - /// Reads an object which was added to the buffer by WriteObject. - public object ReadObject() - { - ObjType t = (ObjType)ReadByte(); - switch (t) - { - case ObjType.boolType: - return ReadBoolean(); - case ObjType.byteType: - return ReadByte(); - case ObjType.uint16Type: - return ReadUInt16(); - case ObjType.uint32Type: - return ReadUInt32(); - case ObjType.uint64Type: - return ReadUInt64(); - case ObjType.sbyteType: - return ReadSByte(); - case ObjType.int16Type: - return ReadInt16(); - case ObjType.int32Type: - return ReadInt32(); - case ObjType.int64Type: - return ReadInt64(); - case ObjType.charType: - return ReadChar(); - case ObjType.stringType: - return base.ReadString(); - case ObjType.singleType: - return ReadSingle(); - case ObjType.doubleType: - return ReadDouble(); - case ObjType.decimalType: - return ReadDecimal(); - case ObjType.dateTimeType: - return ReadDateTime(); - case ObjType.byteArrayType: - return ReadByteArray(); - case ObjType.charArrayType: - return ReadCharArray(); - case ObjType.otherType: - return DynamicDeserializer.Deserialize(BaseStream); - default: - return null; - } - } - - public class DynamicDeserializer - { - private static VersionConfigToNamespaceAssemblyObjectBinder versionBinder; - private static BinaryFormatter formatter; - - private static void initialize() - { - versionBinder = new VersionConfigToNamespaceAssemblyObjectBinder(); - formatter = new BinaryFormatter - { -// AssemblyFormat = FormatterAssemblyStyle.Simple, - Binder = versionBinder - }; - } - - public static object Deserialize(Stream stream) - { - if (formatter == null) - initialize(); - - Debug.Assert(formatter != null, "formatter != null"); - - // ReSharper disable once PossibleNullReferenceException - return formatter.Deserialize(stream); - } - - #region Nested type: VersionConfigToNamespaceAssemblyObjectBinder - - public sealed class VersionConfigToNamespaceAssemblyObjectBinder : SerializationBinder - { - private readonly Dictionary cache = new Dictionary(); - - public override Type BindToType(string assemblyName, string typeName) - { - Type typeToDeserialize; - - if (cache.TryGetValue(assemblyName + typeName, out typeToDeserialize)) - return typeToDeserialize; - - List tmpTypes = new List(); - Type genType = null; - - if (typeName.Contains("System.Collections.Generic") && typeName.Contains("[[")) - { - string[] splitTyps = typeName.Split('['); - - foreach (string typ in splitTyps) - { - if (typ.Contains("Version")) - { - string asmTmp = typ.Substring(typ.IndexOf(',') + 1); - string asmName = asmTmp.Remove(asmTmp.IndexOf(']')).Trim(); - string typName = typ.Remove(typ.IndexOf(',')); - tmpTypes.Add(BindToType(asmName, typName)); - } - else if (typ.Contains("Generic")) - { - genType = BindToType(assemblyName, typ); - } - } - if (genType != null && tmpTypes.Count > 0) - { - return genType.MakeGenericType(tmpTypes.ToArray()); - } - } - - string toAssemblyName = assemblyName.Split(',')[0]; - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (Assembly a in assemblies) - { - if (a.FullName.Split(',')[0] == toAssemblyName) - { - typeToDeserialize = a.GetType(typeName); - break; - } - } - - cache.Add(assemblyName + typeName, typeToDeserialize); - - return typeToDeserialize; - } - } - - #endregion - } - } - - public enum ObjType : byte - { - nullType, - boolType, - byteType, - uint16Type, - uint32Type, - uint64Type, - sbyteType, - int16Type, - int32Type, - int64Type, - charType, - stringType, - singleType, - doubleType, - decimalType, - dateTimeType, - byteArrayType, - charArrayType, - otherType, - ILegacySerializableType - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; + +namespace osu.Game.IO.Legacy +{ + /// SerializationReader. Extends BinaryReader to add additional data types, + /// handle null strings and simplify use with ISerializable. + public class SerializationReader : BinaryReader + { + private readonly Stream stream; + + public SerializationReader(Stream s) + : base(s, Encoding.UTF8) + { + stream = s; + } + + public int RemainingBytes => (int)(stream.Length - stream.Position); + + /// Static method to take a SerializationInfo object (an input to an ISerializable constructor) + /// and produce a SerializationReader from which serialized objects can be read . + public static SerializationReader GetReader(SerializationInfo info) + { + byte[] byteArray = (byte[])info.GetValue("X", typeof(byte[])); + MemoryStream ms = new MemoryStream(byteArray); + return new SerializationReader(ms); + } + + /// Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. + public override string ReadString() + { + if (0 == ReadByte()) return null; + return base.ReadString(); + } + + /// Reads a byte array from the buffer, handling nulls and the array length. + public byte[] ReadByteArray() + { + int len = ReadInt32(); + if (len > 0) return ReadBytes(len); + if (len < 0) return null; + return Array.Empty(); + } + + /// Reads a char array from the buffer, handling nulls and the array length. + public char[] ReadCharArray() + { + int len = ReadInt32(); + if (len > 0) return ReadChars(len); + if (len < 0) return null; + return Array.Empty(); + } + + /// Reads a DateTime from the buffer. + public DateTime ReadDateTime() + { + long ticks = ReadInt64(); + if (ticks < 0) throw new IOException("Bad ticks count read!"); + return new DateTime(ticks, DateTimeKind.Utc); + } + + /// Reads a generic list from the buffer. + public IList ReadBList(bool skipErrors = false) where T : ILegacySerializable, new() + { + int count = ReadInt32(); + if (count < 0) return null; + IList d = new List(count); + + SerializationReader sr = new SerializationReader(BaseStream); + + for (int i = 0; i < count; i++) + { + T obj = new T(); + try + { + obj.ReadFromStream(sr); + } + catch (Exception) + { + if (skipErrors) + continue; + throw; + } + + d.Add(obj); + } + + return d; + } + + /// Reads a generic list from the buffer. + public IList ReadList() + { + int count = ReadInt32(); + if (count < 0) return null; + IList d = new List(count); + for (int i = 0; i < count; i++) d.Add((T)ReadObject()); + return d; + } + + /// Reads a generic Dictionary from the buffer. + public IDictionary ReadDictionary() + { + int count = ReadInt32(); + if (count < 0) return null; + IDictionary d = new Dictionary(); + for (int i = 0; i < count; i++) d[(T)ReadObject()] = (U)ReadObject(); + return d; + } + + /// Reads an object which was added to the buffer by WriteObject. + public object ReadObject() + { + ObjType t = (ObjType)ReadByte(); + switch (t) + { + case ObjType.boolType: + return ReadBoolean(); + case ObjType.byteType: + return ReadByte(); + case ObjType.uint16Type: + return ReadUInt16(); + case ObjType.uint32Type: + return ReadUInt32(); + case ObjType.uint64Type: + return ReadUInt64(); + case ObjType.sbyteType: + return ReadSByte(); + case ObjType.int16Type: + return ReadInt16(); + case ObjType.int32Type: + return ReadInt32(); + case ObjType.int64Type: + return ReadInt64(); + case ObjType.charType: + return ReadChar(); + case ObjType.stringType: + return base.ReadString(); + case ObjType.singleType: + return ReadSingle(); + case ObjType.doubleType: + return ReadDouble(); + case ObjType.decimalType: + return ReadDecimal(); + case ObjType.dateTimeType: + return ReadDateTime(); + case ObjType.byteArrayType: + return ReadByteArray(); + case ObjType.charArrayType: + return ReadCharArray(); + case ObjType.otherType: + return DynamicDeserializer.Deserialize(BaseStream); + default: + return null; + } + } + + public class DynamicDeserializer + { + private static VersionConfigToNamespaceAssemblyObjectBinder versionBinder; + private static BinaryFormatter formatter; + + private static void initialize() + { + versionBinder = new VersionConfigToNamespaceAssemblyObjectBinder(); + formatter = new BinaryFormatter + { +// AssemblyFormat = FormatterAssemblyStyle.Simple, + Binder = versionBinder + }; + } + + public static object Deserialize(Stream stream) + { + if (formatter == null) + initialize(); + + Debug.Assert(formatter != null, "formatter != null"); + + // ReSharper disable once PossibleNullReferenceException + return formatter.Deserialize(stream); + } + + #region Nested type: VersionConfigToNamespaceAssemblyObjectBinder + + public sealed class VersionConfigToNamespaceAssemblyObjectBinder : SerializationBinder + { + private readonly Dictionary cache = new Dictionary(); + + public override Type BindToType(string assemblyName, string typeName) + { + Type typeToDeserialize; + + if (cache.TryGetValue(assemblyName + typeName, out typeToDeserialize)) + return typeToDeserialize; + + List tmpTypes = new List(); + Type genType = null; + + if (typeName.Contains("System.Collections.Generic") && typeName.Contains("[[")) + { + string[] splitTyps = typeName.Split('['); + + foreach (string typ in splitTyps) + { + if (typ.Contains("Version")) + { + string asmTmp = typ.Substring(typ.IndexOf(',') + 1); + string asmName = asmTmp.Remove(asmTmp.IndexOf(']')).Trim(); + string typName = typ.Remove(typ.IndexOf(',')); + tmpTypes.Add(BindToType(asmName, typName)); + } + else if (typ.Contains("Generic")) + { + genType = BindToType(assemblyName, typ); + } + } + if (genType != null && tmpTypes.Count > 0) + { + return genType.MakeGenericType(tmpTypes.ToArray()); + } + } + + string toAssemblyName = assemblyName.Split(',')[0]; + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly a in assemblies) + { + if (a.FullName.Split(',')[0] == toAssemblyName) + { + typeToDeserialize = a.GetType(typeName); + break; + } + } + + cache.Add(assemblyName + typeName, typeToDeserialize); + + return typeToDeserialize; + } + } + + #endregion + } + } + + public enum ObjType : byte + { + nullType, + boolType, + byteType, + uint16Type, + uint32Type, + uint64Type, + sbyteType, + int16Type, + int32Type, + int64Type, + charType, + stringType, + singleType, + doubleType, + decimalType, + dateTimeType, + byteArrayType, + charArrayType, + otherType, + ILegacySerializableType + } +} diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs index f972796383..85728794af 100644 --- a/osu.Game/IO/Legacy/SerializationWriter.cs +++ b/osu.Game/IO/Legacy/SerializationWriter.cs @@ -1,262 +1,262 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters; -using System.Runtime.Serialization.Formatters.Binary; -using System.Text; -// ReSharper disable ConditionIsAlwaysTrueOrFalse (we're allowing nulls to be passed to the writer where the underlying class doesn't). -// ReSharper disable HeuristicUnreachableCode - -namespace osu.Game.IO.Legacy -{ - /// SerializationWriter. Extends BinaryWriter to add additional data types, - /// handle null strings and simplify use with ISerializable. - public class SerializationWriter : BinaryWriter - { - public SerializationWriter(Stream s) - : base(s, Encoding.UTF8) - { - } - - /// Static method to initialise the writer with a suitable MemoryStream. - public static SerializationWriter GetWriter() - { - MemoryStream ms = new MemoryStream(1024); - return new SerializationWriter(ms); - } - - /// Writes a string to the buffer. Overrides the base implementation so it can cope with nulls - public override void Write(string str) - { - if (str == null) - { - Write((byte)ObjType.nullType); - } - else - { - Write((byte)ObjType.stringType); - base.Write(str); - } - } - - /// Writes a byte array to the buffer. Overrides the base implementation to - /// send the length of the array which is needed when it is retrieved - public override void Write(byte[] b) - { - if (b == null) - { - Write(-1); - } - else - { - int len = b.Length; - Write(len); - if (len > 0) base.Write(b); - } - } - - /// Writes a char array to the buffer. Overrides the base implementation to - /// sends the length of the array which is needed when it is read. - public override void Write(char[] c) - { - if (c == null) - { - Write(-1); - } - else - { - int len = c.Length; - Write(len); - if (len > 0) base.Write(c); - } - } - - /// - /// Writes DateTime to the buffer. - /// - /// - public void Write(DateTime dt) - { - Write(dt.ToUniversalTime().Ticks); - } - - /// Writes a generic ICollection (such as an IList(T)) to the buffer. - public void Write(List c) where T : ILegacySerializable - { - if (c == null) - { - Write(-1); - } - else - { - int count = c.Count; - Write(count); - for (int i = 0; i < count; i++) - c[i].WriteToStream(this); - } - } - - /// Writes a generic IDictionary to the buffer. - public void Write(IDictionary d) - { - if (d == null) - { - Write(-1); - } - else - { - Write(d.Count); - foreach (KeyValuePair kvp in d) - { - WriteObject(kvp.Key); - WriteObject(kvp.Value); - } - } - } - - /// Writes an arbitrary object to the buffer. Useful where we have something of type "object" - /// and don't know how to treat it. This works out the best method to use to write to the buffer. - public void WriteObject(object obj) - { - if (obj == null) - { - Write((byte)ObjType.nullType); - } - else - { - switch (obj.GetType().Name) - { - case "Boolean": - Write((byte)ObjType.boolType); - Write((bool)obj); - break; - - case "Byte": - Write((byte)ObjType.byteType); - Write((byte)obj); - break; - - case "UInt16": - Write((byte)ObjType.uint16Type); - Write((ushort)obj); - break; - - case "UInt32": - Write((byte)ObjType.uint32Type); - Write((uint)obj); - break; - - case "UInt64": - Write((byte)ObjType.uint64Type); - Write((ulong)obj); - break; - - case "SByte": - Write((byte)ObjType.sbyteType); - Write((sbyte)obj); - break; - - case "Int16": - Write((byte)ObjType.int16Type); - Write((short)obj); - break; - - case "Int32": - Write((byte)ObjType.int32Type); - Write((int)obj); - break; - - case "Int64": - Write((byte)ObjType.int64Type); - Write((long)obj); - break; - - case "Char": - Write((byte)ObjType.charType); - base.Write((char)obj); - break; - - case "String": - Write((byte)ObjType.stringType); - base.Write((string)obj); - break; - - case "Single": - Write((byte)ObjType.singleType); - Write((float)obj); - break; - - case "Double": - Write((byte)ObjType.doubleType); - Write((double)obj); - break; - - case "Decimal": - Write((byte)ObjType.decimalType); - Write((decimal)obj); - break; - - case "DateTime": - Write((byte)ObjType.dateTimeType); - Write((DateTime)obj); - break; - - case "Byte[]": - Write((byte)ObjType.byteArrayType); - base.Write((byte[])obj); - break; - - case "Char[]": - Write((byte)ObjType.charArrayType); - base.Write((char[])obj); - break; - - default: - Write((byte)ObjType.otherType); - BinaryFormatter b = new BinaryFormatter - { -// AssemblyFormat = FormatterAssemblyStyle.Simple, - TypeFormat = FormatterTypeStyle.TypesWhenNeeded - }; - b.Serialize(BaseStream, obj); - break; - } // switch - } // if obj==null - } // WriteObject - - /// Adds the SerializationWriter buffer to the SerializationInfo at the end of GetObjectData(). - public void AddToInfo(SerializationInfo info) - { - byte[] b = ((MemoryStream)BaseStream).ToArray(); - info.AddValue("X", b, typeof(byte[])); - } - - public void WriteRawBytes(byte[] b) - { - base.Write(b); - } - - public void WriteByteArray(byte[] b) - { - if (b == null) - { - Write(-1); - } - else - { - int len = b.Length; - Write(len); - if (len > 0) base.Write(b); - } - } - - public void WriteUtf8(string str) - { - WriteRawBytes(Encoding.UTF8.GetBytes(str)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +// ReSharper disable ConditionIsAlwaysTrueOrFalse (we're allowing nulls to be passed to the writer where the underlying class doesn't). +// ReSharper disable HeuristicUnreachableCode + +namespace osu.Game.IO.Legacy +{ + /// SerializationWriter. Extends BinaryWriter to add additional data types, + /// handle null strings and simplify use with ISerializable. + public class SerializationWriter : BinaryWriter + { + public SerializationWriter(Stream s) + : base(s, Encoding.UTF8) + { + } + + /// Static method to initialise the writer with a suitable MemoryStream. + public static SerializationWriter GetWriter() + { + MemoryStream ms = new MemoryStream(1024); + return new SerializationWriter(ms); + } + + /// Writes a string to the buffer. Overrides the base implementation so it can cope with nulls + public override void Write(string str) + { + if (str == null) + { + Write((byte)ObjType.nullType); + } + else + { + Write((byte)ObjType.stringType); + base.Write(str); + } + } + + /// Writes a byte array to the buffer. Overrides the base implementation to + /// send the length of the array which is needed when it is retrieved + public override void Write(byte[] b) + { + if (b == null) + { + Write(-1); + } + else + { + int len = b.Length; + Write(len); + if (len > 0) base.Write(b); + } + } + + /// Writes a char array to the buffer. Overrides the base implementation to + /// sends the length of the array which is needed when it is read. + public override void Write(char[] c) + { + if (c == null) + { + Write(-1); + } + else + { + int len = c.Length; + Write(len); + if (len > 0) base.Write(c); + } + } + + /// + /// Writes DateTime to the buffer. + /// + /// + public void Write(DateTime dt) + { + Write(dt.ToUniversalTime().Ticks); + } + + /// Writes a generic ICollection (such as an IList(T)) to the buffer. + public void Write(List c) where T : ILegacySerializable + { + if (c == null) + { + Write(-1); + } + else + { + int count = c.Count; + Write(count); + for (int i = 0; i < count; i++) + c[i].WriteToStream(this); + } + } + + /// Writes a generic IDictionary to the buffer. + public void Write(IDictionary d) + { + if (d == null) + { + Write(-1); + } + else + { + Write(d.Count); + foreach (KeyValuePair kvp in d) + { + WriteObject(kvp.Key); + WriteObject(kvp.Value); + } + } + } + + /// Writes an arbitrary object to the buffer. Useful where we have something of type "object" + /// and don't know how to treat it. This works out the best method to use to write to the buffer. + public void WriteObject(object obj) + { + if (obj == null) + { + Write((byte)ObjType.nullType); + } + else + { + switch (obj.GetType().Name) + { + case "Boolean": + Write((byte)ObjType.boolType); + Write((bool)obj); + break; + + case "Byte": + Write((byte)ObjType.byteType); + Write((byte)obj); + break; + + case "UInt16": + Write((byte)ObjType.uint16Type); + Write((ushort)obj); + break; + + case "UInt32": + Write((byte)ObjType.uint32Type); + Write((uint)obj); + break; + + case "UInt64": + Write((byte)ObjType.uint64Type); + Write((ulong)obj); + break; + + case "SByte": + Write((byte)ObjType.sbyteType); + Write((sbyte)obj); + break; + + case "Int16": + Write((byte)ObjType.int16Type); + Write((short)obj); + break; + + case "Int32": + Write((byte)ObjType.int32Type); + Write((int)obj); + break; + + case "Int64": + Write((byte)ObjType.int64Type); + Write((long)obj); + break; + + case "Char": + Write((byte)ObjType.charType); + base.Write((char)obj); + break; + + case "String": + Write((byte)ObjType.stringType); + base.Write((string)obj); + break; + + case "Single": + Write((byte)ObjType.singleType); + Write((float)obj); + break; + + case "Double": + Write((byte)ObjType.doubleType); + Write((double)obj); + break; + + case "Decimal": + Write((byte)ObjType.decimalType); + Write((decimal)obj); + break; + + case "DateTime": + Write((byte)ObjType.dateTimeType); + Write((DateTime)obj); + break; + + case "Byte[]": + Write((byte)ObjType.byteArrayType); + base.Write((byte[])obj); + break; + + case "Char[]": + Write((byte)ObjType.charArrayType); + base.Write((char[])obj); + break; + + default: + Write((byte)ObjType.otherType); + BinaryFormatter b = new BinaryFormatter + { +// AssemblyFormat = FormatterAssemblyStyle.Simple, + TypeFormat = FormatterTypeStyle.TypesWhenNeeded + }; + b.Serialize(BaseStream, obj); + break; + } // switch + } // if obj==null + } // WriteObject + + /// Adds the SerializationWriter buffer to the SerializationInfo at the end of GetObjectData(). + public void AddToInfo(SerializationInfo info) + { + byte[] b = ((MemoryStream)BaseStream).ToArray(); + info.AddValue("X", b, typeof(byte[])); + } + + public void WriteRawBytes(byte[] b) + { + base.Write(b); + } + + public void WriteByteArray(byte[] b) + { + if (b == null) + { + Write(-1); + } + else + { + int len = b.Length; + Write(len); + if (len > 0) base.Write(b); + } + } + + public void WriteUtf8(string str) + { + WriteRawBytes(Encoding.UTF8.GetBytes(str)); + } + } +} diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index d1cf34629d..42d47cc1fb 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -1,100 +1,100 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace osu.Game.IO.Serialization.Converters -{ - /// - /// A type of that serializes a alongside - /// a lookup table for the types contained. The lookup table is used in deserialization to - /// reconstruct the objects with their original types. - /// - /// The type of objects contained in the this attribute is attached to. - public class TypedListConverter : JsonConverter - { - private readonly bool requiresTypeVersion; - - /// - /// Constructs a new . - /// - // ReSharper disable once UnusedMember.Global - public TypedListConverter() - { - } - - /// - /// Constructs a new . - /// - /// Whether the version of the type should be serialized. - // ReSharper disable once UnusedMember.Global (Used in Beatmap) - public TypedListConverter(bool requiresTypeVersion) - { - this.requiresTypeVersion = requiresTypeVersion; - } - - public override bool CanConvert(Type objectType) => objectType == typeof(List); - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var list = new List(); - - var obj = JObject.Load(reader); - var lookupTable = serializer.Deserialize>(obj["lookup_table"].CreateReader()); - - foreach (var tok in obj["items"]) - { - var itemReader = tok.CreateReader(); - - var typeName = lookupTable[(int)tok["type"]]; - var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); - serializer.Populate(itemReader, instance); - - list.Add(instance); - } - - return list; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var list = (List)value; - - var lookupTable = new List(); - var objects = new List(); - foreach (var item in list) - { - var type = item.GetType(); - var assemblyName = type.Assembly.GetName(); - - var typeString = $"{type.FullName}, {assemblyName.Name}"; - if (requiresTypeVersion) - typeString += $", {assemblyName.Version}"; - - int typeId = lookupTable.IndexOf(typeString); - if (typeId == -1) - { - lookupTable.Add(typeString); - typeId = lookupTable.Count - 1; - } - - var itemObject = JObject.FromObject(item, serializer); - itemObject.AddFirst(new JProperty("type", typeId)); - objects.Add(itemObject); - } - - writer.WriteStartObject(); - - writer.WritePropertyName("lookup_table"); - serializer.Serialize(writer, lookupTable); - - writer.WritePropertyName("items"); - serializer.Serialize(writer, objects); - - writer.WriteEndObject(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace osu.Game.IO.Serialization.Converters +{ + /// + /// A type of that serializes a alongside + /// a lookup table for the types contained. The lookup table is used in deserialization to + /// reconstruct the objects with their original types. + /// + /// The type of objects contained in the this attribute is attached to. + public class TypedListConverter : JsonConverter + { + private readonly bool requiresTypeVersion; + + /// + /// Constructs a new . + /// + // ReSharper disable once UnusedMember.Global + public TypedListConverter() + { + } + + /// + /// Constructs a new . + /// + /// Whether the version of the type should be serialized. + // ReSharper disable once UnusedMember.Global (Used in Beatmap) + public TypedListConverter(bool requiresTypeVersion) + { + this.requiresTypeVersion = requiresTypeVersion; + } + + public override bool CanConvert(Type objectType) => objectType == typeof(List); + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var list = new List(); + + var obj = JObject.Load(reader); + var lookupTable = serializer.Deserialize>(obj["lookup_table"].CreateReader()); + + foreach (var tok in obj["items"]) + { + var itemReader = tok.CreateReader(); + + var typeName = lookupTable[(int)tok["type"]]; + var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); + serializer.Populate(itemReader, instance); + + list.Add(instance); + } + + return list; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var list = (List)value; + + var lookupTable = new List(); + var objects = new List(); + foreach (var item in list) + { + var type = item.GetType(); + var assemblyName = type.Assembly.GetName(); + + var typeString = $"{type.FullName}, {assemblyName.Name}"; + if (requiresTypeVersion) + typeString += $", {assemblyName.Version}"; + + int typeId = lookupTable.IndexOf(typeString); + if (typeId == -1) + { + lookupTable.Add(typeString); + typeId = lookupTable.Count - 1; + } + + var itemObject = JObject.FromObject(item, serializer); + itemObject.AddFirst(new JProperty("type", typeId)); + objects.Add(itemObject); + } + + writer.WriteStartObject(); + + writer.WritePropertyName("lookup_table"); + serializer.Serialize(writer, lookupTable); + + writer.WritePropertyName("items"); + serializer.Serialize(writer, objects); + + writer.WriteEndObject(); + } + } +} diff --git a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs index 2cad4bc0fb..ba2158df28 100644 --- a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs +++ b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs @@ -1,38 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using OpenTK; - -namespace osu.Game.IO.Serialization.Converters -{ - /// - /// A type of that serializes only the X and Y coordinates of a . - /// - public class Vector2Converter : JsonConverter - { - public override bool CanConvert(Type objectType) => objectType == typeof(Vector2); - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var obj = JObject.Load(reader); - return new Vector2((float)obj["x"], (float)obj["y"]); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var vector = (Vector2)value; - - writer.WriteStartObject(); - - writer.WritePropertyName("x"); - writer.WriteValue(vector.X); - writer.WritePropertyName("y"); - writer.WriteValue(vector.Y); - - writer.WriteEndObject(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OpenTK; + +namespace osu.Game.IO.Serialization.Converters +{ + /// + /// A type of that serializes only the X and Y coordinates of a . + /// + public class Vector2Converter : JsonConverter + { + public override bool CanConvert(Type objectType) => objectType == typeof(Vector2); + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var obj = JObject.Load(reader); + return new Vector2((float)obj["x"], (float)obj["y"]); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var vector = (Vector2)value; + + writer.WriteStartObject(); + + writer.WritePropertyName("x"); + writer.WriteValue(vector.X); + writer.WritePropertyName("y"); + writer.WriteValue(vector.Y); + + writer.WriteEndObject(); + } + } +} diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index 9e3aaafc81..c9727725ef 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -1,37 +1,37 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; -using osu.Game.IO.Serialization.Converters; - -namespace osu.Game.IO.Serialization -{ - public interface IJsonSerializable - { - } - - public static class JsonSerializableExtensions - { - public static string Serialize(this IJsonSerializable obj) => JsonConvert.SerializeObject(obj, CreateGlobalSettings()); - - public static T Deserialize(this string objString) => JsonConvert.DeserializeObject(objString, CreateGlobalSettings()); - - public static void DeserializeInto(this string objString, T target) => JsonConvert.PopulateObject(objString, target, CreateGlobalSettings()); - - public static T DeepClone(this T obj) where T : IJsonSerializable => Deserialize(Serialize(obj)); - - /// - /// Creates the default that should be used for all s. - /// - /// - public static JsonSerializerSettings CreateGlobalSettings() => new JsonSerializerSettings - { - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - Formatting = Formatting.Indented, - ObjectCreationHandling = ObjectCreationHandling.Replace, - DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, - Converters = new JsonConverter[] { new Vector2Converter() }, - ContractResolver = new KeyContractResolver() - }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using osu.Game.IO.Serialization.Converters; + +namespace osu.Game.IO.Serialization +{ + public interface IJsonSerializable + { + } + + public static class JsonSerializableExtensions + { + public static string Serialize(this IJsonSerializable obj) => JsonConvert.SerializeObject(obj, CreateGlobalSettings()); + + public static T Deserialize(this string objString) => JsonConvert.DeserializeObject(objString, CreateGlobalSettings()); + + public static void DeserializeInto(this string objString, T target) => JsonConvert.PopulateObject(objString, target, CreateGlobalSettings()); + + public static T DeepClone(this T obj) where T : IJsonSerializable => Deserialize(Serialize(obj)); + + /// + /// Creates the default that should be used for all s. + /// + /// + public static JsonSerializerSettings CreateGlobalSettings() => new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + Formatting = Formatting.Indented, + ObjectCreationHandling = ObjectCreationHandling.Replace, + DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, + Converters = new JsonConverter[] { new Vector2Converter() }, + ContractResolver = new KeyContractResolver() + }; + } +} diff --git a/osu.Game/IO/Serialization/KeyContractResolver.cs b/osu.Game/IO/Serialization/KeyContractResolver.cs index a8d1313091..e2b470ab4a 100644 --- a/osu.Game/IO/Serialization/KeyContractResolver.cs +++ b/osu.Game/IO/Serialization/KeyContractResolver.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Humanizer; -using Newtonsoft.Json.Serialization; - -namespace osu.Game.IO.Serialization -{ - public class KeyContractResolver : DefaultContractResolver - { - protected override string ResolvePropertyName(string propertyName) - { - return propertyName.Underscore(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Humanizer; +using Newtonsoft.Json.Serialization; + +namespace osu.Game.IO.Serialization +{ + public class KeyContractResolver : DefaultContractResolver + { + protected override string ResolvePropertyName(string propertyName) + { + return propertyName.Underscore(); + } + } +} diff --git a/osu.Game/IPC/ArchiveImportIPCChannel.cs b/osu.Game/IPC/ArchiveImportIPCChannel.cs index 9d7bf17c77..6783b9712c 100644 --- a/osu.Game/IPC/ArchiveImportIPCChannel.cs +++ b/osu.Game/IPC/ArchiveImportIPCChannel.cs @@ -1,49 +1,49 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using osu.Framework.Platform; -using osu.Game.Database; - -namespace osu.Game.IPC -{ - public class ArchiveImportIPCChannel : IpcChannel - { - private readonly ICanAcceptFiles importer; - - public ArchiveImportIPCChannel(IIpcHost host, ICanAcceptFiles importer = null) - : base(host) - { - this.importer = importer; - MessageReceived += msg => - { - Debug.Assert(importer != null); - ImportAsync(msg.Path).ContinueWith(t => - { - if (t.Exception != null) throw t.Exception; - }, TaskContinuationOptions.OnlyOnFaulted); - }; - } - - public async Task ImportAsync(string path) - { - if (importer == null) - { - //we want to contact a remote osu! to handle the import. - await SendMessageAsync(new ArchiveImportMessage { Path = path }); - return; - } - - if (importer.HandledExtensions.Contains(Path.GetExtension(path))) - importer.Import(path); - } - } - - public class ArchiveImportMessage - { - public string Path; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Platform; +using osu.Game.Database; + +namespace osu.Game.IPC +{ + public class ArchiveImportIPCChannel : IpcChannel + { + private readonly ICanAcceptFiles importer; + + public ArchiveImportIPCChannel(IIpcHost host, ICanAcceptFiles importer = null) + : base(host) + { + this.importer = importer; + MessageReceived += msg => + { + Debug.Assert(importer != null); + ImportAsync(msg.Path).ContinueWith(t => + { + if (t.Exception != null) throw t.Exception; + }, TaskContinuationOptions.OnlyOnFaulted); + }; + } + + public async Task ImportAsync(string path) + { + if (importer == null) + { + //we want to contact a remote osu! to handle the import. + await SendMessageAsync(new ArchiveImportMessage { Path = path }); + return; + } + + if (importer.HandledExtensions.Contains(Path.GetExtension(path))) + importer.Import(path); + } + } + + public class ArchiveImportMessage + { + public string Path; + } +} diff --git a/osu.Game/IPC/ScoreIPCChannel.cs b/osu.Game/IPC/ScoreIPCChannel.cs index 7f93656549..a66b8ce1f3 100644 --- a/osu.Game/IPC/ScoreIPCChannel.cs +++ b/osu.Game/IPC/ScoreIPCChannel.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Diagnostics; -using System.Threading.Tasks; -using osu.Framework.Platform; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.IPC -{ - public class ScoreIPCChannel : IpcChannel - { - private readonly ScoreStore scores; - - public ScoreIPCChannel(IIpcHost host, ScoreStore scores = null) - : base(host) - { - this.scores = scores; - MessageReceived += msg => - { - Debug.Assert(scores != null); - ImportAsync(msg.Path).ContinueWith(t => - { - if (t.Exception != null) throw t.Exception; - }, TaskContinuationOptions.OnlyOnFaulted); - }; - } - - public async Task ImportAsync(string path) - { - if (scores == null) - { - //we want to contact a remote osu! to handle the import. - await SendMessageAsync(new ScoreImportMessage { Path = path }); - return; - } - - scores.ReadReplayFile(path); - } - } - - public class ScoreImportMessage - { - public string Path; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Diagnostics; +using System.Threading.Tasks; +using osu.Framework.Platform; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.IPC +{ + public class ScoreIPCChannel : IpcChannel + { + private readonly ScoreStore scores; + + public ScoreIPCChannel(IIpcHost host, ScoreStore scores = null) + : base(host) + { + this.scores = scores; + MessageReceived += msg => + { + Debug.Assert(scores != null); + ImportAsync(msg.Path).ContinueWith(t => + { + if (t.Exception != null) throw t.Exception; + }, TaskContinuationOptions.OnlyOnFaulted); + }; + } + + public async Task ImportAsync(string path) + { + if (scores == null) + { + //we want to contact a remote osu! to handle the import. + await SendMessageAsync(new ScoreImportMessage { Path = path }); + return; + } + + scores.ReadReplayFile(path); + } + } + + public class ScoreImportMessage + { + public string Path; + } +} diff --git a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs index d20adda8ed..7a230f32a0 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel.DataAnnotations.Schema; -using osu.Framework.Input.Bindings; -using osu.Game.Database; - -namespace osu.Game.Input.Bindings -{ - [Table("KeyBinding")] - public class DatabasedKeyBinding : KeyBinding, IHasPrimaryKey - { - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int ID { get; set; } - - public int? RulesetID { get; set; } - - public int? Variant { get; set; } - - [Column("Keys")] - public string KeysString - { - get { return KeyCombination.ToString(); } - private set { KeyCombination = value; } - } - - [Column("Action")] - public int IntAction - { - get { return (int)Action; } - set { Action = value; } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel.DataAnnotations.Schema; +using osu.Framework.Input.Bindings; +using osu.Game.Database; + +namespace osu.Game.Input.Bindings +{ + [Table("KeyBinding")] + public class DatabasedKeyBinding : KeyBinding, IHasPrimaryKey + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ID { get; set; } + + public int? RulesetID { get; set; } + + public int? Variant { get; set; } + + [Column("Keys")] + public string KeysString + { + get { return KeyCombination.ToString(); } + private set { KeyCombination = value; } + } + + [Column("Action")] + public int IntAction + { + get { return (int)Action; } + set { Action = value; } + } + } +} diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs index 2b53f77d62..85a8ac7aa3 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs @@ -1,66 +1,66 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets; -using System.Linq; - -namespace osu.Game.Input.Bindings -{ - /// - /// A KeyBindingInputManager with a database backing for custom overrides. - /// - /// The type of the custom action. - public class DatabasedKeyBindingContainer : KeyBindingContainer - where T : struct - { - private readonly RulesetInfo ruleset; - - private readonly int? variant; - - private KeyBindingStore store; - - public override IEnumerable DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0); - - /// - /// Create a new instance. - /// - /// A reference to identify the current . Used to lookup mappings. Null for global mappings. - /// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts. - /// Specify how to deal with multiple matches of s and s. - public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) - : base(simultaneousMode) - { - this.ruleset = ruleset; - this.variant = variant; - - if (ruleset != null && variant == null) - throw new InvalidOperationException($"{nameof(variant)} can not be null when a non-null {nameof(ruleset)} is provided."); - } - - [BackgroundDependencyLoader] - private void load(KeyBindingStore keyBindings) - { - store = keyBindings; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - store.KeyBindingChanged += ReloadMappings; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (store != null) - store.KeyBindingChanged -= ReloadMappings; - } - - protected override void ReloadMappings() => KeyBindings = store.Query(ruleset?.ID, variant).ToList(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets; +using System.Linq; + +namespace osu.Game.Input.Bindings +{ + /// + /// A KeyBindingInputManager with a database backing for custom overrides. + /// + /// The type of the custom action. + public class DatabasedKeyBindingContainer : KeyBindingContainer + where T : struct + { + private readonly RulesetInfo ruleset; + + private readonly int? variant; + + private KeyBindingStore store; + + public override IEnumerable DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0); + + /// + /// Create a new instance. + /// + /// A reference to identify the current . Used to lookup mappings. Null for global mappings. + /// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts. + /// Specify how to deal with multiple matches of s and s. + public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) + : base(simultaneousMode) + { + this.ruleset = ruleset; + this.variant = variant; + + if (ruleset != null && variant == null) + throw new InvalidOperationException($"{nameof(variant)} can not be null when a non-null {nameof(ruleset)} is provided."); + } + + [BackgroundDependencyLoader] + private void load(KeyBindingStore keyBindings) + { + store = keyBindings; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + store.KeyBindingChanged += ReloadMappings; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (store != null) + store.KeyBindingChanged -= ReloadMappings; + } + + protected override void ReloadMappings() => KeyBindings = store.Query(ruleset?.ID, variant).ToList(); + } +} diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 9e74a935ea..dd8f00f6cd 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -1,81 +1,81 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Input; -using osu.Framework.Input.Bindings; - -namespace osu.Game.Input.Bindings -{ - public class GlobalActionContainer : DatabasedKeyBindingContainer, IHandleGlobalInput - { - private readonly Drawable handler; - - public GlobalActionContainer(OsuGameBase game) - { - if (game is IKeyBindingHandler) - handler = game; - } - - public override IEnumerable DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings); - - public IEnumerable GlobalKeyBindings => new[] - { - new KeyBinding(InputKey.F8, GlobalAction.ToggleChat), - new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial), - new KeyBinding(InputKey.F12,GlobalAction.TakeScreenshot), - - new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings), - new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar), - new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), - new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume), - new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume), - new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume), - new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume), - new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), - }; - - public IEnumerable InGameKeyBindings => new[] - { - new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene), - new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry) - }; - - protected override IEnumerable KeyBindingInputQueue => - handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler); - } - - public enum GlobalAction - { - [Description("Toggle chat overlay")] - ToggleChat, - [Description("Toggle social overlay")] - ToggleSocial, - [Description("Reset input settings")] - ResetInputSettings, - [Description("Toggle toolbar")] - ToggleToolbar, - [Description("Toggle settings")] - ToggleSettings, - [Description("Toggle osu!direct")] - ToggleDirect, - [Description("Increase Volume")] - IncreaseVolume, - [Description("Decrease Volume")] - DecreaseVolume, - [Description("Toggle mute")] - ToggleMute, - - // In-Game Keybindings - [Description("Skip Cutscene")] - SkipCutscene, - [Description("Quick Retry (Hold)")] - QuickRetry, - - [Description("Take screenshot")] - TakeScreenshot - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; + +namespace osu.Game.Input.Bindings +{ + public class GlobalActionContainer : DatabasedKeyBindingContainer, IHandleGlobalInput + { + private readonly Drawable handler; + + public GlobalActionContainer(OsuGameBase game) + { + if (game is IKeyBindingHandler) + handler = game; + } + + public override IEnumerable DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings); + + public IEnumerable GlobalKeyBindings => new[] + { + new KeyBinding(InputKey.F8, GlobalAction.ToggleChat), + new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial), + new KeyBinding(InputKey.F12,GlobalAction.TakeScreenshot), + + new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings), + new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar), + new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), + new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume), + new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume), + new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume), + new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume), + new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), + }; + + public IEnumerable InGameKeyBindings => new[] + { + new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene), + new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry) + }; + + protected override IEnumerable KeyBindingInputQueue => + handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler); + } + + public enum GlobalAction + { + [Description("Toggle chat overlay")] + ToggleChat, + [Description("Toggle social overlay")] + ToggleSocial, + [Description("Reset input settings")] + ResetInputSettings, + [Description("Toggle toolbar")] + ToggleToolbar, + [Description("Toggle settings")] + ToggleSettings, + [Description("Toggle osu!direct")] + ToggleDirect, + [Description("Increase Volume")] + IncreaseVolume, + [Description("Decrease Volume")] + DecreaseVolume, + [Description("Toggle mute")] + ToggleMute, + + // In-Game Keybindings + [Description("Skip Cutscene")] + SkipCutscene, + [Description("Quick Retry (Hold)")] + QuickRetry, + + [Description("Take screenshot")] + TakeScreenshot + } +} diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs index c431af0219..5454dd0c9f 100644 --- a/osu.Game/Input/Handlers/ReplayInputHandler.cs +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -1,48 +1,48 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Input; -using osu.Framework.Input.Handlers; -using osu.Framework.Platform; -using OpenTK; - -namespace osu.Game.Input.Handlers -{ - public abstract class ReplayInputHandler : InputHandler - { - /// - /// A function that converts coordinates from gamefield to screen space. - /// - public Func GamefieldToScreenSpace { protected get; set; } - - /// - /// Update the current frame based on an incoming time value. - /// There are cases where we return a "must-use" time value that is different from the input. - /// This is to ensure accurate playback of replay data. - /// - /// The time which we should use for finding the current frame. - /// The usable time value. If null, we should not advance time as we do not have enough data. - public abstract double? SetFrameFromTime(double time); - - public override bool Initialize(GameHost host) => true; - - public override bool IsActive => true; - - public override int Priority => 0; - - public class ReplayState : InputState - where T : struct - { - public List PressedActions; - - public override InputState Clone() - { - var clone = (ReplayState)base.Clone(); - clone.PressedActions = new List(PressedActions); - return clone; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Input; +using osu.Framework.Input.Handlers; +using osu.Framework.Platform; +using OpenTK; + +namespace osu.Game.Input.Handlers +{ + public abstract class ReplayInputHandler : InputHandler + { + /// + /// A function that converts coordinates from gamefield to screen space. + /// + public Func GamefieldToScreenSpace { protected get; set; } + + /// + /// Update the current frame based on an incoming time value. + /// There are cases where we return a "must-use" time value that is different from the input. + /// This is to ensure accurate playback of replay data. + /// + /// The time which we should use for finding the current frame. + /// The usable time value. If null, we should not advance time as we do not have enough data. + public abstract double? SetFrameFromTime(double time); + + public override bool Initialize(GameHost host) => true; + + public override bool IsActive => true; + + public override int Priority => 0; + + public class ReplayState : InputState + where T : struct + { + public List PressedActions; + + public override InputState Clone() + { + var clone = (ReplayState)base.Clone(); + clone.PressedActions = new List(PressedActions); + return clone; + } + } + } +} diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 33cb0911a8..29fdabbbc2 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -1,86 +1,86 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Input.Bindings; -using osu.Framework.Platform; -using osu.Game.Database; -using osu.Game.Input.Bindings; -using osu.Game.Rulesets; - -namespace osu.Game.Input -{ - public class KeyBindingStore : DatabaseBackedStore - { - public event Action KeyBindingChanged; - - public KeyBindingStore(DatabaseContextFactory contextFactory, RulesetStore rulesets, Storage storage = null) - : base(contextFactory, storage) - { - using (ContextFactory.GetForWrite()) - { - foreach (var info in rulesets.AvailableRulesets) - { - var ruleset = info.CreateInstance(); - foreach (var variant in ruleset.AvailableVariants) - insertDefaults(ruleset.GetDefaultKeyBindings(variant), info.ID, variant); - } - } - } - - public void Register(KeyBindingContainer manager) => insertDefaults(manager.DefaultKeyBindings); - - private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) - { - using (var usage = ContextFactory.GetForWrite()) - { - // compare counts in database vs defaults - foreach (var group in defaults.GroupBy(k => k.Action)) - { - int count = Query(rulesetId, variant).Count(k => (int)k.Action == (int)group.Key); - int aimCount = group.Count(); - - if (aimCount <= count) - continue; - - foreach (var insertable in group.Skip(count).Take(aimCount - count)) - // insert any defaults which are missing. - usage.Context.DatabasedKeyBinding.Add(new DatabasedKeyBinding - { - KeyCombination = insertable.KeyCombination, - Action = insertable.Action, - RulesetID = rulesetId, - Variant = variant - }); - } - } - } - - /// - /// Retrieve s for a specified ruleset/variant content. - /// - /// The ruleset's internal ID. - /// An optional variant. - /// - public List Query(int? rulesetId = null, int? variant = null) => - ContextFactory.Get().DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); - - public void Update(KeyBinding keyBinding) - { - using (ContextFactory.GetForWrite()) - { - var dbKeyBinding = (DatabasedKeyBinding)keyBinding; - Refresh(ref dbKeyBinding); - - if (dbKeyBinding.KeyCombination.Equals(keyBinding.KeyCombination)) - return; - - dbKeyBinding.KeyCombination = keyBinding.KeyCombination; - } - - KeyBindingChanged?.Invoke(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Input.Bindings; +using osu.Framework.Platform; +using osu.Game.Database; +using osu.Game.Input.Bindings; +using osu.Game.Rulesets; + +namespace osu.Game.Input +{ + public class KeyBindingStore : DatabaseBackedStore + { + public event Action KeyBindingChanged; + + public KeyBindingStore(DatabaseContextFactory contextFactory, RulesetStore rulesets, Storage storage = null) + : base(contextFactory, storage) + { + using (ContextFactory.GetForWrite()) + { + foreach (var info in rulesets.AvailableRulesets) + { + var ruleset = info.CreateInstance(); + foreach (var variant in ruleset.AvailableVariants) + insertDefaults(ruleset.GetDefaultKeyBindings(variant), info.ID, variant); + } + } + } + + public void Register(KeyBindingContainer manager) => insertDefaults(manager.DefaultKeyBindings); + + private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) + { + using (var usage = ContextFactory.GetForWrite()) + { + // compare counts in database vs defaults + foreach (var group in defaults.GroupBy(k => k.Action)) + { + int count = Query(rulesetId, variant).Count(k => (int)k.Action == (int)group.Key); + int aimCount = group.Count(); + + if (aimCount <= count) + continue; + + foreach (var insertable in group.Skip(count).Take(aimCount - count)) + // insert any defaults which are missing. + usage.Context.DatabasedKeyBinding.Add(new DatabasedKeyBinding + { + KeyCombination = insertable.KeyCombination, + Action = insertable.Action, + RulesetID = rulesetId, + Variant = variant + }); + } + } + } + + /// + /// Retrieve s for a specified ruleset/variant content. + /// + /// The ruleset's internal ID. + /// An optional variant. + /// + public List Query(int? rulesetId = null, int? variant = null) => + ContextFactory.Get().DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); + + public void Update(KeyBinding keyBinding) + { + using (ContextFactory.GetForWrite()) + { + var dbKeyBinding = (DatabasedKeyBinding)keyBinding; + Refresh(ref dbKeyBinding); + + if (dbKeyBinding.KeyCombination.Equals(keyBinding.KeyCombination)) + return; + + dbKeyBinding.KeyCombination = keyBinding.KeyCombination; + } + + KeyBindingChanged?.Invoke(); + } + } +} diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs index 0820041643..c751530bf4 100644 --- a/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs @@ -1,293 +1,293 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20171019041408_InitialCreate")] - partial class InitialCreate - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash"); - - b.HasIndex("MetadataID"); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using osu.Game.Database; +using System; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20171019041408_InitialCreate")] + partial class InitialCreate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash"); + + b.HasIndex("MetadataID"); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.cs b/osu.Game/Migrations/20171019041408_InitialCreate.cs index 922aa85f18..9b6881f98c 100644 --- a/osu.Game/Migrations/20171019041408_InitialCreate.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.cs @@ -1,311 +1,311 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class InitialCreate : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "BeatmapDifficulty", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ApproachRate = table.Column(type: "REAL", nullable: false), - CircleSize = table.Column(type: "REAL", nullable: false), - DrainRate = table.Column(type: "REAL", nullable: false), - OverallDifficulty = table.Column(type: "REAL", nullable: false), - SliderMultiplier = table.Column(type: "REAL", nullable: false), - SliderTickRate = table.Column(type: "REAL", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapDifficulty", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "BeatmapMetadata", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Artist = table.Column(type: "TEXT", nullable: true), - ArtistUnicode = table.Column(type: "TEXT", nullable: true), - AudioFile = table.Column(type: "TEXT", nullable: true), - Author = table.Column(type: "TEXT", nullable: true), - BackgroundFile = table.Column(type: "TEXT", nullable: true), - PreviewTime = table.Column(type: "INTEGER", nullable: false), - Source = table.Column(type: "TEXT", nullable: true), - Tags = table.Column(type: "TEXT", nullable: true), - Title = table.Column(type: "TEXT", nullable: true), - TitleUnicode = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapMetadata", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "FileInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Hash = table.Column(type: "TEXT", nullable: true), - ReferenceCount = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_FileInfo", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "KeyBinding", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Action = table.Column(type: "INTEGER", nullable: false), - Keys = table.Column(type: "TEXT", nullable: true), - RulesetID = table.Column(type: "INTEGER", nullable: true), - Variant = table.Column(type: "INTEGER", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_KeyBinding", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "RulesetInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Available = table.Column(type: "INTEGER", nullable: false), - InstantiationInfo = table.Column(type: "TEXT", nullable: true), - Name = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_RulesetInfo", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "BeatmapSetInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - DeletePending = table.Column(type: "INTEGER", nullable: false), - Hash = table.Column(type: "TEXT", nullable: true), - MetadataID = table.Column(type: "INTEGER", nullable: true), - OnlineBeatmapSetID = table.Column(type: "INTEGER", nullable: true), - Protected = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapSetInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapSetInfo_BeatmapMetadata_MetadataID", - column: x => x.MetadataID, - principalTable: "BeatmapMetadata", - principalColumn: "ID", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "BeatmapInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - AudioLeadIn = table.Column(type: "INTEGER", nullable: false), - BaseDifficultyID = table.Column(type: "INTEGER", nullable: false), - BeatDivisor = table.Column(type: "INTEGER", nullable: false), - BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), - Countdown = table.Column(type: "INTEGER", nullable: false), - DistanceSpacing = table.Column(type: "REAL", nullable: false), - GridSize = table.Column(type: "INTEGER", nullable: false), - Hash = table.Column(type: "TEXT", nullable: true), - Hidden = table.Column(type: "INTEGER", nullable: false), - LetterboxInBreaks = table.Column(type: "INTEGER", nullable: false), - MD5Hash = table.Column(type: "TEXT", nullable: true), - MetadataID = table.Column(type: "INTEGER", nullable: true), - OnlineBeatmapID = table.Column(type: "INTEGER", nullable: true), - Path = table.Column(type: "TEXT", nullable: true), - RulesetID = table.Column(type: "INTEGER", nullable: false), - SpecialStyle = table.Column(type: "INTEGER", nullable: false), - StackLeniency = table.Column(type: "REAL", nullable: false), - StarDifficulty = table.Column(type: "REAL", nullable: false), - StoredBookmarks = table.Column(type: "TEXT", nullable: true), - TimelineZoom = table.Column(type: "REAL", nullable: false), - Version = table.Column(type: "TEXT", nullable: true), - WidescreenStoryboard = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapDifficulty_BaseDifficultyID", - column: x => x.BaseDifficultyID, - principalTable: "BeatmapDifficulty", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapSetInfo_BeatmapSetInfoID", - column: x => x.BeatmapSetInfoID, - principalTable: "BeatmapSetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapMetadata_MetadataID", - column: x => x.MetadataID, - principalTable: "BeatmapMetadata", - principalColumn: "ID", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_BeatmapInfo_RulesetInfo_RulesetID", - column: x => x.RulesetID, - principalTable: "RulesetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "BeatmapSetFileInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), - FileInfoID = table.Column(type: "INTEGER", nullable: false), - Filename = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapSetFileInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapSetFileInfo_BeatmapSetInfo_BeatmapSetInfoID", - column: x => x.BeatmapSetInfoID, - principalTable: "BeatmapSetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapSetFileInfo_FileInfo_FileInfoID", - column: x => x.FileInfoID, - principalTable: "FileInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_BaseDifficultyID", - table: "BeatmapInfo", - column: "BaseDifficultyID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_BeatmapSetInfoID", - table: "BeatmapInfo", - column: "BeatmapSetInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo", - column: "MD5Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MetadataID", - table: "BeatmapInfo", - column: "MetadataID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_RulesetID", - table: "BeatmapInfo", - column: "RulesetID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetFileInfo_BeatmapSetInfoID", - table: "BeatmapSetFileInfo", - column: "BeatmapSetInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetFileInfo_FileInfoID", - table: "BeatmapSetFileInfo", - column: "FileInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_DeletePending", - table: "BeatmapSetInfo", - column: "DeletePending"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_MetadataID", - table: "BeatmapSetInfo", - column: "MetadataID"); - - migrationBuilder.CreateIndex( - name: "IX_FileInfo_Hash", - table: "FileInfo", - column: "Hash", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_FileInfo_ReferenceCount", - table: "FileInfo", - column: "ReferenceCount"); - - migrationBuilder.CreateIndex( - name: "IX_KeyBinding_Action", - table: "KeyBinding", - column: "Action"); - - migrationBuilder.CreateIndex( - name: "IX_KeyBinding_Variant", - table: "KeyBinding", - column: "Variant"); - - migrationBuilder.CreateIndex( - name: "IX_RulesetInfo_Available", - table: "RulesetInfo", - column: "Available"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "BeatmapInfo"); - - migrationBuilder.DropTable( - name: "BeatmapSetFileInfo"); - - migrationBuilder.DropTable( - name: "KeyBinding"); - - migrationBuilder.DropTable( - name: "BeatmapDifficulty"); - - migrationBuilder.DropTable( - name: "RulesetInfo"); - - migrationBuilder.DropTable( - name: "BeatmapSetInfo"); - - migrationBuilder.DropTable( - name: "FileInfo"); - - migrationBuilder.DropTable( - name: "BeatmapMetadata"); - } - } -} +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class InitialCreate : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "BeatmapDifficulty", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ApproachRate = table.Column(type: "REAL", nullable: false), + CircleSize = table.Column(type: "REAL", nullable: false), + DrainRate = table.Column(type: "REAL", nullable: false), + OverallDifficulty = table.Column(type: "REAL", nullable: false), + SliderMultiplier = table.Column(type: "REAL", nullable: false), + SliderTickRate = table.Column(type: "REAL", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapDifficulty", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "BeatmapMetadata", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Artist = table.Column(type: "TEXT", nullable: true), + ArtistUnicode = table.Column(type: "TEXT", nullable: true), + AudioFile = table.Column(type: "TEXT", nullable: true), + Author = table.Column(type: "TEXT", nullable: true), + BackgroundFile = table.Column(type: "TEXT", nullable: true), + PreviewTime = table.Column(type: "INTEGER", nullable: false), + Source = table.Column(type: "TEXT", nullable: true), + Tags = table.Column(type: "TEXT", nullable: true), + Title = table.Column(type: "TEXT", nullable: true), + TitleUnicode = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapMetadata", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "FileInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Hash = table.Column(type: "TEXT", nullable: true), + ReferenceCount = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_FileInfo", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "KeyBinding", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Action = table.Column(type: "INTEGER", nullable: false), + Keys = table.Column(type: "TEXT", nullable: true), + RulesetID = table.Column(type: "INTEGER", nullable: true), + Variant = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_KeyBinding", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "RulesetInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Available = table.Column(type: "INTEGER", nullable: false), + InstantiationInfo = table.Column(type: "TEXT", nullable: true), + Name = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RulesetInfo", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "BeatmapSetInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + DeletePending = table.Column(type: "INTEGER", nullable: false), + Hash = table.Column(type: "TEXT", nullable: true), + MetadataID = table.Column(type: "INTEGER", nullable: true), + OnlineBeatmapSetID = table.Column(type: "INTEGER", nullable: true), + Protected = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapSetInfo", x => x.ID); + table.ForeignKey( + name: "FK_BeatmapSetInfo_BeatmapMetadata_MetadataID", + column: x => x.MetadataID, + principalTable: "BeatmapMetadata", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "BeatmapInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AudioLeadIn = table.Column(type: "INTEGER", nullable: false), + BaseDifficultyID = table.Column(type: "INTEGER", nullable: false), + BeatDivisor = table.Column(type: "INTEGER", nullable: false), + BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), + Countdown = table.Column(type: "INTEGER", nullable: false), + DistanceSpacing = table.Column(type: "REAL", nullable: false), + GridSize = table.Column(type: "INTEGER", nullable: false), + Hash = table.Column(type: "TEXT", nullable: true), + Hidden = table.Column(type: "INTEGER", nullable: false), + LetterboxInBreaks = table.Column(type: "INTEGER", nullable: false), + MD5Hash = table.Column(type: "TEXT", nullable: true), + MetadataID = table.Column(type: "INTEGER", nullable: true), + OnlineBeatmapID = table.Column(type: "INTEGER", nullable: true), + Path = table.Column(type: "TEXT", nullable: true), + RulesetID = table.Column(type: "INTEGER", nullable: false), + SpecialStyle = table.Column(type: "INTEGER", nullable: false), + StackLeniency = table.Column(type: "REAL", nullable: false), + StarDifficulty = table.Column(type: "REAL", nullable: false), + StoredBookmarks = table.Column(type: "TEXT", nullable: true), + TimelineZoom = table.Column(type: "REAL", nullable: false), + Version = table.Column(type: "TEXT", nullable: true), + WidescreenStoryboard = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapInfo", x => x.ID); + table.ForeignKey( + name: "FK_BeatmapInfo_BeatmapDifficulty_BaseDifficultyID", + column: x => x.BaseDifficultyID, + principalTable: "BeatmapDifficulty", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_BeatmapInfo_BeatmapSetInfo_BeatmapSetInfoID", + column: x => x.BeatmapSetInfoID, + principalTable: "BeatmapSetInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_BeatmapInfo_BeatmapMetadata_MetadataID", + column: x => x.MetadataID, + principalTable: "BeatmapMetadata", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_BeatmapInfo_RulesetInfo_RulesetID", + column: x => x.RulesetID, + principalTable: "RulesetInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "BeatmapSetFileInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), + FileInfoID = table.Column(type: "INTEGER", nullable: false), + Filename = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BeatmapSetFileInfo", x => x.ID); + table.ForeignKey( + name: "FK_BeatmapSetFileInfo_BeatmapSetInfo_BeatmapSetInfoID", + column: x => x.BeatmapSetInfoID, + principalTable: "BeatmapSetInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_BeatmapSetFileInfo_FileInfo_FileInfoID", + column: x => x.FileInfoID, + principalTable: "FileInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_BaseDifficultyID", + table: "BeatmapInfo", + column: "BaseDifficultyID"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_BeatmapSetInfoID", + table: "BeatmapInfo", + column: "BeatmapSetInfoID"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_Hash", + table: "BeatmapInfo", + column: "Hash"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_MD5Hash", + table: "BeatmapInfo", + column: "MD5Hash"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_MetadataID", + table: "BeatmapInfo", + column: "MetadataID"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_RulesetID", + table: "BeatmapInfo", + column: "RulesetID"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetFileInfo_BeatmapSetInfoID", + table: "BeatmapSetFileInfo", + column: "BeatmapSetInfoID"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetFileInfo_FileInfoID", + table: "BeatmapSetFileInfo", + column: "FileInfoID"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetInfo_DeletePending", + table: "BeatmapSetInfo", + column: "DeletePending"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetInfo_Hash", + table: "BeatmapSetInfo", + column: "Hash"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetInfo_MetadataID", + table: "BeatmapSetInfo", + column: "MetadataID"); + + migrationBuilder.CreateIndex( + name: "IX_FileInfo_Hash", + table: "FileInfo", + column: "Hash", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_FileInfo_ReferenceCount", + table: "FileInfo", + column: "ReferenceCount"); + + migrationBuilder.CreateIndex( + name: "IX_KeyBinding_Action", + table: "KeyBinding", + column: "Action"); + + migrationBuilder.CreateIndex( + name: "IX_KeyBinding_Variant", + table: "KeyBinding", + column: "Variant"); + + migrationBuilder.CreateIndex( + name: "IX_RulesetInfo_Available", + table: "RulesetInfo", + column: "Available"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "BeatmapInfo"); + + migrationBuilder.DropTable( + name: "BeatmapSetFileInfo"); + + migrationBuilder.DropTable( + name: "KeyBinding"); + + migrationBuilder.DropTable( + name: "BeatmapDifficulty"); + + migrationBuilder.DropTable( + name: "RulesetInfo"); + + migrationBuilder.DropTable( + name: "BeatmapSetInfo"); + + migrationBuilder.DropTable( + name: "FileInfo"); + + migrationBuilder.DropTable( + name: "BeatmapMetadata"); + } + } +} diff --git a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs index 478f27e82c..4cd234f2ef 100644 --- a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs +++ b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs @@ -1,299 +1,299 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20171025071459_AddMissingIndexRules")] - partial class AddMissingIndexRules - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using osu.Game.Database; +using System; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20171025071459_AddMissingIndexRules")] + partial class AddMissingIndexRules + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MD5Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs index ad8884a4bf..c9fc59c5a2 100644 --- a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs +++ b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs @@ -1,80 +1,80 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddMissingIndexRules : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo", - column: "Hash", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_OnlineBeatmapSetID", - table: "BeatmapSetInfo", - column: "OnlineBeatmapSetID", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo", - column: "Hash", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo", - column: "MD5Hash", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapSetInfo_OnlineBeatmapSetID", - table: "BeatmapSetInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo", - column: "MD5Hash"); - } - } -} +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddMissingIndexRules : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_BeatmapSetInfo_Hash", + table: "BeatmapSetInfo"); + + migrationBuilder.DropIndex( + name: "IX_BeatmapInfo_Hash", + table: "BeatmapInfo"); + + migrationBuilder.DropIndex( + name: "IX_BeatmapInfo_MD5Hash", + table: "BeatmapInfo"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetInfo_Hash", + table: "BeatmapSetInfo", + column: "Hash", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetInfo_OnlineBeatmapSetID", + table: "BeatmapSetInfo", + column: "OnlineBeatmapSetID", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_Hash", + table: "BeatmapInfo", + column: "Hash", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_MD5Hash", + table: "BeatmapInfo", + column: "MD5Hash", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_BeatmapSetInfo_Hash", + table: "BeatmapSetInfo"); + + migrationBuilder.DropIndex( + name: "IX_BeatmapSetInfo_OnlineBeatmapSetID", + table: "BeatmapSetInfo"); + + migrationBuilder.DropIndex( + name: "IX_BeatmapInfo_Hash", + table: "BeatmapInfo"); + + migrationBuilder.DropIndex( + name: "IX_BeatmapInfo_MD5Hash", + table: "BeatmapInfo"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapSetInfo_Hash", + table: "BeatmapSetInfo", + column: "Hash"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_Hash", + table: "BeatmapInfo", + column: "Hash"); + + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_MD5Hash", + table: "BeatmapInfo", + column: "MD5Hash"); + } + } +} diff --git a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs index b2f81a729a..006acf12cd 100644 --- a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs +++ b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs @@ -1,302 +1,302 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20171119065731_AddBeatmapOnlineIDUniqueConstraint")] - partial class AddBeatmapOnlineIDUniqueConstraint - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using osu.Game.Database; +using System; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20171119065731_AddBeatmapOnlineIDUniqueConstraint")] + partial class AddBeatmapOnlineIDUniqueConstraint + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MD5Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs index d3830ec0f5..66ebda785b 100644 --- a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs +++ b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs @@ -1,25 +1,25 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; - -namespace osu.Game.Migrations -{ - public partial class AddBeatmapOnlineIDUniqueConstraint : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_OnlineBeatmapID", - table: "BeatmapInfo", - column: "OnlineBeatmapID", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_OnlineBeatmapID", - table: "BeatmapInfo"); - } - } -} +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace osu.Game.Migrations +{ + public partial class AddBeatmapOnlineIDUniqueConstraint : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_BeatmapInfo_OnlineBeatmapID", + table: "BeatmapInfo", + column: "OnlineBeatmapID", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_BeatmapInfo_OnlineBeatmapID", + table: "BeatmapInfo"); + } + } +} diff --git a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs index 6023e88994..fc2496bc24 100644 --- a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs +++ b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs @@ -1,307 +1,307 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20171209034410_AddRulesetInfoShortName")] - partial class AddRulesetInfoShortName - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using osu.Game.Database; +using System; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20171209034410_AddRulesetInfoShortName")] + partial class AddRulesetInfoShortName + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MD5Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs index 94519c0b97..5227d1f8f9 100644 --- a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs +++ b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs @@ -1,35 +1,35 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; - -namespace osu.Game.Migrations -{ - public partial class AddRulesetInfoShortName : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "ShortName", - table: "RulesetInfo", - type: "TEXT", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_RulesetInfo_ShortName", - table: "RulesetInfo", - column: "ShortName", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_RulesetInfo_ShortName", - table: "RulesetInfo"); - - migrationBuilder.DropColumn( - name: "ShortName", - table: "RulesetInfo"); - } - } -} +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace osu.Game.Migrations +{ + public partial class AddRulesetInfoShortName : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ShortName", + table: "RulesetInfo", + type: "TEXT", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_RulesetInfo_ShortName", + table: "RulesetInfo", + column: "ShortName", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_RulesetInfo_ShortName", + table: "RulesetInfo"); + + migrationBuilder.DropColumn( + name: "ShortName", + table: "RulesetInfo"); + } + } +} diff --git a/osu.Game/Migrations/20180125143340_Settings.Designer.cs b/osu.Game/Migrations/20180125143340_Settings.Designer.cs index 8e045abc6f..4bb599eec1 100644 --- a/osu.Game/Migrations/20180125143340_Settings.Designer.cs +++ b/osu.Game/Migrations/20180125143340_Settings.Designer.cs @@ -1,329 +1,329 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180125143340_Settings")] - partial class Settings - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using osu.Game.Database; +using System; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20180125143340_Settings")] + partial class Settings + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MD5Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20180125143340_Settings.cs b/osu.Game/Migrations/20180125143340_Settings.cs index 86a6b2dc5e..20701d672a 100644 --- a/osu.Game/Migrations/20180125143340_Settings.cs +++ b/osu.Game/Migrations/20180125143340_Settings.cs @@ -1,57 +1,57 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; - -namespace osu.Game.Migrations -{ - public partial class Settings : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_KeyBinding_Variant", - table: "KeyBinding"); - - migrationBuilder.CreateTable( - name: "Settings", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Key = table.Column(type: "INTEGER", nullable: false), - RulesetID = table.Column(type: "INTEGER", nullable: true), - Value = table.Column(type: "TEXT", nullable: true), - Variant = table.Column(type: "INTEGER", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Settings", x => x.ID); - }); - - migrationBuilder.CreateIndex( - name: "IX_KeyBinding_RulesetID_Variant", - table: "KeyBinding", - columns: new[] { "RulesetID", "Variant" }); - - migrationBuilder.CreateIndex( - name: "IX_Settings_RulesetID_Variant", - table: "Settings", - columns: new[] { "RulesetID", "Variant" }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Settings"); - - migrationBuilder.DropIndex( - name: "IX_KeyBinding_RulesetID_Variant", - table: "KeyBinding"); - - migrationBuilder.CreateIndex( - name: "IX_KeyBinding_Variant", - table: "KeyBinding", - column: "Variant"); - } - } -} +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace osu.Game.Migrations +{ + public partial class Settings : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_KeyBinding_Variant", + table: "KeyBinding"); + + migrationBuilder.CreateTable( + name: "Settings", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Key = table.Column(type: "INTEGER", nullable: false), + RulesetID = table.Column(type: "INTEGER", nullable: true), + Value = table.Column(type: "TEXT", nullable: true), + Variant = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Settings", x => x.ID); + }); + + migrationBuilder.CreateIndex( + name: "IX_KeyBinding_RulesetID_Variant", + table: "KeyBinding", + columns: new[] { "RulesetID", "Variant" }); + + migrationBuilder.CreateIndex( + name: "IX_Settings_RulesetID_Variant", + table: "Settings", + columns: new[] { "RulesetID", "Variant" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Settings"); + + migrationBuilder.DropIndex( + name: "IX_KeyBinding_RulesetID_Variant", + table: "KeyBinding"); + + migrationBuilder.CreateIndex( + name: "IX_KeyBinding_Variant", + table: "KeyBinding", + column: "Variant"); + } + } +} diff --git a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs index fc1f2fff55..374830ad93 100644 --- a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs +++ b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs @@ -1,25 +1,25 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Infrastructure; -using osu.Game.Database; -using osu.Game.Input.Bindings; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180131154205_AddMuteBinding")] - public partial class AddMuteBinding : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql($"UPDATE KeyBinding SET Action = Action + 1 WHERE RulesetID IS NULL AND Variant IS NULL AND Action >= {(int)GlobalAction.ToggleMute}"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql($"DELETE FROM KeyBinding WHERE RulesetID IS NULL AND Variant IS NULL AND Action = {(int)GlobalAction.ToggleMute}"); - migrationBuilder.Sql($"UPDATE KeyBinding SET Action = Action - 1 WHERE RulesetID IS NULL AND Variant IS NULL AND Action > {(int)GlobalAction.ToggleMute}"); - } - } -} +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; +using osu.Game.Database; +using osu.Game.Input.Bindings; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20180131154205_AddMuteBinding")] + public partial class AddMuteBinding : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql($"UPDATE KeyBinding SET Action = Action + 1 WHERE RulesetID IS NULL AND Variant IS NULL AND Action >= {(int)GlobalAction.ToggleMute}"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql($"DELETE FROM KeyBinding WHERE RulesetID IS NULL AND Variant IS NULL AND Action = {(int)GlobalAction.ToggleMute}"); + migrationBuilder.Sql($"UPDATE KeyBinding SET Action = Action - 1 WHERE RulesetID IS NULL AND Variant IS NULL AND Action > {(int)GlobalAction.ToggleMute}"); + } + } +} diff --git a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs index 83b8d6cf8a..cdc4ef2e66 100644 --- a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs +++ b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs @@ -1,379 +1,379 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180219060912_AddSkins")] - partial class AddSkins - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using osu.Game.Database; +using System; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20180219060912_AddSkins")] + partial class AddSkins + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MD5Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20180219060912_AddSkins.cs b/osu.Game/Migrations/20180219060912_AddSkins.cs index 741fcf4079..6cce4354d9 100644 --- a/osu.Game/Migrations/20180219060912_AddSkins.cs +++ b/osu.Game/Migrations/20180219060912_AddSkins.cs @@ -1,73 +1,73 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; - -namespace osu.Game.Migrations -{ - public partial class AddSkins : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "SkinInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Creator = table.Column(type: "TEXT", nullable: true), - DeletePending = table.Column(type: "INTEGER", nullable: false), - Name = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_SkinInfo", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "SkinFileInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - FileInfoID = table.Column(type: "INTEGER", nullable: false), - Filename = table.Column(type: "TEXT", nullable: false), - SkinInfoID = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_SkinFileInfo", x => x.ID); - table.ForeignKey( - name: "FK_SkinFileInfo_FileInfo_FileInfoID", - column: x => x.FileInfoID, - principalTable: "FileInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_SkinFileInfo_SkinInfo_SkinInfoID", - column: x => x.SkinInfoID, - principalTable: "SkinInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_SkinFileInfo_FileInfoID", - table: "SkinFileInfo", - column: "FileInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_SkinFileInfo_SkinInfoID", - table: "SkinFileInfo", - column: "SkinInfoID"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "SkinFileInfo"); - - migrationBuilder.DropTable( - name: "SkinInfo"); - } - } -} +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace osu.Game.Migrations +{ + public partial class AddSkins : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SkinInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Creator = table.Column(type: "TEXT", nullable: true), + DeletePending = table.Column(type: "INTEGER", nullable: false), + Name = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_SkinInfo", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "SkinFileInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + FileInfoID = table.Column(type: "INTEGER", nullable: false), + Filename = table.Column(type: "TEXT", nullable: false), + SkinInfoID = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SkinFileInfo", x => x.ID); + table.ForeignKey( + name: "FK_SkinFileInfo_FileInfo_FileInfoID", + column: x => x.FileInfoID, + principalTable: "FileInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_SkinFileInfo_SkinInfo_SkinInfoID", + column: x => x.SkinInfoID, + principalTable: "SkinInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_SkinFileInfo_FileInfoID", + table: "SkinFileInfo", + column: "FileInfoID"); + + migrationBuilder.CreateIndex( + name: "IX_SkinFileInfo_SkinInfoID", + table: "SkinFileInfo", + column: "SkinInfoID"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SkinFileInfo"); + + migrationBuilder.DropTable( + name: "SkinInfo"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index b16b9fdefa..2abbe7785f 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -1,374 +1,374 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - partial class OsuDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + partial class OsuDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MD5Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 946d13c02a..8c5fc58878 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -1,334 +1,334 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Logging; -using osu.Game.Configuration; -using osu.Game.Online.API.Requests; -using osu.Game.Users; - -namespace osu.Game.Online.API -{ - public class APIAccess : Component, IAPIProvider - { - private readonly OsuConfigManager config; - private readonly OAuth authentication; - - public string Endpoint = @"https://osu.ppy.sh"; - private const string client_id = @"5"; - private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; - - private ConcurrentQueue queue = new ConcurrentQueue(); - - /// - /// The username/email provided by the user when initiating a login. - /// - public string ProvidedUsername { get; private set; } - - private string password; - - public Bindable LocalUser { get; } = new Bindable(createGuestUser()); - - protected bool HasLogin => authentication.Token.Value != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); - - private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); - - private readonly Logger log; - - public APIAccess(OsuConfigManager config) - { - this.config = config; - - authentication = new OAuth(client_id, client_secret, Endpoint); - log = Logger.GetLogger(LoggingTarget.Network); - - ProvidedUsername = config.Get(OsuSetting.Username); - - authentication.TokenString = config.Get(OsuSetting.Token); - authentication.Token.ValueChanged += onTokenChanged; - - Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); - } - - private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty); - - private readonly List components = new List(); - - internal new void Schedule(Action action) => base.Schedule(action); - - public void Register(IOnlineComponent component) - { - Scheduler.Add(delegate - { - components.Add(component); - component.APIStateChanged(this, state); - }); - } - - public void Unregister(IOnlineComponent component) - { - Scheduler.Add(delegate - { - components.Remove(component); - }); - } - - public string AccessToken => authentication.RequestAccessToken(); - - /// - /// Number of consecutive requests which failed due to network issues. - /// - private int failureCount; - - private void run() - { - while (!cancellationToken.IsCancellationRequested) - { - switch (State) - { - case APIState.Failing: - //todo: replace this with a ping request. - log.Add(@"In a failing state, waiting a bit before we try again..."); - Thread.Sleep(5000); - if (queue.Count == 0) - { - log.Add(@"Queueing a ping request"); - Queue(new ListChannelsRequest { Timeout = 5000 }); - } - break; - case APIState.Offline: - case APIState.Connecting: - //work to restore a connection... - if (!HasLogin) - { - State = APIState.Offline; - Thread.Sleep(50); - continue; - } - - State = APIState.Connecting; - - // save the username at this point, if the user requested for it to be. - config.Set(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty); - - if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(ProvidedUsername, password)) - { - //todo: this fails even on network-related issues. we should probably handle those differently. - //NotificationOverlay.ShowMessage("Login failed!"); - log.Add(@"Login failed!"); - password = null; - authentication.Clear(); - continue; - } - - var userReq = new GetUserRequest(); - userReq.Success += u => - { - LocalUser.Value = u; - failureCount = 0; - - //we're connected! - State = APIState.Online; - }; - - if (!handleRequest(userReq)) - { - Thread.Sleep(500); - continue; - } - - // The Success callback event is fired on the main thread, so we should wait for that to run before proceeding. - // Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests - // before actually going online. - while (State != APIState.Online) - Thread.Sleep(500); - - break; - } - - //hard bail if we can't get a valid access token. - if (authentication.RequestAccessToken() == null) - { - Logout(false); - State = APIState.Offline; - continue; - } - - //process the request queue. - APIRequest req; - while (queue.TryPeek(out req)) - { - if (handleRequest(req)) - { - //we have succeeded, so let's unqueue. - queue.TryDequeue(out req); - } - } - - Thread.Sleep(1); - } - } - - public void Login(string username, string password) - { - Debug.Assert(State == APIState.Offline); - - ProvidedUsername = username; - this.password = password; - } - - /// - /// Handle a single API request. - /// - /// The request. - /// true if we should remove this request from the queue. - private bool handleRequest(APIRequest req) - { - try - { - Logger.Log($@"Performing request {req}", LoggingTarget.Network); - req.Perform(this); - - //we could still be in initialisation, at which point we don't want to say we're Online yet. - if (IsLoggedIn) - State = APIState.Online; - - failureCount = 0; - return true; - } - catch (WebException we) - { - HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); - - switch (statusCode) - { - case HttpStatusCode.Unauthorized: - Logout(false); - return true; - case HttpStatusCode.RequestTimeout: - failureCount++; - log.Add($@"API failure count is now {failureCount}"); - - if (failureCount < 3) - //we might try again at an api level. - return false; - - State = APIState.Failing; - flushQueue(); - return true; - } - - req.Fail(we); - return true; - } - catch (Exception e) - { - if (e is TimeoutException) - log.Add(@"API level timeout exception was hit"); - - req.Fail(e); - return true; - } - } - - private APIState state; - public APIState State - { - get { return state; } - private set - { - APIState oldState = state; - APIState newState = value; - - state = value; - - if (oldState != newState) - { - log.Add($@"We just went {newState}!"); - Scheduler.Add(delegate - { - components.ForEach(c => c.APIStateChanged(this, newState)); - OnStateChange?.Invoke(oldState, newState); - }); - } - } - } - - public bool IsLoggedIn => LocalUser.Value.Id > 1; - - public void Queue(APIRequest request) => queue.Enqueue(request); - - public event StateChangeDelegate OnStateChange; - - public delegate void StateChangeDelegate(APIState oldState, APIState newState); - - private void flushQueue(bool failOldRequests = true) - { - var oldQueue = queue; - - //flush the queue. - queue = new ConcurrentQueue(); - - if (failOldRequests) - { - APIRequest req; - while (oldQueue.TryDequeue(out req)) - req.Fail(new WebException(@"Disconnected from server")); - } - } - - public void Logout(bool clearUsername = true) - { - flushQueue(); - if (clearUsername) ProvidedUsername = null; - password = null; - authentication.Clear(); - LocalUser.Value = createGuestUser(); - } - - private static User createGuestUser() => new User - { - Username = @"Guest", - Id = 1, - }; - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - flushQueue(); - cancellationToken.Cancel(); - } - } - - public enum APIState - { - /// - /// We cannot login (not enough credentials). - /// - Offline, - - /// - /// We are having connectivity issues. - /// - Failing, - - /// - /// We are in the process of (re-)connecting. - /// - Connecting, - - /// - /// We are online. - /// - Online - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Logging; +using osu.Game.Configuration; +using osu.Game.Online.API.Requests; +using osu.Game.Users; + +namespace osu.Game.Online.API +{ + public class APIAccess : Component, IAPIProvider + { + private readonly OsuConfigManager config; + private readonly OAuth authentication; + + public string Endpoint = @"https://osu.ppy.sh"; + private const string client_id = @"5"; + private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; + + private ConcurrentQueue queue = new ConcurrentQueue(); + + /// + /// The username/email provided by the user when initiating a login. + /// + public string ProvidedUsername { get; private set; } + + private string password; + + public Bindable LocalUser { get; } = new Bindable(createGuestUser()); + + protected bool HasLogin => authentication.Token.Value != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password); + + private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); + + private readonly Logger log; + + public APIAccess(OsuConfigManager config) + { + this.config = config; + + authentication = new OAuth(client_id, client_secret, Endpoint); + log = Logger.GetLogger(LoggingTarget.Network); + + ProvidedUsername = config.Get(OsuSetting.Username); + + authentication.TokenString = config.Get(OsuSetting.Token); + authentication.Token.ValueChanged += onTokenChanged; + + Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + } + + private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty); + + private readonly List components = new List(); + + internal new void Schedule(Action action) => base.Schedule(action); + + public void Register(IOnlineComponent component) + { + Scheduler.Add(delegate + { + components.Add(component); + component.APIStateChanged(this, state); + }); + } + + public void Unregister(IOnlineComponent component) + { + Scheduler.Add(delegate + { + components.Remove(component); + }); + } + + public string AccessToken => authentication.RequestAccessToken(); + + /// + /// Number of consecutive requests which failed due to network issues. + /// + private int failureCount; + + private void run() + { + while (!cancellationToken.IsCancellationRequested) + { + switch (State) + { + case APIState.Failing: + //todo: replace this with a ping request. + log.Add(@"In a failing state, waiting a bit before we try again..."); + Thread.Sleep(5000); + if (queue.Count == 0) + { + log.Add(@"Queueing a ping request"); + Queue(new ListChannelsRequest { Timeout = 5000 }); + } + break; + case APIState.Offline: + case APIState.Connecting: + //work to restore a connection... + if (!HasLogin) + { + State = APIState.Offline; + Thread.Sleep(50); + continue; + } + + State = APIState.Connecting; + + // save the username at this point, if the user requested for it to be. + config.Set(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty); + + if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(ProvidedUsername, password)) + { + //todo: this fails even on network-related issues. we should probably handle those differently. + //NotificationOverlay.ShowMessage("Login failed!"); + log.Add(@"Login failed!"); + password = null; + authentication.Clear(); + continue; + } + + var userReq = new GetUserRequest(); + userReq.Success += u => + { + LocalUser.Value = u; + failureCount = 0; + + //we're connected! + State = APIState.Online; + }; + + if (!handleRequest(userReq)) + { + Thread.Sleep(500); + continue; + } + + // The Success callback event is fired on the main thread, so we should wait for that to run before proceeding. + // Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests + // before actually going online. + while (State != APIState.Online) + Thread.Sleep(500); + + break; + } + + //hard bail if we can't get a valid access token. + if (authentication.RequestAccessToken() == null) + { + Logout(false); + State = APIState.Offline; + continue; + } + + //process the request queue. + APIRequest req; + while (queue.TryPeek(out req)) + { + if (handleRequest(req)) + { + //we have succeeded, so let's unqueue. + queue.TryDequeue(out req); + } + } + + Thread.Sleep(1); + } + } + + public void Login(string username, string password) + { + Debug.Assert(State == APIState.Offline); + + ProvidedUsername = username; + this.password = password; + } + + /// + /// Handle a single API request. + /// + /// The request. + /// true if we should remove this request from the queue. + private bool handleRequest(APIRequest req) + { + try + { + Logger.Log($@"Performing request {req}", LoggingTarget.Network); + req.Perform(this); + + //we could still be in initialisation, at which point we don't want to say we're Online yet. + if (IsLoggedIn) + State = APIState.Online; + + failureCount = 0; + return true; + } + catch (WebException we) + { + HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); + + switch (statusCode) + { + case HttpStatusCode.Unauthorized: + Logout(false); + return true; + case HttpStatusCode.RequestTimeout: + failureCount++; + log.Add($@"API failure count is now {failureCount}"); + + if (failureCount < 3) + //we might try again at an api level. + return false; + + State = APIState.Failing; + flushQueue(); + return true; + } + + req.Fail(we); + return true; + } + catch (Exception e) + { + if (e is TimeoutException) + log.Add(@"API level timeout exception was hit"); + + req.Fail(e); + return true; + } + } + + private APIState state; + public APIState State + { + get { return state; } + private set + { + APIState oldState = state; + APIState newState = value; + + state = value; + + if (oldState != newState) + { + log.Add($@"We just went {newState}!"); + Scheduler.Add(delegate + { + components.ForEach(c => c.APIStateChanged(this, newState)); + OnStateChange?.Invoke(oldState, newState); + }); + } + } + } + + public bool IsLoggedIn => LocalUser.Value.Id > 1; + + public void Queue(APIRequest request) => queue.Enqueue(request); + + public event StateChangeDelegate OnStateChange; + + public delegate void StateChangeDelegate(APIState oldState, APIState newState); + + private void flushQueue(bool failOldRequests = true) + { + var oldQueue = queue; + + //flush the queue. + queue = new ConcurrentQueue(); + + if (failOldRequests) + { + APIRequest req; + while (oldQueue.TryDequeue(out req)) + req.Fail(new WebException(@"Disconnected from server")); + } + } + + public void Logout(bool clearUsername = true) + { + flushQueue(); + if (clearUsername) ProvidedUsername = null; + password = null; + authentication.Clear(); + LocalUser.Value = createGuestUser(); + } + + private static User createGuestUser() => new User + { + Username = @"Guest", + Id = 1, + }; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + flushQueue(); + cancellationToken.Cancel(); + } + } + + public enum APIState + { + /// + /// We cannot login (not enough credentials). + /// + Offline, + + /// + /// We are having connectivity issues. + /// + Failing, + + /// + /// We are in the process of (re-)connecting. + /// + Connecting, + + /// + /// We are online. + /// + Online + } +} diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index 0a5210723d..86558faf3b 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.IO.Network; - -namespace osu.Game.Online.API -{ - public abstract class APIDownloadRequest : APIRequest - { - protected override WebRequest CreateWebRequest() - { - var request = new WebRequest(Uri); - request.DownloadProgress += request_Progress; - return request; - } - - private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total)); - - protected APIDownloadRequest() - { - base.Success += onSuccess; - } - - private void onSuccess() - { - Success?.Invoke(WebRequest.ResponseData); - } - - public event APIProgressHandler Progress; - - public new event APISuccessHandler Success; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API +{ + public abstract class APIDownloadRequest : APIRequest + { + protected override WebRequest CreateWebRequest() + { + var request = new WebRequest(Uri); + request.DownloadProgress += request_Progress; + return request; + } + + private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total)); + + protected APIDownloadRequest() + { + base.Success += onSuccess; + } + + private void onSuccess() + { + Success?.Invoke(WebRequest.ResponseData); + } + + public event APIProgressHandler Progress; + + public new event APISuccessHandler Success; + } +} diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 4b05df661b..9af142b9e8 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -1,121 +1,121 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.IO.Network; - -namespace osu.Game.Online.API -{ - /// - /// An API request with a well-defined response type. - /// - /// Type of the response (used for deserialisation). - public abstract class APIRequest : APIRequest - { - protected override WebRequest CreateWebRequest() => new JsonWebRequest(Uri); - - protected APIRequest() - { - base.Success += onSuccess; - } - - private void onSuccess() - { - Success?.Invoke(((JsonWebRequest)WebRequest).ResponseObject); - } - - public new event APISuccessHandler Success; - } - - /// - /// AN API request with no specified response type. - /// - public abstract class APIRequest - { - /// - /// The maximum amount of time before this request will fail. - /// - public int Timeout = WebRequest.DEFAULT_TIMEOUT; - - protected virtual string Target => string.Empty; - - protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri); - - protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}"; - - private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds); - - public bool ExceededTimeout => remainingTime == 0; - - private DateTimeOffset? startTime; - - protected APIAccess API; - protected WebRequest WebRequest; - - public event APISuccessHandler Success; - public event APIFailureHandler Failure; - - private bool cancelled; - - private Action pendingFailure; - - public void Perform(APIAccess api) - { - API = api; - - if (checkAndProcessFailure()) - return; - - if (startTime == null) - startTime = DateTimeOffset.UtcNow; - - if (remainingTime <= 0) - throw new TimeoutException(@"API request timeout hit"); - - WebRequest = CreateWebRequest(); - WebRequest.AllowRetryOnTimeout = false; - WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}"); - - if (checkAndProcessFailure()) - return; - - if (!WebRequest.Aborted) //could have been aborted by a Cancel() call - WebRequest.Perform(); - - if (checkAndProcessFailure()) - return; - - api.Schedule(delegate { Success?.Invoke(); }); - } - - public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); - - public void Fail(Exception e) - { - cancelled = true; - - WebRequest?.Abort(); - - pendingFailure = () => Failure?.Invoke(e); - checkAndProcessFailure(); - } - - /// - /// Checked for cancellation or error. Also queues up the Failed event if we can. - /// - /// Whether we are in a failed or cancelled state. - private bool checkAndProcessFailure() - { - if (API == null || pendingFailure == null) return cancelled; - - API.Schedule(pendingFailure); - pendingFailure = null; - return true; - } - } - - public delegate void APIFailureHandler(Exception e); - public delegate void APISuccessHandler(); - public delegate void APIProgressHandler(long current, long total); - public delegate void APISuccessHandler(T content); -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API +{ + /// + /// An API request with a well-defined response type. + /// + /// Type of the response (used for deserialisation). + public abstract class APIRequest : APIRequest + { + protected override WebRequest CreateWebRequest() => new JsonWebRequest(Uri); + + protected APIRequest() + { + base.Success += onSuccess; + } + + private void onSuccess() + { + Success?.Invoke(((JsonWebRequest)WebRequest).ResponseObject); + } + + public new event APISuccessHandler Success; + } + + /// + /// AN API request with no specified response type. + /// + public abstract class APIRequest + { + /// + /// The maximum amount of time before this request will fail. + /// + public int Timeout = WebRequest.DEFAULT_TIMEOUT; + + protected virtual string Target => string.Empty; + + protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri); + + protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}"; + + private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds); + + public bool ExceededTimeout => remainingTime == 0; + + private DateTimeOffset? startTime; + + protected APIAccess API; + protected WebRequest WebRequest; + + public event APISuccessHandler Success; + public event APIFailureHandler Failure; + + private bool cancelled; + + private Action pendingFailure; + + public void Perform(APIAccess api) + { + API = api; + + if (checkAndProcessFailure()) + return; + + if (startTime == null) + startTime = DateTimeOffset.UtcNow; + + if (remainingTime <= 0) + throw new TimeoutException(@"API request timeout hit"); + + WebRequest = CreateWebRequest(); + WebRequest.AllowRetryOnTimeout = false; + WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}"); + + if (checkAndProcessFailure()) + return; + + if (!WebRequest.Aborted) //could have been aborted by a Cancel() call + WebRequest.Perform(); + + if (checkAndProcessFailure()) + return; + + api.Schedule(delegate { Success?.Invoke(); }); + } + + public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); + + public void Fail(Exception e) + { + cancelled = true; + + WebRequest?.Abort(); + + pendingFailure = () => Failure?.Invoke(e); + checkAndProcessFailure(); + } + + /// + /// Checked for cancellation or error. Also queues up the Failed event if we can. + /// + /// Whether we are in a failed or cancelled state. + private bool checkAndProcessFailure() + { + if (API == null || pendingFailure == null) return cancelled; + + API.Schedule(pendingFailure); + pendingFailure = null; + return true; + } + } + + public delegate void APIFailureHandler(Exception e); + public delegate void APISuccessHandler(); + public delegate void APIProgressHandler(long current, long total); + public delegate void APISuccessHandler(T content); +} diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index fc0dc0ef8b..f99f184d5c 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Game.Users; - -namespace osu.Game.Online.API -{ - public class DummyAPIAccess : IAPIProvider - { - public Bindable LocalUser { get; } = new Bindable(new User - { - Username = @"Dummy", - Id = 1, - }); - - public bool IsLoggedIn => true; - - public void Update() - { - } - - public virtual void Queue(APIRequest request) - { - } - - public void Register(IOnlineComponent component) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Game.Users; + +namespace osu.Game.Online.API +{ + public class DummyAPIAccess : IAPIProvider + { + public Bindable LocalUser { get; } = new Bindable(new User + { + Username = @"Dummy", + Id = 1, + }); + + public bool IsLoggedIn => true; + + public void Update() + { + } + + public virtual void Queue(APIRequest request) + { + } + + public void Register(IOnlineComponent component) + { + } + } +} diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 4119691c85..a3fc9a699c 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Game.Users; - -namespace osu.Game.Online.API -{ - public interface IAPIProvider - { - /// - /// The local user. - /// - Bindable LocalUser { get; } - - /// - /// Returns whether the local user is logged in. - /// - bool IsLoggedIn { get; } - - /// - /// Queue a new request. - /// - /// The request to perform. - void Queue(APIRequest request); - - /// - /// Register a component to receive state changes. - /// - /// The component to register. - void Register(IOnlineComponent component); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Game.Users; + +namespace osu.Game.Online.API +{ + public interface IAPIProvider + { + /// + /// The local user. + /// + Bindable LocalUser { get; } + + /// + /// Returns whether the local user is logged in. + /// + bool IsLoggedIn { get; } + + /// + /// Queue a new request. + /// + /// The request to perform. + void Queue(APIRequest request); + + /// + /// Register a component to receive state changes. + /// + /// The component to register. + void Register(IOnlineComponent component); + } +} diff --git a/osu.Game/Online/API/IOnlineComponent.cs b/osu.Game/Online/API/IOnlineComponent.cs index a3a49b42ae..5ca6143428 100644 --- a/osu.Game/Online/API/IOnlineComponent.cs +++ b/osu.Game/Online/API/IOnlineComponent.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Online.API -{ - public interface IOnlineComponent - { - void APIStateChanged(APIAccess api, APIState state); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Online.API +{ + public interface IOnlineComponent + { + void APIStateChanged(APIAccess api, APIState state); + } +} diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs index af01fc99a9..67b908e894 100644 --- a/osu.Game/Online/API/OAuth.cs +++ b/osu.Game/Online/API/OAuth.cs @@ -1,185 +1,185 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Diagnostics; -using osu.Framework.Configuration; -using osu.Framework.IO.Network; - -namespace osu.Game.Online.API -{ - public class OAuth - { - private readonly string clientId; - private readonly string clientSecret; - private readonly string endpoint; - - public readonly Bindable Token = new Bindable(); - - public string TokenString - { - get => Token.Value?.ToString(); - set => Token.Value = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); - } - - internal OAuth(string clientId, string clientSecret, string endpoint) - { - Debug.Assert(clientId != null); - Debug.Assert(clientSecret != null); - Debug.Assert(endpoint != null); - - this.clientId = clientId; - this.clientSecret = clientSecret; - this.endpoint = endpoint; - } - - internal bool AuthenticateWithLogin(string username, string password) - { - if (string.IsNullOrEmpty(username)) return false; - if (string.IsNullOrEmpty(password)) return false; - - using (var req = new AccessTokenRequestPassword(username, password) - { - Url = $@"{endpoint}/oauth/token", - Method = HttpMethod.POST, - ClientId = clientId, - ClientSecret = clientSecret - }) - { - try - { - req.Perform(); - } - catch - { - return false; - } - - Token.Value = req.ResponseObject; - return true; - } - } - - internal bool AuthenticateWithRefresh(string refresh) - { - try - { - using (var req = new AccessTokenRequestRefresh(refresh) - { - Url = $@"{endpoint}/oauth/token", - Method = HttpMethod.POST, - ClientId = clientId, - ClientSecret = clientSecret - }) - { - req.Perform(); - - Token.Value = req.ResponseObject; - return true; - } - } - catch - { - //todo: potentially only kill the refresh token on certain exception types. - Token.Value = null; - return false; - } - } - - private static readonly object access_token_retrieval_lock = new object(); - - /// - /// Should be run before any API request to make sure we have a valid key. - /// - private bool ensureAccessToken() - { - // if we already have a valid access token, let's use it. - if (accessTokenValid) return true; - - // we want to ensure only a single authentication update is happening at once. - lock (access_token_retrieval_lock) - { - // re-check if valid, in case another request completed and revalidated our access. - if (accessTokenValid) return true; - - // if not, let's try using our refresh token to request a new access token. - if (!string.IsNullOrEmpty(Token.Value?.RefreshToken)) - // ReSharper disable once PossibleNullReferenceException - AuthenticateWithRefresh(Token.Value.RefreshToken); - - return accessTokenValid; - } - } - - private bool accessTokenValid => Token.Value?.IsValid ?? false; - - internal bool HasValidAccessToken => RequestAccessToken() != null; - - internal string RequestAccessToken() - { - if (!ensureAccessToken()) return null; - - return Token.Value.AccessToken; - } - - internal void Clear() - { - Token.Value = null; - } - - private class AccessTokenRequestRefresh : AccessTokenRequest - { - internal readonly string RefreshToken; - - internal AccessTokenRequestRefresh(string refreshToken) - { - RefreshToken = refreshToken; - GrantType = @"refresh_token"; - } - - protected override void PrePerform() - { - AddParameter("refresh_token", RefreshToken); - - base.PrePerform(); - } - } - - private class AccessTokenRequestPassword : AccessTokenRequest - { - internal readonly string Username; - internal readonly string Password; - - internal AccessTokenRequestPassword(string username, string password) - { - Username = username; - Password = password; - GrantType = @"password"; - } - - protected override void PrePerform() - { - AddParameter("username", Username); - AddParameter("password", Password); - - base.PrePerform(); - } - } - - private class AccessTokenRequest : JsonWebRequest - { - protected string GrantType; - - internal string ClientId; - internal string ClientSecret; - - protected override void PrePerform() - { - AddParameter("grant_type", GrantType); - AddParameter("client_id", ClientId); - AddParameter("client_secret", ClientSecret); - - base.PrePerform(); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Diagnostics; +using osu.Framework.Configuration; +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API +{ + public class OAuth + { + private readonly string clientId; + private readonly string clientSecret; + private readonly string endpoint; + + public readonly Bindable Token = new Bindable(); + + public string TokenString + { + get => Token.Value?.ToString(); + set => Token.Value = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); + } + + internal OAuth(string clientId, string clientSecret, string endpoint) + { + Debug.Assert(clientId != null); + Debug.Assert(clientSecret != null); + Debug.Assert(endpoint != null); + + this.clientId = clientId; + this.clientSecret = clientSecret; + this.endpoint = endpoint; + } + + internal bool AuthenticateWithLogin(string username, string password) + { + if (string.IsNullOrEmpty(username)) return false; + if (string.IsNullOrEmpty(password)) return false; + + using (var req = new AccessTokenRequestPassword(username, password) + { + Url = $@"{endpoint}/oauth/token", + Method = HttpMethod.POST, + ClientId = clientId, + ClientSecret = clientSecret + }) + { + try + { + req.Perform(); + } + catch + { + return false; + } + + Token.Value = req.ResponseObject; + return true; + } + } + + internal bool AuthenticateWithRefresh(string refresh) + { + try + { + using (var req = new AccessTokenRequestRefresh(refresh) + { + Url = $@"{endpoint}/oauth/token", + Method = HttpMethod.POST, + ClientId = clientId, + ClientSecret = clientSecret + }) + { + req.Perform(); + + Token.Value = req.ResponseObject; + return true; + } + } + catch + { + //todo: potentially only kill the refresh token on certain exception types. + Token.Value = null; + return false; + } + } + + private static readonly object access_token_retrieval_lock = new object(); + + /// + /// Should be run before any API request to make sure we have a valid key. + /// + private bool ensureAccessToken() + { + // if we already have a valid access token, let's use it. + if (accessTokenValid) return true; + + // we want to ensure only a single authentication update is happening at once. + lock (access_token_retrieval_lock) + { + // re-check if valid, in case another request completed and revalidated our access. + if (accessTokenValid) return true; + + // if not, let's try using our refresh token to request a new access token. + if (!string.IsNullOrEmpty(Token.Value?.RefreshToken)) + // ReSharper disable once PossibleNullReferenceException + AuthenticateWithRefresh(Token.Value.RefreshToken); + + return accessTokenValid; + } + } + + private bool accessTokenValid => Token.Value?.IsValid ?? false; + + internal bool HasValidAccessToken => RequestAccessToken() != null; + + internal string RequestAccessToken() + { + if (!ensureAccessToken()) return null; + + return Token.Value.AccessToken; + } + + internal void Clear() + { + Token.Value = null; + } + + private class AccessTokenRequestRefresh : AccessTokenRequest + { + internal readonly string RefreshToken; + + internal AccessTokenRequestRefresh(string refreshToken) + { + RefreshToken = refreshToken; + GrantType = @"refresh_token"; + } + + protected override void PrePerform() + { + AddParameter("refresh_token", RefreshToken); + + base.PrePerform(); + } + } + + private class AccessTokenRequestPassword : AccessTokenRequest + { + internal readonly string Username; + internal readonly string Password; + + internal AccessTokenRequestPassword(string username, string password) + { + Username = username; + Password = password; + GrantType = @"password"; + } + + protected override void PrePerform() + { + AddParameter("username", Username); + AddParameter("password", Password); + + base.PrePerform(); + } + } + + private class AccessTokenRequest : JsonWebRequest + { + protected string GrantType; + + internal string ClientId; + internal string ClientSecret; + + protected override void PrePerform() + { + AddParameter("grant_type", GrantType); + AddParameter("client_id", ClientId); + AddParameter("client_secret", ClientSecret); + + base.PrePerform(); + } + } + } +} diff --git a/osu.Game/Online/API/OAuthToken.cs b/osu.Game/Online/API/OAuthToken.cs index d2b9703a93..b9458583f1 100644 --- a/osu.Game/Online/API/OAuthToken.cs +++ b/osu.Game/Online/API/OAuthToken.cs @@ -1,63 +1,63 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Globalization; -using Newtonsoft.Json; - -namespace osu.Game.Online.API -{ - [Serializable] - public class OAuthToken - { - /// - /// OAuth 2.0 access token. - /// - [JsonProperty(@"access_token")] - public string AccessToken; - - [JsonProperty(@"expires_in")] - public long ExpiresIn - { - get - { - return AccessTokenExpiry - DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - } - - set - { - AccessTokenExpiry = DateTimeOffset.Now.AddSeconds(value).ToUnixTimeSeconds(); - } - } - - public bool IsValid => !string.IsNullOrEmpty(AccessToken) && ExpiresIn > 30; - - public long AccessTokenExpiry; - - /// - /// OAuth 2.0 refresh token. - /// - [JsonProperty(@"refresh_token")] - public string RefreshToken; - - public override string ToString() => $@"{AccessToken}|{AccessTokenExpiry.ToString(NumberFormatInfo.InvariantInfo)}|{RefreshToken}"; - - public static OAuthToken Parse(string value) - { - try - { - string[] parts = value.Split('|'); - return new OAuthToken - { - AccessToken = parts[0], - AccessTokenExpiry = long.Parse(parts[1], NumberFormatInfo.InvariantInfo), - RefreshToken = parts[2] - }; - } - catch - { - } - return null; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Globalization; +using Newtonsoft.Json; + +namespace osu.Game.Online.API +{ + [Serializable] + public class OAuthToken + { + /// + /// OAuth 2.0 access token. + /// + [JsonProperty(@"access_token")] + public string AccessToken; + + [JsonProperty(@"expires_in")] + public long ExpiresIn + { + get + { + return AccessTokenExpiry - DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + } + + set + { + AccessTokenExpiry = DateTimeOffset.Now.AddSeconds(value).ToUnixTimeSeconds(); + } + } + + public bool IsValid => !string.IsNullOrEmpty(AccessToken) && ExpiresIn > 30; + + public long AccessTokenExpiry; + + /// + /// OAuth 2.0 refresh token. + /// + [JsonProperty(@"refresh_token")] + public string RefreshToken; + + public override string ToString() => $@"{AccessToken}|{AccessTokenExpiry.ToString(NumberFormatInfo.InvariantInfo)}|{RefreshToken}"; + + public static OAuthToken Parse(string value) + { + try + { + string[] parts = value.Split('|'); + return new OAuthToken + { + AccessToken = parts[0], + AccessTokenExpiry = long.Parse(parts[1], NumberFormatInfo.InvariantInfo), + RefreshToken = parts[2] + }; + } + catch + { + } + return null; + } + } +} diff --git a/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs b/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs index 28376a1b4f..2661303652 100644 --- a/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs @@ -1,145 +1,145 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using System; - -namespace osu.Game.Online.API.Requests -{ - public class APIResponseBeatmapSet : BeatmapMetadata // todo: this is a bit wrong... - { - [JsonProperty(@"covers")] - private BeatmapSetOnlineCovers covers { get; set; } - - [JsonProperty(@"preview_url")] - private string preview { get; set; } - - [JsonProperty(@"play_count")] - private int playCount { get; set; } - - [JsonProperty(@"favourite_count")] - private int favouriteCount { get; set; } - - [JsonProperty(@"bpm")] - private double bpm { get; set; } - - [JsonProperty(@"video")] - private bool hasVideo { get; set; } - - [JsonProperty(@"status")] - private BeatmapSetOnlineStatus status { get; set; } - - [JsonProperty(@"submitted_date")] - private DateTimeOffset submitted { get; set; } - - [JsonProperty(@"ranked_date")] - private DateTimeOffset ranked { get; set; } - - [JsonProperty(@"last_updated")] - private DateTimeOffset lastUpdated { get; set; } - - [JsonProperty(@"user_id")] - private long creatorId { - set { Author.Id = value; } - } - - [JsonProperty(@"beatmaps")] - private IEnumerable beatmaps { get; set; } - - public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) - { - return new BeatmapSetInfo - { - OnlineBeatmapSetID = OnlineBeatmapSetID, - Metadata = this, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = covers, - Preview = preview, - PlayCount = playCount, - FavouriteCount = favouriteCount, - BPM = bpm, - Status = status, - HasVideo = hasVideo, - Submitted = submitted, - Ranked = ranked, - LastUpdated = lastUpdated, - }, - Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), - }; - } - - private class APIResponseBeatmap : BeatmapMetadata - { - [JsonProperty(@"id")] - private int onlineBeatmapID { get; set; } - - [JsonProperty(@"playcount")] - private int playCount { get; set; } - - [JsonProperty(@"passcount")] - private int passCount { get; set; } - - [JsonProperty(@"mode_int")] - private int ruleset { get; set; } - - [JsonProperty(@"difficulty_rating")] - private double starDifficulty { get; set; } - - [JsonProperty(@"drain")] - private float drainRate { get; set; } - - [JsonProperty(@"cs")] - private float circleSize { get; set; } - - [JsonProperty(@"ar")] - private float approachRate { get; set; } - - [JsonProperty(@"accuracy")] - private float overallDifficulty { get; set; } - - [JsonProperty(@"total_length")] - private double length { get; set; } - - [JsonProperty(@"count_circles")] - private int circleCount { get; set; } - - [JsonProperty(@"count_sliders")] - private int sliderCount { get; set; } - - [JsonProperty(@"version")] - private string version { get; set; } - - public BeatmapInfo ToBeatmap(RulesetStore rulesets) - { - return new BeatmapInfo - { - Metadata = this, - Ruleset = rulesets.GetRuleset(ruleset), - StarDifficulty = starDifficulty, - OnlineBeatmapID = onlineBeatmapID, - Version = version, - BaseDifficulty = new BeatmapDifficulty - { - DrainRate = drainRate, - CircleSize = circleSize, - ApproachRate = approachRate, - OverallDifficulty = overallDifficulty, - }, - OnlineInfo = new BeatmapOnlineInfo - { - PlayCount = playCount, - PassCount = passCount, - Length = length, - CircleCount = circleCount, - SliderCount = sliderCount, - }, - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using System; + +namespace osu.Game.Online.API.Requests +{ + public class APIResponseBeatmapSet : BeatmapMetadata // todo: this is a bit wrong... + { + [JsonProperty(@"covers")] + private BeatmapSetOnlineCovers covers { get; set; } + + [JsonProperty(@"preview_url")] + private string preview { get; set; } + + [JsonProperty(@"play_count")] + private int playCount { get; set; } + + [JsonProperty(@"favourite_count")] + private int favouriteCount { get; set; } + + [JsonProperty(@"bpm")] + private double bpm { get; set; } + + [JsonProperty(@"video")] + private bool hasVideo { get; set; } + + [JsonProperty(@"status")] + private BeatmapSetOnlineStatus status { get; set; } + + [JsonProperty(@"submitted_date")] + private DateTimeOffset submitted { get; set; } + + [JsonProperty(@"ranked_date")] + private DateTimeOffset ranked { get; set; } + + [JsonProperty(@"last_updated")] + private DateTimeOffset lastUpdated { get; set; } + + [JsonProperty(@"user_id")] + private long creatorId { + set { Author.Id = value; } + } + + [JsonProperty(@"beatmaps")] + private IEnumerable beatmaps { get; set; } + + public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) + { + return new BeatmapSetInfo + { + OnlineBeatmapSetID = OnlineBeatmapSetID, + Metadata = this, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = covers, + Preview = preview, + PlayCount = playCount, + FavouriteCount = favouriteCount, + BPM = bpm, + Status = status, + HasVideo = hasVideo, + Submitted = submitted, + Ranked = ranked, + LastUpdated = lastUpdated, + }, + Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), + }; + } + + private class APIResponseBeatmap : BeatmapMetadata + { + [JsonProperty(@"id")] + private int onlineBeatmapID { get; set; } + + [JsonProperty(@"playcount")] + private int playCount { get; set; } + + [JsonProperty(@"passcount")] + private int passCount { get; set; } + + [JsonProperty(@"mode_int")] + private int ruleset { get; set; } + + [JsonProperty(@"difficulty_rating")] + private double starDifficulty { get; set; } + + [JsonProperty(@"drain")] + private float drainRate { get; set; } + + [JsonProperty(@"cs")] + private float circleSize { get; set; } + + [JsonProperty(@"ar")] + private float approachRate { get; set; } + + [JsonProperty(@"accuracy")] + private float overallDifficulty { get; set; } + + [JsonProperty(@"total_length")] + private double length { get; set; } + + [JsonProperty(@"count_circles")] + private int circleCount { get; set; } + + [JsonProperty(@"count_sliders")] + private int sliderCount { get; set; } + + [JsonProperty(@"version")] + private string version { get; set; } + + public BeatmapInfo ToBeatmap(RulesetStore rulesets) + { + return new BeatmapInfo + { + Metadata = this, + Ruleset = rulesets.GetRuleset(ruleset), + StarDifficulty = starDifficulty, + OnlineBeatmapID = onlineBeatmapID, + Version = version, + BaseDifficulty = new BeatmapDifficulty + { + DrainRate = drainRate, + CircleSize = circleSize, + ApproachRate = approachRate, + OverallDifficulty = overallDifficulty, + }, + OnlineInfo = new BeatmapOnlineInfo + { + PlayCount = playCount, + PassCount = passCount, + Length = length, + CircleCount = circleCount, + SliderCount = sliderCount, + }, + }; + } + } + } +} diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs index 34ebe0c92a..c72bf8cbed 100644 --- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using System; - -namespace osu.Game.Online.API.Requests -{ - public class DownloadBeatmapSetRequest : APIDownloadRequest - { - public readonly BeatmapSetInfo BeatmapSet; - - public Action DownloadProgressed; - - private readonly bool noVideo; - - public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo) - { - this.noVideo = noVideo; - BeatmapSet = set; - - Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total); - } - - protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using System; + +namespace osu.Game.Online.API.Requests +{ + public class DownloadBeatmapSetRequest : APIDownloadRequest + { + public readonly BeatmapSetInfo BeatmapSet; + + public Action DownloadProgressed; + + private readonly bool noVideo; + + public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo) + { + this.noVideo = noVideo; + BeatmapSet = set; + + Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total); + } + + protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}"; + } +} diff --git a/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs index 4b6129e015..b853da7e76 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; -using osu.Game.Beatmaps; - -namespace osu.Game.Online.API.Requests -{ - public class GetBeatmapDetailsRequest : APIRequest - { - private readonly BeatmapInfo beatmap; - - private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={System.Uri.EscapeUriString(beatmap.Path)}"; - - public GetBeatmapDetailsRequest(BeatmapInfo beatmap) - { - this.beatmap = beatmap; - } - - protected override string Target => $@"beatmaps/{lookupString}"; - } - - public class GetBeatmapDetailsResponse : BeatmapMetrics - { - //the online API returns some metrics as a nested object. - [JsonProperty(@"failtimes")] - private BeatmapMetrics failTimes - { - set - { - Fails = value.Fails; - Retries = value.Retries; - } - } - - //and other metrics in the beatmap set. - [JsonProperty(@"beatmapset")] - private BeatmapMetrics beatmapSet - { - set - { - Ratings = value.Ratings; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using osu.Game.Beatmaps; + +namespace osu.Game.Online.API.Requests +{ + public class GetBeatmapDetailsRequest : APIRequest + { + private readonly BeatmapInfo beatmap; + + private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={System.Uri.EscapeUriString(beatmap.Path)}"; + + public GetBeatmapDetailsRequest(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + } + + protected override string Target => $@"beatmaps/{lookupString}"; + } + + public class GetBeatmapDetailsResponse : BeatmapMetrics + { + //the online API returns some metrics as a nested object. + [JsonProperty(@"failtimes")] + private BeatmapMetrics failTimes + { + set + { + Fails = value.Fails; + Retries = value.Retries; + } + } + + //and other metrics in the beatmap set. + [JsonProperty(@"beatmapset")] + private BeatmapMetrics beatmapSet + { + set + { + Ratings = value.Ratings; + } + } + } +} diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs index cba1d9471c..b031791900 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Online.API.Requests -{ - public class GetBeatmapSetRequest : APIRequest - { - private readonly int beatmapSetId; - - public GetBeatmapSetRequest(int beatmapSetId) - { - this.beatmapSetId = beatmapSetId; - } - - protected override string Target => $@"beatmapsets/{beatmapSetId}"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Online.API.Requests +{ + public class GetBeatmapSetRequest : APIRequest + { + private readonly int beatmapSetId; + + public GetBeatmapSetRequest(int beatmapSetId) + { + this.beatmapSetId = beatmapSetId; + } + + protected override string Target => $@"beatmapsets/{beatmapSetId}"; + } +} diff --git a/osu.Game/Online/API/Requests/GetFriendsRequest.cs b/osu.Game/Online/API/Requests/GetFriendsRequest.cs index 5bc3d8e39e..13bdd23170 100644 --- a/osu.Game/Online/API/Requests/GetFriendsRequest.cs +++ b/osu.Game/Online/API/Requests/GetFriendsRequest.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Users; - -namespace osu.Game.Online.API.Requests -{ - public class GetFriendsRequest : APIRequest> - { - protected override string Target => @"friends"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests +{ + public class GetFriendsRequest : APIRequest> + { + protected override string Target => @"friends"; + } +} diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index a8f63887a1..d600f40716 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.IO.Network; -using osu.Game.Online.Chat; - -namespace osu.Game.Online.API.Requests -{ - public class GetMessagesRequest : APIRequest> - { - private readonly List channels; - private long? since; - - public GetMessagesRequest(List channels, long? sinceId) - { - this.channels = channels; - since = sinceId; - } - - protected override WebRequest CreateWebRequest() - { - string channelString = string.Join(",", channels.Select(x => x.Id)); - - var req = base.CreateWebRequest(); - req.AddParameter(@"channels", channelString); - if (since.HasValue) req.AddParameter(@"since", since.Value.ToString()); - - return req; - } - - protected override string Target => @"chat/messages"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class GetMessagesRequest : APIRequest> + { + private readonly List channels; + private long? since; + + public GetMessagesRequest(List channels, long? sinceId) + { + this.channels = channels; + since = sinceId; + } + + protected override WebRequest CreateWebRequest() + { + string channelString = string.Join(",", channels.Select(x => x.Id)); + + var req = base.CreateWebRequest(); + req.AddParameter(@"channels", channelString); + if (since.HasValue) req.AddParameter(@"since", since.Value.ToString()); + + return req; + } + + protected override string Target => @"chat/messages"; + } +} diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index b726a3767a..cff6fd4ea5 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -1,166 +1,166 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Users; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Select.Leaderboards; -using osu.Framework.IO.Network; - -namespace osu.Game.Online.API.Requests -{ - public class GetScoresRequest : APIRequest - { - private readonly BeatmapInfo beatmap; - private readonly LeaderboardScope scope; - private readonly RulesetInfo ruleset; - - public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global) - { - if (!beatmap.OnlineBeatmapID.HasValue) - throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); - - if (scope == LeaderboardScope.Local) - throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard"); - - this.beatmap = beatmap; - this.scope = scope; - this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset)); - - Success += onSuccess; - } - - private void onSuccess(GetScoresResponse r) - { - foreach (OnlineScore score in r.Scores) - score.ApplyBeatmap(beatmap); - } - - protected override WebRequest CreateWebRequest() - { - var req = base.CreateWebRequest(); - - req.Timeout = 30000; - req.AddParameter(@"type", scope.ToString().ToLowerInvariant()); - req.AddParameter(@"mode", ruleset.ShortName); - - return req; - } - - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; - } - - public class GetScoresResponse - { - [JsonProperty(@"scores")] - public IEnumerable Scores; - } - - public class OnlineScore : Score - { - [JsonProperty(@"score")] - private double totalScore - { - set { TotalScore = value; } - } - - [JsonProperty(@"max_combo")] - private int maxCombo - { - set { MaxCombo = value; } - } - - [JsonProperty(@"user")] - private User user - { - set { User = value; } - } - - [JsonProperty(@"replay_data")] - private Replay replay - { - set { Replay = value; } - } - - [JsonProperty(@"mode_int")] - public int OnlineRulesetID { get; set; } - - [JsonProperty(@"score_id")] - private long onlineScoreID - { - set { OnlineScoreID = value; } - } - - [JsonProperty(@"created_at")] - private DateTimeOffset date - { - set { Date = value; } - } - - [JsonProperty(@"beatmap")] - private BeatmapInfo beatmap - { - set { Beatmap = value; } - } - - [JsonProperty(@"beatmapset")] - private BeatmapMetadata metadata - { - set { Beatmap.Metadata = value; } - } - - [JsonProperty(@"statistics")] - private Dictionary jsonStats - { - set - { - foreach (var kvp in value) - { - HitResult newKey; - switch (kvp.Key) - { - case @"count_300": - newKey = HitResult.Great; - break; - case @"count_100": - newKey = HitResult.Good; - break; - case @"count_50": - newKey = HitResult.Meh; - break; - case @"count_miss": - newKey = HitResult.Miss; - break; - default: - continue; - } - - Statistics.Add(newKey, kvp.Value); - } - } - } - - [JsonProperty(@"mods")] - private string[] modStrings { get; set; } - - public void ApplyBeatmap(BeatmapInfo beatmap) - { - Beatmap = beatmap; - ApplyRuleset(beatmap.Ruleset); - } - - public void ApplyRuleset(RulesetInfo ruleset) - { - Ruleset = ruleset; - - // Evaluate the mod string - Mods = Ruleset.CreateInstance().GetAllMods().Where(mod => modStrings.Contains(mod.ShortenedName)).ToArray(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Users; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Select.Leaderboards; +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API.Requests +{ + public class GetScoresRequest : APIRequest + { + private readonly BeatmapInfo beatmap; + private readonly LeaderboardScope scope; + private readonly RulesetInfo ruleset; + + public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global) + { + if (!beatmap.OnlineBeatmapID.HasValue) + throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); + + if (scope == LeaderboardScope.Local) + throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard"); + + this.beatmap = beatmap; + this.scope = scope; + this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset)); + + Success += onSuccess; + } + + private void onSuccess(GetScoresResponse r) + { + foreach (OnlineScore score in r.Scores) + score.ApplyBeatmap(beatmap); + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + req.Timeout = 30000; + req.AddParameter(@"type", scope.ToString().ToLowerInvariant()); + req.AddParameter(@"mode", ruleset.ShortName); + + return req; + } + + protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; + } + + public class GetScoresResponse + { + [JsonProperty(@"scores")] + public IEnumerable Scores; + } + + public class OnlineScore : Score + { + [JsonProperty(@"score")] + private double totalScore + { + set { TotalScore = value; } + } + + [JsonProperty(@"max_combo")] + private int maxCombo + { + set { MaxCombo = value; } + } + + [JsonProperty(@"user")] + private User user + { + set { User = value; } + } + + [JsonProperty(@"replay_data")] + private Replay replay + { + set { Replay = value; } + } + + [JsonProperty(@"mode_int")] + public int OnlineRulesetID { get; set; } + + [JsonProperty(@"score_id")] + private long onlineScoreID + { + set { OnlineScoreID = value; } + } + + [JsonProperty(@"created_at")] + private DateTimeOffset date + { + set { Date = value; } + } + + [JsonProperty(@"beatmap")] + private BeatmapInfo beatmap + { + set { Beatmap = value; } + } + + [JsonProperty(@"beatmapset")] + private BeatmapMetadata metadata + { + set { Beatmap.Metadata = value; } + } + + [JsonProperty(@"statistics")] + private Dictionary jsonStats + { + set + { + foreach (var kvp in value) + { + HitResult newKey; + switch (kvp.Key) + { + case @"count_300": + newKey = HitResult.Great; + break; + case @"count_100": + newKey = HitResult.Good; + break; + case @"count_50": + newKey = HitResult.Meh; + break; + case @"count_miss": + newKey = HitResult.Miss; + break; + default: + continue; + } + + Statistics.Add(newKey, kvp.Value); + } + } + } + + [JsonProperty(@"mods")] + private string[] modStrings { get; set; } + + public void ApplyBeatmap(BeatmapInfo beatmap) + { + Beatmap = beatmap; + ApplyRuleset(beatmap.Ruleset); + } + + public void ApplyRuleset(RulesetInfo ruleset) + { + Ruleset = ruleset; + + // Evaluate the mod string + Mods = Ruleset.CreateInstance().GetAllMods().Where(mod => modStrings.Contains(mod.ShortenedName)).ToArray(); + } + } +} diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 70468c557a..48e9babd97 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Humanizer; -using System.Collections.Generic; - -namespace osu.Game.Online.API.Requests -{ - public class GetUserBeatmapsRequest : APIRequest> - { - private readonly long userId; - private readonly int offset; - private readonly BeatmapSetType type; - - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) - { - this.userId = userId; - this.offset = offset; - this.type = type; - } - - // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; - } - - public enum BeatmapSetType - { - Favourite, - RankedAndApproved, - Unranked, - Graveyard - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Humanizer; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserBeatmapsRequest : APIRequest> + { + private readonly long userId; + private readonly int offset; + private readonly BeatmapSetType type; + + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) + { + this.userId = userId; + this.offset = offset; + this.type = type; + } + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; + } + + public enum BeatmapSetType + { + Favourite, + RankedAndApproved, + Unranked, + Graveyard + } +} diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 3fcbe98b11..106f3d4fb1 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -1,48 +1,48 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using System.Collections.Generic; - -namespace osu.Game.Online.API.Requests -{ - public class GetUserMostPlayedBeatmapsRequest : APIRequest> - { - private readonly long userId; - private readonly int offset; - - public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0) - { - this.userId = userId; - this.offset = offset; - } - - protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}"; - } - - public class MostPlayedBeatmap - { - [JsonProperty("beatmap_id")] - public int BeatmapID; - - [JsonProperty("count")] - public int PlayCount; - - [JsonProperty] - private BeatmapInfo beatmap; - - [JsonProperty] - private APIResponseBeatmapSet beatmapSet; - - public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) - { - BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); - beatmap.BeatmapSet = setInfo; - beatmap.OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID; - beatmap.Metadata = setInfo.Metadata; - return beatmap; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserMostPlayedBeatmapsRequest : APIRequest> + { + private readonly long userId; + private readonly int offset; + + public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0) + { + this.userId = userId; + this.offset = offset; + } + + protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}"; + } + + public class MostPlayedBeatmap + { + [JsonProperty("beatmap_id")] + public int BeatmapID; + + [JsonProperty("count")] + public int PlayCount; + + [JsonProperty] + private BeatmapInfo beatmap; + + [JsonProperty] + private APIResponseBeatmapSet beatmapSet; + + public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) + { + BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); + beatmap.BeatmapSet = setInfo; + beatmap.OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID; + beatmap.Metadata = setInfo.Metadata; + return beatmap; + } + } +} diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index d1685b01f3..ded0e4f80d 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -1,130 +1,130 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; -using osu.Game.Rulesets.Scoring; -using Humanizer; -using System; -using System.Collections.Generic; - -namespace osu.Game.Online.API.Requests -{ - public class GetUserRecentActivitiesRequest : APIRequest> - { - private readonly long userId; - private readonly int offset; - - public GetUserRecentActivitiesRequest(long userId, int offset = 0) - { - this.userId = userId; - this.offset = offset; - } - - protected override string Target => $"users/{userId}/recent_activity?offset={offset}"; - } - - public class RecentActivity - { - [JsonProperty("id")] - public int ID; - - [JsonProperty("createdAt")] - public DateTimeOffset CreatedAt; - - [JsonProperty] - private string type - { - set => Type = (RecentActivityType)Enum.Parse(typeof(RecentActivityType), value.Pascalize()); - } - - public RecentActivityType Type; - - [JsonProperty] - private string scoreRank - { - set => ScoreRank = (ScoreRank)Enum.Parse(typeof(ScoreRank), value); - } - - public ScoreRank ScoreRank; - - [JsonProperty("rank")] - public int Rank; - - [JsonProperty("approval")] - public BeatmapApproval Approval; - - [JsonProperty("count")] - public int Count; - - [JsonProperty("mode")] - public string Mode; - - [JsonProperty("beatmap")] - public RecentActivityBeatmap Beatmap; - - [JsonProperty("beatmapset")] - public RecentActivityBeatmap Beatmapset; - - [JsonProperty("user")] - public RecentActivityUser User; - - [JsonProperty("achievement")] - public RecentActivityAchievement Achievement; - - public class RecentActivityBeatmap - { - [JsonProperty("title")] - public string Title; - - [JsonProperty("url")] - public string Url; - } - - public class RecentActivityUser - { - [JsonProperty("username")] - public string Username; - - [JsonProperty("url")] - public string Url; - - [JsonProperty("previousUsername")] - public string PreviousUsername; - } - - public class RecentActivityAchievement - { - [JsonProperty("slug")] - public string Slug; - - [JsonProperty("name")] - public string Name; - } - - } - - public enum RecentActivityType - { - Achievement, - BeatmapPlaycount, - BeatmapsetApprove, - BeatmapsetDelete, - BeatmapsetRevive, - BeatmapsetUpdate, - BeatmapsetUpload, - Medal, - Rank, - RankLost, - UserSupportAgain, - UserSupportFirst, - UserSupportGift, - UsernameChange, - } - - public enum BeatmapApproval - { - Ranked, - Approved, - Qualified, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using osu.Game.Rulesets.Scoring; +using Humanizer; +using System; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserRecentActivitiesRequest : APIRequest> + { + private readonly long userId; + private readonly int offset; + + public GetUserRecentActivitiesRequest(long userId, int offset = 0) + { + this.userId = userId; + this.offset = offset; + } + + protected override string Target => $"users/{userId}/recent_activity?offset={offset}"; + } + + public class RecentActivity + { + [JsonProperty("id")] + public int ID; + + [JsonProperty("createdAt")] + public DateTimeOffset CreatedAt; + + [JsonProperty] + private string type + { + set => Type = (RecentActivityType)Enum.Parse(typeof(RecentActivityType), value.Pascalize()); + } + + public RecentActivityType Type; + + [JsonProperty] + private string scoreRank + { + set => ScoreRank = (ScoreRank)Enum.Parse(typeof(ScoreRank), value); + } + + public ScoreRank ScoreRank; + + [JsonProperty("rank")] + public int Rank; + + [JsonProperty("approval")] + public BeatmapApproval Approval; + + [JsonProperty("count")] + public int Count; + + [JsonProperty("mode")] + public string Mode; + + [JsonProperty("beatmap")] + public RecentActivityBeatmap Beatmap; + + [JsonProperty("beatmapset")] + public RecentActivityBeatmap Beatmapset; + + [JsonProperty("user")] + public RecentActivityUser User; + + [JsonProperty("achievement")] + public RecentActivityAchievement Achievement; + + public class RecentActivityBeatmap + { + [JsonProperty("title")] + public string Title; + + [JsonProperty("url")] + public string Url; + } + + public class RecentActivityUser + { + [JsonProperty("username")] + public string Username; + + [JsonProperty("url")] + public string Url; + + [JsonProperty("previousUsername")] + public string PreviousUsername; + } + + public class RecentActivityAchievement + { + [JsonProperty("slug")] + public string Slug; + + [JsonProperty("name")] + public string Name; + } + + } + + public enum RecentActivityType + { + Achievement, + BeatmapPlaycount, + BeatmapsetApprove, + BeatmapsetDelete, + BeatmapsetRevive, + BeatmapsetUpdate, + BeatmapsetUpload, + Medal, + Rank, + RankLost, + UserSupportAgain, + UserSupportFirst, + UserSupportGift, + UsernameChange, + } + + public enum BeatmapApproval + { + Ranked, + Approved, + Qualified, + } +} diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 6a586bfe2e..9026d10334 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Users; - -namespace osu.Game.Online.API.Requests -{ - public class GetUserRequest : APIRequest - { - private long? userId; - - public GetUserRequest(long? userId = null) - { - this.userId = userId; - } - - protected override string Target => userId.HasValue ? $@"users/{userId}" : @"me"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserRequest : APIRequest + { + private long? userId; + + public GetUserRequest(long? userId = null) + { + this.userId = userId; + } + + protected override string Target => userId.HasValue ? $@"users/{userId}" : @"me"; + } +} diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 7bdd1a94b9..7757f529e0 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; - -namespace osu.Game.Online.API.Requests -{ - public class GetUserScoresRequest : APIRequest> - { - private readonly long userId; - private readonly ScoreType type; - private readonly int offset; - - public GetUserScoresRequest(long userId, ScoreType type, int offset = 0) - { - this.userId = userId; - this.type = type; - this.offset = offset; - } - - // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLower()}?offset={offset}"; - } - - public enum ScoreType - { - Best, - Firsts, - Recent - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserScoresRequest : APIRequest> + { + private readonly long userId; + private readonly ScoreType type; + private readonly int offset; + + public GetUserScoresRequest(long userId, ScoreType type, int offset = 0) + { + this.userId = userId; + this.type = type; + this.offset = offset; + } + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLower()}?offset={offset}"; + } + + public enum ScoreType + { + Best, + Firsts, + Recent + } +} diff --git a/osu.Game/Online/API/Requests/GetUsersRequest.cs b/osu.Game/Online/API/Requests/GetUsersRequest.cs index 267985a253..575d0464aa 100644 --- a/osu.Game/Online/API/Requests/GetUsersRequest.cs +++ b/osu.Game/Online/API/Requests/GetUsersRequest.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using Newtonsoft.Json; -using osu.Game.Users; - -namespace osu.Game.Online.API.Requests -{ - public class GetUsersRequest : APIRequest> - { - protected override string Target => @"rankings/osu/performance"; - } - - public class RankingEntry - { - [JsonProperty] - public User User; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using Newtonsoft.Json; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests +{ + public class GetUsersRequest : APIRequest> + { + protected override string Target => @"rankings/osu/performance"; + } + + public class RankingEntry + { + [JsonProperty] + public User User; + } +} diff --git a/osu.Game/Online/API/Requests/ListChannelsRequest.cs b/osu.Game/Online/API/Requests/ListChannelsRequest.cs index b387af9694..d1ee4fde01 100644 --- a/osu.Game/Online/API/Requests/ListChannelsRequest.cs +++ b/osu.Game/Online/API/Requests/ListChannelsRequest.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Online.Chat; - -namespace osu.Game.Online.API.Requests -{ - public class ListChannelsRequest : APIRequest> - { - protected override string Target => @"chat/channels"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class ListChannelsRequest : APIRequest> + { + protected override string Target => @"chat/channels"; + } +} diff --git a/osu.Game/Online/API/Requests/PostMessageRequest.cs b/osu.Game/Online/API/Requests/PostMessageRequest.cs index e13128cc3c..44429bdcd5 100644 --- a/osu.Game/Online/API/Requests/PostMessageRequest.cs +++ b/osu.Game/Online/API/Requests/PostMessageRequest.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions; -using osu.Framework.IO.Network; -using osu.Game.Online.Chat; - -namespace osu.Game.Online.API.Requests -{ - public class PostMessageRequest : APIRequest - { - private readonly Message message; - - public PostMessageRequest(Message message) - { - this.message = message; - } - - protected override WebRequest CreateWebRequest() - { - var req = base.CreateWebRequest(); - - req.Method = HttpMethod.POST; - req.AddParameter(@"target_type", message.TargetType.GetDescription()); - req.AddParameter(@"target_id", message.TargetId.ToString()); - req.AddParameter(@"is_action", message.IsAction.ToString().ToLower()); - req.AddParameter(@"message", message.Content); - - return req; - } - - protected override string Target => @"chat/messages"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class PostMessageRequest : APIRequest + { + private readonly Message message; + + public PostMessageRequest(Message message) + { + this.message = message; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + req.Method = HttpMethod.POST; + req.AddParameter(@"target_type", message.TargetType.GetDescription()); + req.AddParameter(@"target_id", message.TargetId.ToString()); + req.AddParameter(@"is_action", message.IsAction.ToString().ToLower()); + req.AddParameter(@"message", message.Content); + + return req; + } + + protected override string Target => @"chat/messages"; + } +} diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index c54d0ea556..e961dd9a9e 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Overlays; -using osu.Game.Overlays.Direct; -using osu.Game.Rulesets; - -namespace osu.Game.Online.API.Requests -{ - public class SearchBeatmapSetsRequest : APIRequest> - { - private readonly string query; - private readonly RulesetInfo ruleset; - private readonly RankStatus rankStatus; - private readonly DirectSortCriteria sortCriteria; - private readonly SortDirection direction; - private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; - - public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, RankStatus rankStatus = RankStatus.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending) - { - this.query = System.Uri.EscapeDataString(query); - this.ruleset = ruleset; - this.rankStatus = rankStatus; - this.sortCriteria = sortCriteria; - this.direction = direction; - } - - // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Overlays; +using osu.Game.Overlays.Direct; +using osu.Game.Rulesets; + +namespace osu.Game.Online.API.Requests +{ + public class SearchBeatmapSetsRequest : APIRequest> + { + private readonly string query; + private readonly RulesetInfo ruleset; + private readonly RankStatus rankStatus; + private readonly DirectSortCriteria sortCriteria; + private readonly SortDirection direction; + private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; + + public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, RankStatus rankStatus = RankStatus.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending) + { + this.query = System.Uri.EscapeDataString(query); + this.ruleset = ruleset; + this.rankStatus = rankStatus; + this.sortCriteria = sortCriteria; + this.direction = direction; + } + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; + } +} diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 35952fbc6e..e7aabad780 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -1,105 +1,105 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using osu.Framework.Configuration; -using osu.Framework.Lists; - -namespace osu.Game.Online.Chat -{ - public class Channel - { - [JsonProperty(@"name")] - public string Name; - - [JsonProperty(@"description")] - public string Topic; - - [JsonProperty(@"type")] - public string Type; - - [JsonProperty(@"channel_id")] - public int Id; - - public readonly SortedList Messages = new SortedList(Comparer.Default); - - private readonly List pendingMessages = new List(); - - public Bindable Joined = new Bindable(); - - public bool ReadOnly => false; - - public const int MAX_HISTORY = 300; - - [JsonConstructor] - public Channel() - { - } - - public event Action> NewMessagesArrived; - public event Action PendingMessageResolved; - public event Action MessageRemoved; - - public void AddLocalEcho(LocalEchoMessage message) - { - pendingMessages.Add(message); - Messages.Add(message); - - NewMessagesArrived?.Invoke(new[] { message }); - } - - public void AddNewMessages(params Message[] messages) - { - messages = messages.Except(Messages).ToArray(); - - Messages.AddRange(messages); - - purgeOldMessages(); - - NewMessagesArrived?.Invoke(messages); - } - - private void purgeOldMessages() - { - // never purge local echos - int messageCount = Messages.Count - pendingMessages.Count; - if (messageCount > MAX_HISTORY) - Messages.RemoveRange(0, messageCount - MAX_HISTORY); - } - - /// - /// Replace or remove a message from the channel. - /// - /// The local echo message (client-side). - /// The response message, or null if the message became invalid. - public void ReplaceMessage(LocalEchoMessage echo, Message final) - { - if (!pendingMessages.Remove(echo)) - throw new InvalidOperationException("Attempted to remove echo that wasn't present"); - - Messages.Remove(echo); - - if (final == null) - { - MessageRemoved?.Invoke(echo); - return; - } - - if (Messages.Contains(final)) - { - // message already inserted, so let's throw away this update. - // we may want to handle this better in the future, but for the time being api requests are single-threaded so order is assumed. - MessageRemoved?.Invoke(echo); - return; - } - - Messages.Add(final); - PendingMessageResolved?.Invoke(echo, final); - } - - public override string ToString() => Name; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using osu.Framework.Configuration; +using osu.Framework.Lists; + +namespace osu.Game.Online.Chat +{ + public class Channel + { + [JsonProperty(@"name")] + public string Name; + + [JsonProperty(@"description")] + public string Topic; + + [JsonProperty(@"type")] + public string Type; + + [JsonProperty(@"channel_id")] + public int Id; + + public readonly SortedList Messages = new SortedList(Comparer.Default); + + private readonly List pendingMessages = new List(); + + public Bindable Joined = new Bindable(); + + public bool ReadOnly => false; + + public const int MAX_HISTORY = 300; + + [JsonConstructor] + public Channel() + { + } + + public event Action> NewMessagesArrived; + public event Action PendingMessageResolved; + public event Action MessageRemoved; + + public void AddLocalEcho(LocalEchoMessage message) + { + pendingMessages.Add(message); + Messages.Add(message); + + NewMessagesArrived?.Invoke(new[] { message }); + } + + public void AddNewMessages(params Message[] messages) + { + messages = messages.Except(Messages).ToArray(); + + Messages.AddRange(messages); + + purgeOldMessages(); + + NewMessagesArrived?.Invoke(messages); + } + + private void purgeOldMessages() + { + // never purge local echos + int messageCount = Messages.Count - pendingMessages.Count; + if (messageCount > MAX_HISTORY) + Messages.RemoveRange(0, messageCount - MAX_HISTORY); + } + + /// + /// Replace or remove a message from the channel. + /// + /// The local echo message (client-side). + /// The response message, or null if the message became invalid. + public void ReplaceMessage(LocalEchoMessage echo, Message final) + { + if (!pendingMessages.Remove(echo)) + throw new InvalidOperationException("Attempted to remove echo that wasn't present"); + + Messages.Remove(echo); + + if (final == null) + { + MessageRemoved?.Invoke(echo); + return; + } + + if (Messages.Contains(final)) + { + // message already inserted, so let's throw away this update. + // we may want to handle this better in the future, but for the time being api requests are single-threaded so order is assumed. + MessageRemoved?.Invoke(echo); + return; + } + + Messages.Add(final); + PendingMessageResolved?.Invoke(echo, final); + } + + public override string ToString() => Name; + } +} diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index 234781fb52..475363bd51 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -1,59 +1,59 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Cursor; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using OpenTK; - -namespace osu.Game.Online.Chat -{ - /// - /// An invisible drawable that brings multiple pieces together to form a consumable clickable link. - /// - public class DrawableLinkCompiler : OsuHoverContainer, IHasTooltip - { - /// - /// Each word part of a chat link (split for word-wrap support). - /// - public List Parts; - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceiveMouseInputAt(screenSpacePos)); - - protected override HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); - - public DrawableLinkCompiler(IEnumerable parts) - { - Parts = parts.ToList(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - IdleColour = colours.Blue; - } - - protected override IEnumerable EffectTargets => Parts; - - public string TooltipText { get; set; } - - private class LinkHoverSounds : HoverClickSounds - { - private readonly List parts; - - public LinkHoverSounds(HoverSampleSet sampleSet, List parts) - : base(sampleSet) - { - this.parts = parts; - } - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => parts.Any(d => d.ReceiveMouseInputAt(screenSpacePos)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Cursor; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using OpenTK; + +namespace osu.Game.Online.Chat +{ + /// + /// An invisible drawable that brings multiple pieces together to form a consumable clickable link. + /// + public class DrawableLinkCompiler : OsuHoverContainer, IHasTooltip + { + /// + /// Each word part of a chat link (split for word-wrap support). + /// + public List Parts; + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceiveMouseInputAt(screenSpacePos)); + + protected override HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); + + public DrawableLinkCompiler(IEnumerable parts) + { + Parts = parts.ToList(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IdleColour = colours.Blue; + } + + protected override IEnumerable EffectTargets => Parts; + + public string TooltipText { get; set; } + + private class LinkHoverSounds : HoverClickSounds + { + private readonly List parts; + + public LinkHoverSounds(HoverSampleSet sampleSet, List parts) + : base(sampleSet) + { + this.parts = parts; + } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => parts.Any(d => d.ReceiveMouseInputAt(screenSpacePos)); + } + } +} diff --git a/osu.Game/Online/Chat/ErrorMessage.cs b/osu.Game/Online/Chat/ErrorMessage.cs index b083cb6a10..87652c50c4 100644 --- a/osu.Game/Online/Chat/ErrorMessage.cs +++ b/osu.Game/Online/Chat/ErrorMessage.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Online.Chat -{ - public class ErrorMessage : InfoMessage - { - public ErrorMessage(string message) : base(message) - { - Sender.Colour = @"ff0000"; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Online.Chat +{ + public class ErrorMessage : InfoMessage + { + public ErrorMessage(string message) : base(message) + { + Sender.Colour = @"ff0000"; + } + } +} diff --git a/osu.Game/Online/Chat/InfoMessage.cs b/osu.Game/Online/Chat/InfoMessage.cs index 3e521de28d..2be025e403 100644 --- a/osu.Game/Online/Chat/InfoMessage.cs +++ b/osu.Game/Online/Chat/InfoMessage.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Users; - -namespace osu.Game.Online.Chat -{ - public class InfoMessage : Message - { - private static int infoID = -1; - - public InfoMessage(string message) : base(infoID--) - { - Timestamp = DateTimeOffset.Now; - Content = message; - - Sender = new User - { - Username = @"system", - Colour = @"0000ff", - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Users; + +namespace osu.Game.Online.Chat +{ + public class InfoMessage : Message + { + private static int infoID = -1; + + public InfoMessage(string message) : base(infoID--) + { + Timestamp = DateTimeOffset.Now; + Content = message; + + Sender = new User + { + Username = @"system", + Colour = @"0000ff", + }; + } + } +} diff --git a/osu.Game/Online/Chat/LocalEchoMessage.cs b/osu.Game/Online/Chat/LocalEchoMessage.cs index 1b31fe08d6..2e90b9d3fd 100644 --- a/osu.Game/Online/Chat/LocalEchoMessage.cs +++ b/osu.Game/Online/Chat/LocalEchoMessage.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Online.Chat -{ - public class LocalEchoMessage : Message - { - public LocalEchoMessage() : base(null) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Online.Chat +{ + public class LocalEchoMessage : Message + { + public LocalEchoMessage() : base(null) + { + } + } +} diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs index df3753da6a..535035e4fc 100644 --- a/osu.Game/Online/Chat/Message.cs +++ b/osu.Game/Online/Chat/Message.cs @@ -1,83 +1,83 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using Newtonsoft.Json; -using osu.Game.Users; - -namespace osu.Game.Online.Chat -{ - public class Message : IComparable, IEquatable - { - [JsonProperty(@"message_id")] - public readonly long? Id; - - //todo: this should be inside sender. - [JsonProperty(@"sender_id")] - public int UserId; - - [JsonProperty(@"target_type")] - public TargetType TargetType; - - [JsonProperty(@"target_id")] - public int TargetId; - - [JsonProperty(@"is_action")] - public bool IsAction; - - [JsonProperty(@"timestamp")] - public DateTimeOffset Timestamp; - - [JsonProperty(@"content")] - public string Content; - - [JsonProperty(@"sender")] - public User Sender; - - [JsonConstructor] - public Message() - { - } - - /// - /// The text that is displayed in chat. - /// - public string DisplayContent { get; set; } - - /// - /// The links found in this message. - /// - /// The s' and s are according to - public List Links; - - public Message(long? id) - { - Id = id; - } - - public int CompareTo(Message other) - { - if (!Id.HasValue) - return other.Id.HasValue ? 1 : Timestamp.CompareTo(other.Timestamp); - if (!other.Id.HasValue) - return -1; - - return Id.Value.CompareTo(other.Id.Value); - } - - public virtual bool Equals(Message other) => Id == other?.Id; - - // ReSharper disable once ImpureMethodCallOnReadonlyValueField - public override int GetHashCode() => Id.GetHashCode(); - } - - public enum TargetType - { - [Description(@"channel")] - Channel, - [Description(@"user")] - User - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Newtonsoft.Json; +using osu.Game.Users; + +namespace osu.Game.Online.Chat +{ + public class Message : IComparable, IEquatable + { + [JsonProperty(@"message_id")] + public readonly long? Id; + + //todo: this should be inside sender. + [JsonProperty(@"sender_id")] + public int UserId; + + [JsonProperty(@"target_type")] + public TargetType TargetType; + + [JsonProperty(@"target_id")] + public int TargetId; + + [JsonProperty(@"is_action")] + public bool IsAction; + + [JsonProperty(@"timestamp")] + public DateTimeOffset Timestamp; + + [JsonProperty(@"content")] + public string Content; + + [JsonProperty(@"sender")] + public User Sender; + + [JsonConstructor] + public Message() + { + } + + /// + /// The text that is displayed in chat. + /// + public string DisplayContent { get; set; } + + /// + /// The links found in this message. + /// + /// The s' and s are according to + public List Links; + + public Message(long? id) + { + Id = id; + } + + public int CompareTo(Message other) + { + if (!Id.HasValue) + return other.Id.HasValue ? 1 : Timestamp.CompareTo(other.Timestamp); + if (!other.Id.HasValue) + return -1; + + return Id.Value.CompareTo(other.Id.Value); + } + + public virtual bool Equals(Message other) => Id == other?.Id; + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public override int GetHashCode() => Id.GetHashCode(); + } + + public enum TargetType + { + [Description(@"channel")] + Channel, + [Description(@"user")] + User + } +} diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 9966f78435..dd19868954 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -1,278 +1,278 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace osu.Game.Online.Chat -{ - public static class MessageFormatter - { - // [[Performance Points]] -> wiki:Performance Points (https://osu.ppy.sh/wiki/Performance_Points) - private static readonly Regex wiki_regex = new Regex(@"\[\[([^\]]+)\]\]"); - - // (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234) - private static readonly Regex old_link_regex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]"); - - // [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234) - private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ([^\[\]]*(((?\[)[^\[\]]*)+((?\])[^\[\]]*)+)*(?(open)(?!)))\]"); - - // [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format - private static readonly Regex markdown_link_regex = new Regex(@"\[([^\]]*)\]\(([a-z]+://[^ ]+)\)"); - - // advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used - // This is in the format (, [optional]): - // http[s]://.[:port][/path][?query][#fragment] - private static readonly Regex advanced_link_regex = new Regex( - // protocol - @"(?[a-z]*?:\/\/" + - // domain + tld - @"(?(?:[a-z0-9]\.|[a-z0-9][a-z0-9-]*[a-z0-9]\.)*[a-z0-9-]*[a-z0-9]" + - // port (optional) - @"(?::\d+)?)" + - // path (optional) - @"(?(?:(?:\/+(?:[a-z0-9$_\.\+!\*\',;:\(\)@&~=-]|%[0-9a-f]{2})*)*" + - // query (optional) - @"(?:\?(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?" + - // fragment (optional) - @"(?:#(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?)", - RegexOptions.IgnoreCase); - - // 00:00:000 (1,2,3) - test - private static readonly Regex time_regex = new Regex(@"\d\d:\d\d:\d\d\d? [^-]*"); - - // #osu - private static readonly Regex channel_regex = new Regex(@"(#[a-zA-Z]+[a-zA-Z0-9]+)"); - - // Unicode emojis - private static readonly Regex emoji_regex = new Regex(@"(\uD83D[\uDC00-\uDE4F])"); - - private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null) - { - int captureOffset = 0; - foreach (Match m in regex.Matches(result.Text, startIndex)) - { - var index = m.Index - captureOffset; - - var displayText = string.Format(display, - m.Groups[0], - m.Groups.Count > 1 ? m.Groups[1].Value : "", - m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim(); - - var linkText = string.Format(link, - m.Groups[0], - m.Groups.Count > 1 ? m.Groups[1].Value : "", - m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim(); - - if (displayText.Length == 0 || linkText.Length == 0) continue; - - // Check for encapsulated links - if (result.Links.Find(l => l.Index <= index && l.Index + l.Length >= index + m.Length || index <= l.Index && index + m.Length >= l.Index + l.Length) == null) - { - result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText); - - //since we just changed the line display text, offset any already processed links. - result.Links.ForEach(l => l.Index -= l.Index > index ? m.Length - displayText.Length : 0); - - var details = getLinkDetails(linkText); - result.Links.Add(new Link(linkText, index, displayText.Length, linkActionOverride ?? details.Action, details.Argument)); - - //adjust the offset for processing the current matches group. - captureOffset += m.Length - displayText.Length; - } - } - } - - private static void handleAdvanced(Regex regex, MessageFormatterResult result, int startIndex = 0) - { - foreach (Match m in regex.Matches(result.Text, startIndex)) - { - var index = m.Index; - var link = m.Groups["link"].Value; - var indexLength = link.Length; - - var details = getLinkDetails(link); - result.Links.Add(new Link(link, index, indexLength, details.Action, details.Argument)); - } - } - - private static LinkDetails getLinkDetails(string url) - { - var args = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); - args[0] = args[0].TrimEnd(':'); - - switch (args[0]) - { - case "http": - case "https": - // length > 3 since all these links need another argument to work - if (args.Length > 3 && (args[1] == "osu.ppy.sh" || args[1] == "new.ppy.sh")) - { - switch (args[2]) - { - case "b": - case "beatmaps": - return new LinkDetails(LinkAction.OpenBeatmap, args[3]); - case "s": - case "beatmapsets": - case "d": - return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]); - case "u": - return new LinkDetails(LinkAction.OpenUserProfile, args[3]); - } - } - - return new LinkDetails(LinkAction.External, null); - case "osu": - // every internal link also needs some kind of argument - if (args.Length < 3) - return new LinkDetails(LinkAction.External, null); - - LinkAction linkType; - switch (args[1]) - { - case "chan": - linkType = LinkAction.OpenChannel; - break; - case "edit": - linkType = LinkAction.OpenEditorTimestamp; - break; - case "b": - linkType = LinkAction.OpenBeatmap; - break; - case "s": - case "dl": - linkType = LinkAction.OpenBeatmapSet; - break; - case "spectate": - linkType = LinkAction.Spectate; - break; - case "u": - linkType = LinkAction.OpenUserProfile; - break; - default: - linkType = LinkAction.External; - break; - } - - return new LinkDetails(linkType, args[2]); - case "osump": - return new LinkDetails(LinkAction.JoinMultiplayerMatch, args[1]); - default: - return new LinkDetails(LinkAction.External, null); - } - } - - private static MessageFormatterResult format(string toFormat, int startIndex = 0, int space = 3) - { - var result = new MessageFormatterResult(toFormat); - - // handle the [link display] format - handleMatches(new_link_regex, "{2}", "{1}", result, startIndex); - - // handle the standard markdown []() format - handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex); - - // handle the ()[] link format - handleMatches(old_link_regex, "{1}", "{2}", result, startIndex); - - // handle wiki links - handleMatches(wiki_regex, "{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex); - - // handle bare links - handleAdvanced(advanced_link_regex, result, startIndex); - - // handle editor times - handleMatches(time_regex, "{0}", "osu://edit/{0}", result, startIndex, LinkAction.OpenEditorTimestamp); - - // handle channels - handleMatches(channel_regex, "{0}", "osu://chan/{0}", result, startIndex, LinkAction.OpenChannel); - - var empty = ""; - while (space-- > 0) - empty += "\0"; - - handleMatches(emoji_regex, empty, "{0}", result, startIndex); - - return result; - } - - public static Message FormatMessage(Message inputMessage) - { - var result = format(inputMessage.Content); - - inputMessage.DisplayContent = result.Text; - - // Sometimes, regex matches are not in order - result.Links.Sort(); - inputMessage.Links = result.Links; - return inputMessage; - } - - public static MessageFormatterResult FormatText(string text) - { - var result = format(text); - - result.Links.Sort(); - - return result; - } - - public class MessageFormatterResult - { - public List Links = new List(); - public string Text; - public string OriginalText; - - public MessageFormatterResult(string text) - { - OriginalText = Text = text; - } - } - - public class LinkDetails - { - public LinkAction Action; - public string Argument; - - public LinkDetails(LinkAction action, string argument) - { - Action = action; - Argument = argument; - } - } - } - - public enum LinkAction - { - External, - OpenBeatmap, - OpenBeatmapSet, - OpenChannel, - OpenEditorTimestamp, - JoinMultiplayerMatch, - Spectate, - OpenUserProfile, - } - - public class Link : IComparable - { - public string Url; - public int Index; - public int Length; - public LinkAction Action; - public string Argument; - - public Link(string url, int startIndex, int length, LinkAction action, string argument) - { - Url = url; - Index = startIndex; - Length = length; - Action = action; - Argument = argument; - } - - public int CompareTo(Link otherLink) => Index > otherLink.Index ? 1 : -1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace osu.Game.Online.Chat +{ + public static class MessageFormatter + { + // [[Performance Points]] -> wiki:Performance Points (https://osu.ppy.sh/wiki/Performance_Points) + private static readonly Regex wiki_regex = new Regex(@"\[\[([^\]]+)\]\]"); + + // (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234) + private static readonly Regex old_link_regex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]"); + + // [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234) + private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ([^\[\]]*(((?\[)[^\[\]]*)+((?\])[^\[\]]*)+)*(?(open)(?!)))\]"); + + // [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format + private static readonly Regex markdown_link_regex = new Regex(@"\[([^\]]*)\]\(([a-z]+://[^ ]+)\)"); + + // advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used + // This is in the format (, [optional]): + // http[s]://.[:port][/path][?query][#fragment] + private static readonly Regex advanced_link_regex = new Regex( + // protocol + @"(?[a-z]*?:\/\/" + + // domain + tld + @"(?(?:[a-z0-9]\.|[a-z0-9][a-z0-9-]*[a-z0-9]\.)*[a-z0-9-]*[a-z0-9]" + + // port (optional) + @"(?::\d+)?)" + + // path (optional) + @"(?(?:(?:\/+(?:[a-z0-9$_\.\+!\*\',;:\(\)@&~=-]|%[0-9a-f]{2})*)*" + + // query (optional) + @"(?:\?(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?" + + // fragment (optional) + @"(?:#(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?)", + RegexOptions.IgnoreCase); + + // 00:00:000 (1,2,3) - test + private static readonly Regex time_regex = new Regex(@"\d\d:\d\d:\d\d\d? [^-]*"); + + // #osu + private static readonly Regex channel_regex = new Regex(@"(#[a-zA-Z]+[a-zA-Z0-9]+)"); + + // Unicode emojis + private static readonly Regex emoji_regex = new Regex(@"(\uD83D[\uDC00-\uDE4F])"); + + private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null) + { + int captureOffset = 0; + foreach (Match m in regex.Matches(result.Text, startIndex)) + { + var index = m.Index - captureOffset; + + var displayText = string.Format(display, + m.Groups[0], + m.Groups.Count > 1 ? m.Groups[1].Value : "", + m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim(); + + var linkText = string.Format(link, + m.Groups[0], + m.Groups.Count > 1 ? m.Groups[1].Value : "", + m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim(); + + if (displayText.Length == 0 || linkText.Length == 0) continue; + + // Check for encapsulated links + if (result.Links.Find(l => l.Index <= index && l.Index + l.Length >= index + m.Length || index <= l.Index && index + m.Length >= l.Index + l.Length) == null) + { + result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText); + + //since we just changed the line display text, offset any already processed links. + result.Links.ForEach(l => l.Index -= l.Index > index ? m.Length - displayText.Length : 0); + + var details = getLinkDetails(linkText); + result.Links.Add(new Link(linkText, index, displayText.Length, linkActionOverride ?? details.Action, details.Argument)); + + //adjust the offset for processing the current matches group. + captureOffset += m.Length - displayText.Length; + } + } + } + + private static void handleAdvanced(Regex regex, MessageFormatterResult result, int startIndex = 0) + { + foreach (Match m in regex.Matches(result.Text, startIndex)) + { + var index = m.Index; + var link = m.Groups["link"].Value; + var indexLength = link.Length; + + var details = getLinkDetails(link); + result.Links.Add(new Link(link, index, indexLength, details.Action, details.Argument)); + } + } + + private static LinkDetails getLinkDetails(string url) + { + var args = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + args[0] = args[0].TrimEnd(':'); + + switch (args[0]) + { + case "http": + case "https": + // length > 3 since all these links need another argument to work + if (args.Length > 3 && (args[1] == "osu.ppy.sh" || args[1] == "new.ppy.sh")) + { + switch (args[2]) + { + case "b": + case "beatmaps": + return new LinkDetails(LinkAction.OpenBeatmap, args[3]); + case "s": + case "beatmapsets": + case "d": + return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]); + case "u": + return new LinkDetails(LinkAction.OpenUserProfile, args[3]); + } + } + + return new LinkDetails(LinkAction.External, null); + case "osu": + // every internal link also needs some kind of argument + if (args.Length < 3) + return new LinkDetails(LinkAction.External, null); + + LinkAction linkType; + switch (args[1]) + { + case "chan": + linkType = LinkAction.OpenChannel; + break; + case "edit": + linkType = LinkAction.OpenEditorTimestamp; + break; + case "b": + linkType = LinkAction.OpenBeatmap; + break; + case "s": + case "dl": + linkType = LinkAction.OpenBeatmapSet; + break; + case "spectate": + linkType = LinkAction.Spectate; + break; + case "u": + linkType = LinkAction.OpenUserProfile; + break; + default: + linkType = LinkAction.External; + break; + } + + return new LinkDetails(linkType, args[2]); + case "osump": + return new LinkDetails(LinkAction.JoinMultiplayerMatch, args[1]); + default: + return new LinkDetails(LinkAction.External, null); + } + } + + private static MessageFormatterResult format(string toFormat, int startIndex = 0, int space = 3) + { + var result = new MessageFormatterResult(toFormat); + + // handle the [link display] format + handleMatches(new_link_regex, "{2}", "{1}", result, startIndex); + + // handle the standard markdown []() format + handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex); + + // handle the ()[] link format + handleMatches(old_link_regex, "{1}", "{2}", result, startIndex); + + // handle wiki links + handleMatches(wiki_regex, "{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex); + + // handle bare links + handleAdvanced(advanced_link_regex, result, startIndex); + + // handle editor times + handleMatches(time_regex, "{0}", "osu://edit/{0}", result, startIndex, LinkAction.OpenEditorTimestamp); + + // handle channels + handleMatches(channel_regex, "{0}", "osu://chan/{0}", result, startIndex, LinkAction.OpenChannel); + + var empty = ""; + while (space-- > 0) + empty += "\0"; + + handleMatches(emoji_regex, empty, "{0}", result, startIndex); + + return result; + } + + public static Message FormatMessage(Message inputMessage) + { + var result = format(inputMessage.Content); + + inputMessage.DisplayContent = result.Text; + + // Sometimes, regex matches are not in order + result.Links.Sort(); + inputMessage.Links = result.Links; + return inputMessage; + } + + public static MessageFormatterResult FormatText(string text) + { + var result = format(text); + + result.Links.Sort(); + + return result; + } + + public class MessageFormatterResult + { + public List Links = new List(); + public string Text; + public string OriginalText; + + public MessageFormatterResult(string text) + { + OriginalText = Text = text; + } + } + + public class LinkDetails + { + public LinkAction Action; + public string Argument; + + public LinkDetails(LinkAction action, string argument) + { + Action = action; + Argument = argument; + } + } + } + + public enum LinkAction + { + External, + OpenBeatmap, + OpenBeatmapSet, + OpenChannel, + OpenEditorTimestamp, + JoinMultiplayerMatch, + Spectate, + OpenUserProfile, + } + + public class Link : IComparable + { + public string Url; + public int Index; + public int Length; + public LinkAction Action; + public string Argument; + + public Link(string url, int startIndex, int length, LinkAction action, string argument) + { + Url = url; + Index = startIndex; + Length = length; + Action = action; + Argument = argument; + } + + public int CompareTo(Link otherLink) => Index > otherLink.Index ? 1 : -1; + } +} diff --git a/osu.Game/Online/Multiplayer/GameType.cs b/osu.Game/Online/Multiplayer/GameType.cs index 01588b05d3..571d3df681 100644 --- a/osu.Game/Online/Multiplayer/GameType.cs +++ b/osu.Game/Online/Multiplayer/GameType.cs @@ -1,146 +1,146 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; - -namespace osu.Game.Online.Multiplayer -{ - public abstract class GameType - { - public abstract string Name { get; } - public abstract Drawable GetIcon(OsuColour colours, float size); - } - - public class GameTypeTag : GameType - { - public override string Name => "Tag"; - public override Drawable GetIcon(OsuColour colours, float size) - { - return new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.fa_refresh, - Size = new Vector2(size), - Colour = colours.Blue, - Shadow = false, - }; - } - } - - public class GameTypeVersus : GameType - { - public override string Name => "Versus"; - public override Drawable GetIcon(OsuColour colours, float size) - { - return new VersusRow(colours.Blue, colours.Blue, size * 0.6f) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - } - } - - public class GameTypeTagTeam : GameType - { - public override string Name => "Tag Team"; - public override Drawable GetIcon(OsuColour colours, float size) - { - return new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2f), - Children = new[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_refresh, - Size = new Vector2(size * 0.75f), - Colour = colours.Blue, - Shadow = false, - }, - new SpriteIcon - { - Icon = FontAwesome.fa_refresh, - Size = new Vector2(size * 0.75f), - Colour = colours.Pink, - Shadow = false, - }, - }, - }; - } - } - - public class GameTypeTeamVersus : GameType - { - public override string Name => "Team Versus"; - public override Drawable GetIcon(OsuColour colours, float size) - { - return new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2f), - Children = new[] - { - new VersusRow(colours.Blue, colours.Pink, size * 0.5f), - new VersusRow(colours.Blue, colours.Pink, size * 0.5f), - }, - }; - } - } - - public class VersusRow : FillFlowContainer - { - public VersusRow(Color4 first, Color4 second, float size) - { - var triangleSize = new Vector2(size); - AutoSizeAxes = Axes.Both; - Spacing = new Vector2(2f, 0f); - - Children = new[] - { - new Container - { - Size = triangleSize, - Colour = first, - Children = new[] - { - new EquilateralTriangle - { - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Rotation = 90, - EdgeSmoothness = new Vector2(1f), - }, - }, - }, - new Container - { - Size = triangleSize, - Colour = second, - Children = new[] - { - new EquilateralTriangle - { - Anchor = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Rotation = -90, - EdgeSmoothness = new Vector2(1f), - }, - }, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; + +namespace osu.Game.Online.Multiplayer +{ + public abstract class GameType + { + public abstract string Name { get; } + public abstract Drawable GetIcon(OsuColour colours, float size); + } + + public class GameTypeTag : GameType + { + public override string Name => "Tag"; + public override Drawable GetIcon(OsuColour colours, float size) + { + return new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_refresh, + Size = new Vector2(size), + Colour = colours.Blue, + Shadow = false, + }; + } + } + + public class GameTypeVersus : GameType + { + public override string Name => "Versus"; + public override Drawable GetIcon(OsuColour colours, float size) + { + return new VersusRow(colours.Blue, colours.Blue, size * 0.6f) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + } + + public class GameTypeTagTeam : GameType + { + public override string Name => "Tag Team"; + public override Drawable GetIcon(OsuColour colours, float size) + { + return new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2f), + Children = new[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_refresh, + Size = new Vector2(size * 0.75f), + Colour = colours.Blue, + Shadow = false, + }, + new SpriteIcon + { + Icon = FontAwesome.fa_refresh, + Size = new Vector2(size * 0.75f), + Colour = colours.Pink, + Shadow = false, + }, + }, + }; + } + } + + public class GameTypeTeamVersus : GameType + { + public override string Name => "Team Versus"; + public override Drawable GetIcon(OsuColour colours, float size) + { + return new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2f), + Children = new[] + { + new VersusRow(colours.Blue, colours.Pink, size * 0.5f), + new VersusRow(colours.Blue, colours.Pink, size * 0.5f), + }, + }; + } + } + + public class VersusRow : FillFlowContainer + { + public VersusRow(Color4 first, Color4 second, float size) + { + var triangleSize = new Vector2(size); + AutoSizeAxes = Axes.Both; + Spacing = new Vector2(2f, 0f); + + Children = new[] + { + new Container + { + Size = triangleSize, + Colour = first, + Children = new[] + { + new EquilateralTriangle + { + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Rotation = 90, + EdgeSmoothness = new Vector2(1f), + }, + }, + }, + new Container + { + Size = triangleSize, + Colour = second, + Children = new[] + { + new EquilateralTriangle + { + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Rotation = -90, + EdgeSmoothness = new Vector2(1f), + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 1971f9941c..f1c23e9e84 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Game.Beatmaps; -using osu.Game.Users; - -namespace osu.Game.Online.Multiplayer -{ - public class Room - { - public Bindable Name = new Bindable(); - public Bindable Host = new Bindable(); - public Bindable Status = new Bindable(); - public Bindable Type = new Bindable(); - public Bindable Beatmap = new Bindable(); - public Bindable MaxParticipants = new Bindable(); - public Bindable Participants = new Bindable(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Game.Beatmaps; +using osu.Game.Users; + +namespace osu.Game.Online.Multiplayer +{ + public class Room + { + public Bindable Name = new Bindable(); + public Bindable Host = new Bindable(); + public Bindable Status = new Bindable(); + public Bindable Type = new Bindable(); + public Bindable Beatmap = new Bindable(); + public Bindable MaxParticipants = new Bindable(); + public Bindable Participants = new Bindable(); + } +} diff --git a/osu.Game/Online/Multiplayer/RoomStatus.cs b/osu.Game/Online/Multiplayer/RoomStatus.cs index 1d82c8f6a8..6b82e5f059 100644 --- a/osu.Game/Online/Multiplayer/RoomStatus.cs +++ b/osu.Game/Online/Multiplayer/RoomStatus.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Game.Graphics; - -namespace osu.Game.Online.Multiplayer -{ - public abstract class RoomStatus - { - public abstract string Message { get; } - public abstract Color4 GetAppropriateColour(OsuColour colours); - } - - public class RoomStatusOpen : RoomStatus - { - public override string Message => @"Welcoming Players"; - public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight; - } - - public class RoomStatusPlaying : RoomStatus - { - public override string Message => @"Now Playing"; - public override Color4 GetAppropriateColour(OsuColour colours) => colours.Purple; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Game.Graphics; + +namespace osu.Game.Online.Multiplayer +{ + public abstract class RoomStatus + { + public abstract string Message { get; } + public abstract Color4 GetAppropriateColour(OsuColour colours); + } + + public class RoomStatusOpen : RoomStatus + { + public override string Message => @"Welcoming Players"; + public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight; + } + + public class RoomStatusPlaying : RoomStatus + { + public override string Message => @"Now Playing"; + public override Color4 GetAppropriateColour(OsuColour colours) => colours.Purple; + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2f6f8ff348..d1cf372067 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1,551 +1,551 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Configuration; -using osu.Framework.Screens; -using osu.Game.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Overlays; -using osu.Framework.Logging; -using osu.Framework.Allocation; -using osu.Game.Overlays.Toolbar; -using osu.Game.Screens; -using osu.Game.Screens.Menu; -using OpenTK; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using osu.Framework.Audio; -using osu.Framework.Input; -using osu.Framework.Input.Bindings; -using osu.Framework.Platform; -using osu.Framework.Threading; -using osu.Game.Graphics; -using osu.Game.Rulesets.Scoring; -using osu.Game.Overlays.Notifications; -using osu.Game.Rulesets; -using osu.Game.Screens.Play; -using osu.Game.Input.Bindings; -using osu.Game.Rulesets.Mods; -using osu.Game.Skinning; -using OpenTK.Graphics; -using osu.Game.Overlays.Volume; - -namespace osu.Game -{ - /// - /// The full osu! experience. Builds on top of to add menus and binding logic - /// for initial components that are generally retrieved via DI. - /// - public class OsuGame : OsuGameBase, IKeyBindingHandler - { - public Toolbar Toolbar; - - private ChatOverlay chat; - - private MusicController musicController; - - private NotificationOverlay notifications; - - private DialogOverlay dialogOverlay; - - private DirectOverlay direct; - - private SocialOverlay social; - - private UserProfileOverlay userProfile; - - private BeatmapSetOverlay beatmapSetOverlay; - - public virtual Storage GetStorageForStableInstall() => null; - - private Intro intro - { - get - { - Screen s = screenStack; - while (s != null && !(s is Intro)) - s = s.ChildScreen; - return s as Intro; - } - } - - public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight; - - public readonly BindableBool ShowOverlays = new BindableBool(); - - private OsuScreen screenStack; - - private VolumeOverlay volume; - private OnScreenDisplay onscreenDisplay; - - private Bindable configRuleset; - public Bindable Ruleset = new Bindable(); - - private Bindable configSkin; - - private readonly string[] args; - - private SettingsOverlay settings; - - // todo: move this to SongSelect once Screen has the ability to unsuspend. - public readonly Bindable> SelectedMods = new Bindable>(new List()); - - public OsuGame(string[] args = null) - { - this.args = args; - } - - public void ToggleSettings() => settings.ToggleVisibility(); - - public void ToggleDirect() => direct.ToggleVisibility(); - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => - dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); - - [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig) - { - this.frameworkConfig = frameworkConfig; - - ScoreStore.ScoreImported += score => Schedule(() => LoadScore(score)); - - if (!Host.IsPrimaryInstance) - { - Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); - Environment.Exit(0); - } - - if (args?.Length > 0) - { - var paths = args.Where(a => !a.StartsWith(@"-")); - - Task.Run(() => Import(paths.ToArray())); - } - - dependencies.CacheAs(this); - - // bind config int to database RulesetInfo - configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); - Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First(); - Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0; - - // bind config int to database SkinInfo - configSkin = LocalConfig.GetBindable(OsuSetting.Skin); - SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID; - configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default; - configSkin.TriggerChange(); - - LocalConfig.BindWith(OsuSetting.VolumeInactive, inactiveVolumeAdjust); - } - - private ScheduledDelegate scoreLoad; - - /// - /// Open chat to a channel matching the provided name, if present. - /// - /// The name of the channel. - public void OpenChannel(string channelName) => chat.OpenChannel(chat.AvailableChannels.Find(c => c.Name == channelName)); - - /// - /// Show a beatmap set as an overlay. - /// - /// The set to display. - public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId); - - /// - /// Show a user's profile as an overlay. - /// - /// The user to display. - public void ShowUser(long userId) => userProfile.ShowUser(userId); - - protected void LoadScore(Score s) - { - scoreLoad?.Cancel(); - - var menu = intro.ChildScreen; - - if (menu == null) - { - scoreLoad = Schedule(() => LoadScore(s)); - return; - } - - if (!menu.IsCurrentScreen) - { - menu.MakeCurrent(); - this.Delay(500).Schedule(() => LoadScore(s), out scoreLoad); - return; - } - - if (s.Beatmap == null) - { - notifications.Post(new SimpleNotification - { - Text = @"Tried to load a score for a beatmap we don't have!", - Icon = FontAwesome.fa_life_saver, - }); - return; - } - - Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); - - menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // The next time this is updated is in UpdateAfterChildren, which occurs too late and results - // in the cursor being shown for a few frames during the intro. - // This prevents the cursor from showing until we have a screen with CursorVisible = true - CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; - - // hook up notifications to components. - SkinManager.PostNotification = n => notifications?.Post(n); - BeatmapManager.PostNotification = n => notifications?.Post(n); - - BeatmapManager.GetStableStorage = GetStorageForStableInstall; - - AddRange(new Drawable[] - { - new VolumeControlReceptor - { - RelativeSizeAxes = Axes.Both, - ActionRequested = action => volume.Adjust(action) - }, - mainContent = new Container { RelativeSizeAxes = Axes.Both }, - overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, - }); - - loadComponentSingleFile(screenStack = new Loader(), d => - { - screenStack.ModePushed += screenAdded; - screenStack.Exited += screenRemoved; - mainContent.Add(screenStack); - }); - - loadComponentSingleFile(Toolbar = new Toolbar - { - Depth = -5, - OnHome = delegate - { - hideAllOverlays(); - intro?.ChildScreen?.MakeCurrent(); - }, - }, overlayContent.Add); - - loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); - loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); - loadComponentSingleFile(new ScreenshotManager(), Add); - - //overlay elements - loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); - loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); - loadComponentSingleFile(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); - loadComponentSingleFile(settings = new MainSettings - { - GetToolbarHeight = () => ToolbarOffset, - Depth = -1 - }, overlayContent.Add); - loadComponentSingleFile(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); - loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); - loadComponentSingleFile(musicController = new MusicController - { - Depth = -4, - Position = new Vector2(0, Toolbar.HEIGHT), - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }, overlayContent.Add); - - loadComponentSingleFile(notifications = new NotificationOverlay - { - GetToolbarHeight = () => ToolbarOffset, - Depth = -4, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }, overlayContent.Add); - - loadComponentSingleFile(dialogOverlay = new DialogOverlay - { - Depth = -6, - }, overlayContent.Add); - - forwardLoggedErrorsToNotifications(); - - dependencies.Cache(settings); - dependencies.Cache(onscreenDisplay); - dependencies.Cache(social); - dependencies.Cache(direct); - dependencies.Cache(chat); - dependencies.Cache(userProfile); - dependencies.Cache(musicController); - dependencies.Cache(beatmapSetOverlay); - dependencies.Cache(notifications); - dependencies.Cache(dialogOverlay); - - // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; - foreach (var overlay in singleDisplayOverlays) - { - overlay.StateChanged += state => - { - if (state == Visibility.Hidden) return; - - foreach (var c in singleDisplayOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } - }; - } - - var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; - foreach (var overlay in singleDisplaySideOverlays) - { - overlay.StateChanged += state => - { - if (state == Visibility.Hidden) return; - - foreach (var c in singleDisplaySideOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } - }; - } - - // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. - var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; - foreach (var overlay in informationalOverlays) - { - overlay.StateChanged += state => - { - if (state == Visibility.Hidden) return; - - foreach (var c in informationalOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } - }; - } - - void updateScreenOffset() - { - float offset = 0; - - if (settings.State == Visibility.Visible) - offset += ToolbarButton.WIDTH / 2; - if (notifications.State == Visibility.Visible) - offset -= ToolbarButton.WIDTH / 2; - - screenStack.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); - } - - settings.StateChanged += _ => updateScreenOffset(); - notifications.StateChanged += _ => updateScreenOffset(); - - notifications.Enabled.BindTo(ShowOverlays); - - ShowOverlays.ValueChanged += show => - { - //central game screen change logic. - if (!show) - { - hideAllOverlays(); - musicController.State = Visibility.Hidden; - Toolbar.State = Visibility.Hidden; - } - else - Toolbar.State = Visibility.Visible; - }; - } - - private void forwardLoggedErrorsToNotifications() - { - int recentErrorCount = 0; - - const double debounce = 5000; - - Logger.NewEntry += entry => - { - if (entry.Level < LogLevel.Error || entry.Target == null) return; - - if (recentErrorCount < 2) - { - notifications.Post(new SimpleNotification - { - Icon = FontAwesome.fa_bomb, - Text = (recentErrorCount == 0 ? entry.Message : "Subsequent errors occurred and have been logged.") + "\nClick to view log files.", - Activated = () => - { - Host.Storage.GetStorageForDirectory("logs").OpenInNativeExplorer(); - return true; - } - }); - } - - Interlocked.Increment(ref recentErrorCount); - - Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentErrorCount), debounce); - }; - } - - private Task asyncLoadStream; - private int visibleOverlayCount; - - private void loadComponentSingleFile(T d, Action add) - where T : Drawable - { - var focused = d as FocusedOverlayContainer; - if (focused != null) - { - focused.StateChanged += s => - { - visibleOverlayCount += s == Visibility.Visible ? 1 : -1; - screenStack.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint); - }; - } - - // schedule is here to ensure that all component loads are done after LoadComplete is run (and thus all dependencies are cached). - // with some better organisation of LoadComplete to do construction and dependency caching in one step, followed by calls to loadComponentSingleFile, - // we could avoid the need for scheduling altogether. - Schedule(() => { asyncLoadStream = asyncLoadStream?.ContinueWith(t => LoadComponentAsync(d, add).Wait()) ?? LoadComponentAsync(d, add); }); - } - - public bool OnPressed(GlobalAction action) - { - if (intro == null) return false; - - switch (action) - { - case GlobalAction.ToggleChat: - chat.ToggleVisibility(); - return true; - case GlobalAction.ToggleSocial: - social.ToggleVisibility(); - return true; - case GlobalAction.ResetInputSettings: - var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); - - sensitivity.Disabled = false; - sensitivity.Value = 1; - sensitivity.Disabled = true; - - frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); - frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault(); - return true; - case GlobalAction.ToggleToolbar: - Toolbar.ToggleVisibility(); - return true; - case GlobalAction.ToggleSettings: - settings.ToggleVisibility(); - return true; - case GlobalAction.ToggleDirect: - direct.ToggleVisibility(); - return true; - } - - return false; - } - - private readonly BindableDouble inactiveVolumeAdjust = new BindableDouble(); - - protected override void OnDeactivated() - { - base.OnDeactivated(); - Audio.AddAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust); - } - - protected override void OnActivated() - { - base.OnActivated(); - Audio.RemoveAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust); - } - - public bool OnReleased(GlobalAction action) => false; - - private Container mainContent; - - private Container overlayContent; - - private OsuScreen currentScreen; - private FrameworkConfigManager frameworkConfig; - - private void hideAllOverlays() - { - settings.State = Visibility.Hidden; - chat.State = Visibility.Hidden; - direct.State = Visibility.Hidden; - social.State = Visibility.Hidden; - userProfile.State = Visibility.Hidden; - notifications.State = Visibility.Hidden; - } - - protected override bool OnExiting() - { - if (screenStack.ChildScreen == null) return false; - - if (intro == null) return true; - - if (!intro.DidLoadMenu || intro.ChildScreen != null) - { - Scheduler.Add(intro.MakeCurrent); - return true; - } - - return base.OnExiting(); - } - - /// - /// Use to programatically exit the game as if the user was triggering via alt-f4. - /// Will keep persisting until an exit occurs (exit may be blocked multiple times). - /// - public void GracefullyExit() - { - if (!OnExiting()) - Exit(); - else - Scheduler.AddDelayed(GracefullyExit, 2000); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - // we only want to apply these restrictions when we are inside a screen stack. - // the use case for not applying is in visual/unit tests. - bool applyRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false; - - Ruleset.Disabled = applyRestrictions; - Beatmap.Disabled = applyRestrictions; - - mainContent.Padding = new MarginPadding { Top = ToolbarOffset }; - - CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; - } - - private void screenAdded(Screen newScreen) - { - currentScreen = (OsuScreen)newScreen; - - newScreen.ModePushed += screenAdded; - newScreen.Exited += screenRemoved; - } - - private void screenRemoved(Screen newScreen) - { - currentScreen = (OsuScreen)newScreen; - - if (newScreen == null) - Exit(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Configuration; +using osu.Framework.Screens; +using osu.Game.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; +using osu.Framework.Logging; +using osu.Framework.Allocation; +using osu.Game.Overlays.Toolbar; +using osu.Game.Screens; +using osu.Game.Screens.Menu; +using OpenTK; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Audio; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Platform; +using osu.Framework.Threading; +using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; +using osu.Game.Overlays.Notifications; +using osu.Game.Rulesets; +using osu.Game.Screens.Play; +using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Mods; +using osu.Game.Skinning; +using OpenTK.Graphics; +using osu.Game.Overlays.Volume; + +namespace osu.Game +{ + /// + /// The full osu! experience. Builds on top of to add menus and binding logic + /// for initial components that are generally retrieved via DI. + /// + public class OsuGame : OsuGameBase, IKeyBindingHandler + { + public Toolbar Toolbar; + + private ChatOverlay chat; + + private MusicController musicController; + + private NotificationOverlay notifications; + + private DialogOverlay dialogOverlay; + + private DirectOverlay direct; + + private SocialOverlay social; + + private UserProfileOverlay userProfile; + + private BeatmapSetOverlay beatmapSetOverlay; + + public virtual Storage GetStorageForStableInstall() => null; + + private Intro intro + { + get + { + Screen s = screenStack; + while (s != null && !(s is Intro)) + s = s.ChildScreen; + return s as Intro; + } + } + + public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight; + + public readonly BindableBool ShowOverlays = new BindableBool(); + + private OsuScreen screenStack; + + private VolumeOverlay volume; + private OnScreenDisplay onscreenDisplay; + + private Bindable configRuleset; + public Bindable Ruleset = new Bindable(); + + private Bindable configSkin; + + private readonly string[] args; + + private SettingsOverlay settings; + + // todo: move this to SongSelect once Screen has the ability to unsuspend. + public readonly Bindable> SelectedMods = new Bindable>(new List()); + + public OsuGame(string[] args = null) + { + this.args = args; + } + + public void ToggleSettings() => settings.ToggleVisibility(); + + public void ToggleDirect() => direct.ToggleVisibility(); + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => + dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager frameworkConfig) + { + this.frameworkConfig = frameworkConfig; + + ScoreStore.ScoreImported += score => Schedule(() => LoadScore(score)); + + if (!Host.IsPrimaryInstance) + { + Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); + Environment.Exit(0); + } + + if (args?.Length > 0) + { + var paths = args.Where(a => !a.StartsWith(@"-")); + + Task.Run(() => Import(paths.ToArray())); + } + + dependencies.CacheAs(this); + + // bind config int to database RulesetInfo + configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); + Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First(); + Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0; + + // bind config int to database SkinInfo + configSkin = LocalConfig.GetBindable(OsuSetting.Skin); + SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID; + configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default; + configSkin.TriggerChange(); + + LocalConfig.BindWith(OsuSetting.VolumeInactive, inactiveVolumeAdjust); + } + + private ScheduledDelegate scoreLoad; + + /// + /// Open chat to a channel matching the provided name, if present. + /// + /// The name of the channel. + public void OpenChannel(string channelName) => chat.OpenChannel(chat.AvailableChannels.Find(c => c.Name == channelName)); + + /// + /// Show a beatmap set as an overlay. + /// + /// The set to display. + public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId); + + /// + /// Show a user's profile as an overlay. + /// + /// The user to display. + public void ShowUser(long userId) => userProfile.ShowUser(userId); + + protected void LoadScore(Score s) + { + scoreLoad?.Cancel(); + + var menu = intro.ChildScreen; + + if (menu == null) + { + scoreLoad = Schedule(() => LoadScore(s)); + return; + } + + if (!menu.IsCurrentScreen) + { + menu.MakeCurrent(); + this.Delay(500).Schedule(() => LoadScore(s), out scoreLoad); + return; + } + + if (s.Beatmap == null) + { + notifications.Post(new SimpleNotification + { + Text = @"Tried to load a score for a beatmap we don't have!", + Icon = FontAwesome.fa_life_saver, + }); + return; + } + + Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); + + menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // The next time this is updated is in UpdateAfterChildren, which occurs too late and results + // in the cursor being shown for a few frames during the intro. + // This prevents the cursor from showing until we have a screen with CursorVisible = true + CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; + + // hook up notifications to components. + SkinManager.PostNotification = n => notifications?.Post(n); + BeatmapManager.PostNotification = n => notifications?.Post(n); + + BeatmapManager.GetStableStorage = GetStorageForStableInstall; + + AddRange(new Drawable[] + { + new VolumeControlReceptor + { + RelativeSizeAxes = Axes.Both, + ActionRequested = action => volume.Adjust(action) + }, + mainContent = new Container { RelativeSizeAxes = Axes.Both }, + overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, + }); + + loadComponentSingleFile(screenStack = new Loader(), d => + { + screenStack.ModePushed += screenAdded; + screenStack.Exited += screenRemoved; + mainContent.Add(screenStack); + }); + + loadComponentSingleFile(Toolbar = new Toolbar + { + Depth = -5, + OnHome = delegate + { + hideAllOverlays(); + intro?.ChildScreen?.MakeCurrent(); + }, + }, overlayContent.Add); + + loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); + loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); + loadComponentSingleFile(new ScreenshotManager(), Add); + + //overlay elements + loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); + loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); + loadComponentSingleFile(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); + loadComponentSingleFile(settings = new MainSettings + { + GetToolbarHeight = () => ToolbarOffset, + Depth = -1 + }, overlayContent.Add); + loadComponentSingleFile(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); + loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); + loadComponentSingleFile(musicController = new MusicController + { + Depth = -4, + Position = new Vector2(0, Toolbar.HEIGHT), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, overlayContent.Add); + + loadComponentSingleFile(notifications = new NotificationOverlay + { + GetToolbarHeight = () => ToolbarOffset, + Depth = -4, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, overlayContent.Add); + + loadComponentSingleFile(dialogOverlay = new DialogOverlay + { + Depth = -6, + }, overlayContent.Add); + + forwardLoggedErrorsToNotifications(); + + dependencies.Cache(settings); + dependencies.Cache(onscreenDisplay); + dependencies.Cache(social); + dependencies.Cache(direct); + dependencies.Cache(chat); + dependencies.Cache(userProfile); + dependencies.Cache(musicController); + dependencies.Cache(beatmapSetOverlay); + dependencies.Cache(notifications); + dependencies.Cache(dialogOverlay); + + // ensure only one of these overlays are open at once. + var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; + foreach (var overlay in singleDisplayOverlays) + { + overlay.StateChanged += state => + { + if (state == Visibility.Hidden) return; + + foreach (var c in singleDisplayOverlays) + { + if (c == overlay) continue; + c.State = Visibility.Hidden; + } + }; + } + + var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; + foreach (var overlay in singleDisplaySideOverlays) + { + overlay.StateChanged += state => + { + if (state == Visibility.Hidden) return; + + foreach (var c in singleDisplaySideOverlays) + { + if (c == overlay) continue; + c.State = Visibility.Hidden; + } + }; + } + + // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. + var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; + foreach (var overlay in informationalOverlays) + { + overlay.StateChanged += state => + { + if (state == Visibility.Hidden) return; + + foreach (var c in informationalOverlays) + { + if (c == overlay) continue; + c.State = Visibility.Hidden; + } + }; + } + + void updateScreenOffset() + { + float offset = 0; + + if (settings.State == Visibility.Visible) + offset += ToolbarButton.WIDTH / 2; + if (notifications.State == Visibility.Visible) + offset -= ToolbarButton.WIDTH / 2; + + screenStack.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); + } + + settings.StateChanged += _ => updateScreenOffset(); + notifications.StateChanged += _ => updateScreenOffset(); + + notifications.Enabled.BindTo(ShowOverlays); + + ShowOverlays.ValueChanged += show => + { + //central game screen change logic. + if (!show) + { + hideAllOverlays(); + musicController.State = Visibility.Hidden; + Toolbar.State = Visibility.Hidden; + } + else + Toolbar.State = Visibility.Visible; + }; + } + + private void forwardLoggedErrorsToNotifications() + { + int recentErrorCount = 0; + + const double debounce = 5000; + + Logger.NewEntry += entry => + { + if (entry.Level < LogLevel.Error || entry.Target == null) return; + + if (recentErrorCount < 2) + { + notifications.Post(new SimpleNotification + { + Icon = FontAwesome.fa_bomb, + Text = (recentErrorCount == 0 ? entry.Message : "Subsequent errors occurred and have been logged.") + "\nClick to view log files.", + Activated = () => + { + Host.Storage.GetStorageForDirectory("logs").OpenInNativeExplorer(); + return true; + } + }); + } + + Interlocked.Increment(ref recentErrorCount); + + Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentErrorCount), debounce); + }; + } + + private Task asyncLoadStream; + private int visibleOverlayCount; + + private void loadComponentSingleFile(T d, Action add) + where T : Drawable + { + var focused = d as FocusedOverlayContainer; + if (focused != null) + { + focused.StateChanged += s => + { + visibleOverlayCount += s == Visibility.Visible ? 1 : -1; + screenStack.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint); + }; + } + + // schedule is here to ensure that all component loads are done after LoadComplete is run (and thus all dependencies are cached). + // with some better organisation of LoadComplete to do construction and dependency caching in one step, followed by calls to loadComponentSingleFile, + // we could avoid the need for scheduling altogether. + Schedule(() => { asyncLoadStream = asyncLoadStream?.ContinueWith(t => LoadComponentAsync(d, add).Wait()) ?? LoadComponentAsync(d, add); }); + } + + public bool OnPressed(GlobalAction action) + { + if (intro == null) return false; + + switch (action) + { + case GlobalAction.ToggleChat: + chat.ToggleVisibility(); + return true; + case GlobalAction.ToggleSocial: + social.ToggleVisibility(); + return true; + case GlobalAction.ResetInputSettings: + var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); + + sensitivity.Disabled = false; + sensitivity.Value = 1; + sensitivity.Disabled = true; + + frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); + frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault(); + return true; + case GlobalAction.ToggleToolbar: + Toolbar.ToggleVisibility(); + return true; + case GlobalAction.ToggleSettings: + settings.ToggleVisibility(); + return true; + case GlobalAction.ToggleDirect: + direct.ToggleVisibility(); + return true; + } + + return false; + } + + private readonly BindableDouble inactiveVolumeAdjust = new BindableDouble(); + + protected override void OnDeactivated() + { + base.OnDeactivated(); + Audio.AddAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust); + } + + protected override void OnActivated() + { + base.OnActivated(); + Audio.RemoveAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust); + } + + public bool OnReleased(GlobalAction action) => false; + + private Container mainContent; + + private Container overlayContent; + + private OsuScreen currentScreen; + private FrameworkConfigManager frameworkConfig; + + private void hideAllOverlays() + { + settings.State = Visibility.Hidden; + chat.State = Visibility.Hidden; + direct.State = Visibility.Hidden; + social.State = Visibility.Hidden; + userProfile.State = Visibility.Hidden; + notifications.State = Visibility.Hidden; + } + + protected override bool OnExiting() + { + if (screenStack.ChildScreen == null) return false; + + if (intro == null) return true; + + if (!intro.DidLoadMenu || intro.ChildScreen != null) + { + Scheduler.Add(intro.MakeCurrent); + return true; + } + + return base.OnExiting(); + } + + /// + /// Use to programatically exit the game as if the user was triggering via alt-f4. + /// Will keep persisting until an exit occurs (exit may be blocked multiple times). + /// + public void GracefullyExit() + { + if (!OnExiting()) + Exit(); + else + Scheduler.AddDelayed(GracefullyExit, 2000); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // we only want to apply these restrictions when we are inside a screen stack. + // the use case for not applying is in visual/unit tests. + bool applyRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false; + + Ruleset.Disabled = applyRestrictions; + Beatmap.Disabled = applyRestrictions; + + mainContent.Padding = new MarginPadding { Top = ToolbarOffset }; + + CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; + } + + private void screenAdded(Screen newScreen) + { + currentScreen = (OsuScreen)newScreen; + + newScreen.ModePushed += screenAdded; + newScreen.Exited += screenRemoved; + } + + private void screenRemoved(Screen newScreen) + { + currentScreen = (OsuScreen)newScreen; + + if (newScreen == null) + Exit(); + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 533a04286b..b76510ea1e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -1,260 +1,260 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Configuration; -using osu.Framework.Development; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.IO.Stores; -using osu.Framework.Platform; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Graphics.Cursor; -using osu.Game.Online.API; -using osu.Framework.Graphics.Performance; -using osu.Framework.Graphics.Textures; -using osu.Framework.Logging; -using osu.Game.Database; -using osu.Game.Graphics.Textures; -using osu.Game.Input; -using osu.Game.Input.Bindings; -using osu.Game.IO; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game -{ - /// - /// The most basic that can be used to host osu! components and systems. - /// Unlike , this class will not load any kind of UI, allowing it to be used - /// for provide dependencies to test cases without interfering with them. - /// - public class OsuGameBase : Framework.Game, ICanAcceptFiles - { - protected OsuConfigManager LocalConfig; - - protected BeatmapManager BeatmapManager; - - protected SkinManager SkinManager; - - protected RulesetStore RulesetStore; - - protected FileStore FileStore; - - protected ScoreStore ScoreStore; - - protected KeyBindingStore KeyBindingStore; - - protected SettingsStore SettingsStore; - - protected CursorOverrideContainer CursorOverrideContainer; - - protected override string MainResourceFile => @"osu.Game.Resources.dll"; - - private Container content; - - protected override Container Content => content; - - public Bindable Beatmap { get; private set; } - - private Bindable fpsDisplayVisible; - - protected AssemblyName AssemblyName => Assembly.GetEntryAssembly()?.GetName() ?? new AssemblyName { Version = new Version() }; - - public bool IsDeployedBuild => AssemblyName.Version.Major > 0; - - public string Version - { - get - { - if (!IsDeployedBuild) - return @"local " + (DebugUtils.IsDebug ? @"debug" : @"release"); - - var assembly = AssemblyName; - return $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}"; - } - } - - public OsuGameBase() - { - Name = @"osu!lazer"; - } - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => - dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); - - private DatabaseContextFactory contextFactory; - - [BackgroundDependencyLoader] - private void load() - { - dependencies.Cache(contextFactory = new DatabaseContextFactory(Host)); - - dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); - - dependencies.CacheAs(this); - dependencies.Cache(LocalConfig); - - runMigrations(); - - dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); - dependencies.CacheAs(SkinManager); - - var api = new APIAccess(LocalConfig); - - dependencies.Cache(api); - dependencies.CacheAs(api); - - dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); - dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); - dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore)); - dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); - dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); - dependencies.Cache(new OsuColour()); - - fileImporters.Add(BeatmapManager); - fileImporters.Add(ScoreStore); - fileImporters.Add(SkinManager); - - //this completely overrides the framework default. will need to change once we make a proper FontStore. - dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }); - - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Medium")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-MediumItalic")); - - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Basic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Hangul")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Basic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Compatibility")); - - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Regular")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-RegularItalic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBold")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBoldItalic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Bold")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BoldItalic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Light")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-LightItalic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Black")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BlackItalic")); - - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light")); - - var defaultBeatmap = new DummyWorkingBeatmap(this); - Beatmap = new NonNullableBindable(defaultBeatmap); - BeatmapManager.DefaultBeatmap = defaultBeatmap; - - // tracks play so loud our samples can't keep up. - // this adds a global reduction of track volume for the time being. - Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); - - Beatmap.ValueChanged += b => - { - var trackLoaded = lastBeatmap?.TrackLoaded ?? false; - - // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) - if (!trackLoaded || lastBeatmap?.Track != b.Track) - { - if (trackLoaded) - { - Debug.Assert(lastBeatmap != null); - Debug.Assert(lastBeatmap.Track != null); - - lastBeatmap.RecycleTrack(); - } - - Audio.Track.AddItem(b.Track); - } - - lastBeatmap = b; - }; - - FileStore.Cleanup(); - - AddInternal(api); - } - - private void runMigrations() - { - try - { - using (var db = contextFactory.GetForWrite()) - db.Context.Migrate(); - } - catch (MigrationFailedException e) - { - Logger.Error(e.InnerException ?? e, "Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database); - - // if we failed, let's delete the database and start fresh. - // todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this. - contextFactory.ResetDatabase(); - Logger.Log("Database purged successfully.", LoggingTarget.Database, LogLevel.Important); - - using (var db = contextFactory.GetForWrite()) - db.Context.Migrate(); - } - } - - private WorkingBeatmap lastBeatmap; - - protected override void LoadComplete() - { - base.LoadComplete(); - - GlobalActionContainer globalBinding; - - CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; - CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) - { - RelativeSizeAxes = Axes.Both, - Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } - }; - - base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); - - KeyBindingStore.Register(globalBinding); - dependencies.Cache(globalBinding); - - // TODO: This is temporary until we reimplement the local FPS display. - // It's just to allow end-users to access the framework FPS display without knowing the shortcut key. - fpsDisplayVisible = LocalConfig.GetBindable(OsuSetting.ShowFpsDisplay); - fpsDisplayVisible.ValueChanged += val => { FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; }; - fpsDisplayVisible.TriggerChange(); - } - - public override void SetHost(GameHost host) - { - if (LocalConfig == null) - LocalConfig = new OsuConfigManager(host.Storage); - base.SetHost(host); - } - - private readonly List fileImporters = new List(); - - public void Import(params string[] paths) - { - var extension = Path.GetExtension(paths.First()); - - foreach (var importer in fileImporters) - if (importer.HandledExtensions.Contains(extension)) importer.Import(paths); - } - - public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Configuration; +using osu.Framework.Development; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.IO.Stores; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Cursor; +using osu.Game.Online.API; +using osu.Framework.Graphics.Performance; +using osu.Framework.Graphics.Textures; +using osu.Framework.Logging; +using osu.Game.Database; +using osu.Game.Graphics.Textures; +using osu.Game.Input; +using osu.Game.Input.Bindings; +using osu.Game.IO; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game +{ + /// + /// The most basic that can be used to host osu! components and systems. + /// Unlike , this class will not load any kind of UI, allowing it to be used + /// for provide dependencies to test cases without interfering with them. + /// + public class OsuGameBase : Framework.Game, ICanAcceptFiles + { + protected OsuConfigManager LocalConfig; + + protected BeatmapManager BeatmapManager; + + protected SkinManager SkinManager; + + protected RulesetStore RulesetStore; + + protected FileStore FileStore; + + protected ScoreStore ScoreStore; + + protected KeyBindingStore KeyBindingStore; + + protected SettingsStore SettingsStore; + + protected CursorOverrideContainer CursorOverrideContainer; + + protected override string MainResourceFile => @"osu.Game.Resources.dll"; + + private Container content; + + protected override Container Content => content; + + public Bindable Beatmap { get; private set; } + + private Bindable fpsDisplayVisible; + + protected AssemblyName AssemblyName => Assembly.GetEntryAssembly()?.GetName() ?? new AssemblyName { Version = new Version() }; + + public bool IsDeployedBuild => AssemblyName.Version.Major > 0; + + public string Version + { + get + { + if (!IsDeployedBuild) + return @"local " + (DebugUtils.IsDebug ? @"debug" : @"release"); + + var assembly = AssemblyName; + return $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}"; + } + } + + public OsuGameBase() + { + Name = @"osu!lazer"; + } + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => + dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + + private DatabaseContextFactory contextFactory; + + [BackgroundDependencyLoader] + private void load() + { + dependencies.Cache(contextFactory = new DatabaseContextFactory(Host)); + + dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); + + dependencies.CacheAs(this); + dependencies.Cache(LocalConfig); + + runMigrations(); + + dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); + dependencies.CacheAs(SkinManager); + + var api = new APIAccess(LocalConfig); + + dependencies.Cache(api); + dependencies.CacheAs(api); + + dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); + dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); + dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore)); + dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); + dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); + dependencies.Cache(new OsuColour()); + + fileImporters.Add(BeatmapManager); + fileImporters.Add(ScoreStore); + fileImporters.Add(SkinManager); + + //this completely overrides the framework default. will need to change once we make a proper FontStore. + dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }); + + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Medium")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-MediumItalic")); + + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Basic")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Hangul")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Basic")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Compatibility")); + + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Regular")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-RegularItalic")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBold")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBoldItalic")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Bold")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BoldItalic")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Light")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-LightItalic")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Black")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BlackItalic")); + + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light")); + + var defaultBeatmap = new DummyWorkingBeatmap(this); + Beatmap = new NonNullableBindable(defaultBeatmap); + BeatmapManager.DefaultBeatmap = defaultBeatmap; + + // tracks play so loud our samples can't keep up. + // this adds a global reduction of track volume for the time being. + Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); + + Beatmap.ValueChanged += b => + { + var trackLoaded = lastBeatmap?.TrackLoaded ?? false; + + // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) + if (!trackLoaded || lastBeatmap?.Track != b.Track) + { + if (trackLoaded) + { + Debug.Assert(lastBeatmap != null); + Debug.Assert(lastBeatmap.Track != null); + + lastBeatmap.RecycleTrack(); + } + + Audio.Track.AddItem(b.Track); + } + + lastBeatmap = b; + }; + + FileStore.Cleanup(); + + AddInternal(api); + } + + private void runMigrations() + { + try + { + using (var db = contextFactory.GetForWrite()) + db.Context.Migrate(); + } + catch (MigrationFailedException e) + { + Logger.Error(e.InnerException ?? e, "Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database); + + // if we failed, let's delete the database and start fresh. + // todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this. + contextFactory.ResetDatabase(); + Logger.Log("Database purged successfully.", LoggingTarget.Database, LogLevel.Important); + + using (var db = contextFactory.GetForWrite()) + db.Context.Migrate(); + } + } + + private WorkingBeatmap lastBeatmap; + + protected override void LoadComplete() + { + base.LoadComplete(); + + GlobalActionContainer globalBinding; + + CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; + CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) + { + RelativeSizeAxes = Axes.Both, + Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } + }; + + base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); + + KeyBindingStore.Register(globalBinding); + dependencies.Cache(globalBinding); + + // TODO: This is temporary until we reimplement the local FPS display. + // It's just to allow end-users to access the framework FPS display without knowing the shortcut key. + fpsDisplayVisible = LocalConfig.GetBindable(OsuSetting.ShowFpsDisplay); + fpsDisplayVisible.ValueChanged += val => { FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; }; + fpsDisplayVisible.TriggerChange(); + } + + public override void SetHost(GameHost host) + { + if (LocalConfig == null) + LocalConfig = new OsuConfigManager(host.Storage); + base.SetHost(host); + } + + private readonly List fileImporters = new List(); + + public void Import(params string[] paths) + { + var extension = Path.GetExtension(paths.First()); + + foreach (var importer in fileImporters) + if (importer.HandledExtensions.Contains(extension)) importer.Import(paths); + } + + public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray(); + } +} diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index eec69214de..8b19bca671 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -1,131 +1,131 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Sprites; -using osu.Game.Users; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Game.Graphics.Containers; -using osu.Framework.Graphics.Cursor; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class AuthorInfo : Container - { - private const float height = 50; - - private readonly UpdateableAvatar avatar; - private readonly ClickableArea clickableArea; - private readonly FillFlowContainer fields; - - private UserProfileOverlay profile; - - private BeatmapSetInfo beatmapSet; - public BeatmapSetInfo BeatmapSet - { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - var i = BeatmapSet.OnlineInfo; - - avatar.User = BeatmapSet.Metadata.Author; - clickableArea.Action = () => profile?.ShowUser(avatar.User); - - fields.Children = new Drawable[] - { - new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), - new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold") - { - Margin = new MarginPadding { Top = 5 }, - }, - }; - - if (i.Ranked.HasValue) - { - fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); - } - else if (i.LastUpdated.HasValue) - { - fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); - } - } - } - - public AuthorInfo() - { - RelativeSizeAxes = Axes.X; - Height = height; - - Children = new Drawable[] - { - clickableArea = new ClickableArea - { - AutoSizeAxes = Axes.Both, - CornerRadius = 3, - Masking = true, - Child = avatar = new UpdateableAvatar - { - Size = new Vector2(height), - }, - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.25f), - Type = EdgeEffectType.Shadow, - Radius = 3, - Offset = new Vector2(0f, 1f), - }, - }, - fields = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Left = height + 5 }, - }, - }; - } - - [BackgroundDependencyLoader(true)] - private void load(UserProfileOverlay profile) - { - this.profile = profile; - clickableArea.Action = () => profile?.ShowUser(avatar.User); - } - - private class Field : FillFlowContainer - { - public Field(string first, string second, string secondFont) - { - AutoSizeAxes = Axes.Both; - Direction = FillDirection.Horizontal; - - Children = new[] - { - new OsuSpriteText - { - Text = $"{first} ", - TextSize = 13, - }, - new OsuSpriteText - { - Text = second, - TextSize = 13, - Font = secondFont, - }, - }; - } - } - - private class ClickableArea : OsuClickableContainer, IHasTooltip - { - public string TooltipText => @"View Profile"; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Game.Graphics.Containers; +using osu.Framework.Graphics.Cursor; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class AuthorInfo : Container + { + private const float height = 50; + + private readonly UpdateableAvatar avatar; + private readonly ClickableArea clickableArea; + private readonly FillFlowContainer fields; + + private UserProfileOverlay profile; + + private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet + { + get { return beatmapSet; } + set + { + if (value == beatmapSet) return; + beatmapSet = value; + + var i = BeatmapSet.OnlineInfo; + + avatar.User = BeatmapSet.Metadata.Author; + clickableArea.Action = () => profile?.ShowUser(avatar.User); + + fields.Children = new Drawable[] + { + new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), + new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold") + { + Margin = new MarginPadding { Top = 5 }, + }, + }; + + if (i.Ranked.HasValue) + { + fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); + } + else if (i.LastUpdated.HasValue) + { + fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); + } + } + } + + public AuthorInfo() + { + RelativeSizeAxes = Axes.X; + Height = height; + + Children = new Drawable[] + { + clickableArea = new ClickableArea + { + AutoSizeAxes = Axes.Both, + CornerRadius = 3, + Masking = true, + Child = avatar = new UpdateableAvatar + { + Size = new Vector2(height), + }, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 3, + Offset = new Vector2(0f, 1f), + }, + }, + fields = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Left = height + 5 }, + }, + }; + } + + [BackgroundDependencyLoader(true)] + private void load(UserProfileOverlay profile) + { + this.profile = profile; + clickableArea.Action = () => profile?.ShowUser(avatar.User); + } + + private class Field : FillFlowContainer + { + public Field(string first, string second, string secondFont) + { + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Horizontal; + + Children = new[] + { + new OsuSpriteText + { + Text = $"{first} ", + TextSize = 13, + }, + new OsuSpriteText + { + Text = second, + TextSize = 13, + Font = secondFont, + }, + }; + } + } + + private class ClickableArea : OsuClickableContainer, IHasTooltip + { + public string TooltipText => @"View Profile"; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 1b8fff36ec..8fd34914a7 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -1,130 +1,130 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class BasicStats : Container - { - private readonly Statistic length, bpm, circleCount, sliderCount; - - private BeatmapSetInfo beatmapSet; - public BeatmapSetInfo BeatmapSet - { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##"); - } - } - - private BeatmapInfo beatmap; - public BeatmapInfo Beatmap - { - get { return beatmap; } - set - { - if (value == beatmap) return; - beatmap = value; - - length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss"); - circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); - sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); - } - } - - public BasicStats() - { - Child = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new[] - { - length = new Statistic(FontAwesome.fa_clock_o, "Length") { Width = 0.25f }, - bpm = new Statistic(FontAwesome.fa_circle, "BPM") { Width = 0.25f }, - circleCount = new Statistic(FontAwesome.fa_circle_o, "Circle Count") { Width = 0.25f }, - sliderCount = new Statistic(FontAwesome.fa_circle, "Slider Count") { Width = 0.25f }, - }, - }; - } - - private class Statistic : Container, IHasTooltip - { - private readonly string name; - private readonly OsuSpriteText value; - - public string TooltipText => name; - public string Value - { - get { return value.Text; } - set { this.value.Text = value; } - } - - public Statistic(FontAwesome icon, string name) - { - this.name = name; - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Children = new Drawable[] - { - new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - Icon = FontAwesome.fa_square, - Size = new Vector2(13), - Rotation = 45, - Colour = OsuColour.FromHex(@"441288"), - }, - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - Icon = icon, - Size = new Vector2(13), - Colour = OsuColour.FromHex(@"f7dd55"), - Scale = new Vector2(0.8f), - }, - value = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - TextSize = 13, - Font = @"Exo2.0-Bold", - Margin = new MarginPadding { Left = 10 }, - }, - }, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - value.Colour = colour.Yellow; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class BasicStats : Container + { + private readonly Statistic length, bpm, circleCount, sliderCount; + + private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet + { + get { return beatmapSet; } + set + { + if (value == beatmapSet) return; + beatmapSet = value; + + bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##"); + } + } + + private BeatmapInfo beatmap; + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + if (value == beatmap) return; + beatmap = value; + + length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss"); + circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); + sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); + } + } + + public BasicStats() + { + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new[] + { + length = new Statistic(FontAwesome.fa_clock_o, "Length") { Width = 0.25f }, + bpm = new Statistic(FontAwesome.fa_circle, "BPM") { Width = 0.25f }, + circleCount = new Statistic(FontAwesome.fa_circle_o, "Circle Count") { Width = 0.25f }, + sliderCount = new Statistic(FontAwesome.fa_circle, "Slider Count") { Width = 0.25f }, + }, + }; + } + + private class Statistic : Container, IHasTooltip + { + private readonly string name; + private readonly OsuSpriteText value; + + public string TooltipText => name; + public string Value + { + get { return value.Text; } + set { this.value.Text = value; } + } + + public Statistic(FontAwesome icon, string name) + { + this.name = name; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_square, + Size = new Vector2(13), + Rotation = 45, + Colour = OsuColour.FromHex(@"441288"), + }, + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Icon = icon, + Size = new Vector2(13), + Colour = OsuColour.FromHex(@"f7dd55"), + Scale = new Vector2(0.8f), + }, + value = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = 13, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Left = 10 }, + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + value.Colour = colour.Yellow; + } + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 1781e606f9..87dc9b104b 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -1,312 +1,312 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class BeatmapPicker : Container - { - private const float tile_icon_padding = 7; - private const float tile_spacing = 2; - - private readonly DifficultiesContainer difficulties; - private readonly OsuSpriteText version, starRating; - private readonly Statistic plays, favourites; - - public readonly Bindable Beatmap = new Bindable(); - - private BeatmapSetInfo beatmapSet; - public BeatmapSetInfo BeatmapSet - { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - Beatmap.Value = BeatmapSet.Beatmaps.First(); - plays.Value = BeatmapSet.OnlineInfo.PlayCount; - favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; - difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b) - { - State = DifficultySelectorState.NotSelected, - OnHovered = beatmap => - { - showBeatmap(beatmap); - starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##"); - starRating.FadeIn(100); - }, - OnClicked = beatmap => - { - Beatmap.Value = beatmap; - }, - }); - - updateDifficultyButtons(); - } - } - - public BeatmapPicker() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - difficulties = new DifficultiesContainer - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2) }, - OnLostHover = () => - { - showBeatmap(Beatmap.Value); - starRating.FadeOut(100); - }, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 10 }, - Spacing = new Vector2(5f), - Children = new[] - { - version = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - TextSize = 20, - Font = @"Exo2.0-Bold", - }, - starRating = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - TextSize = 13, - Font = @"Exo2.0-Bold", - Text = "Star Difficulty", - Alpha = 0, - Margin = new MarginPadding { Bottom = 1 }, - }, - }, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10f), - Margin = new MarginPadding { Top = 5 }, - Children = new[] - { - plays = new Statistic(FontAwesome.fa_play_circle), - favourites = new Statistic(FontAwesome.fa_heart), - }, - }, - }, - }, - }; - - Beatmap.ValueChanged += b => - { - showBeatmap(b); - updateDifficultyButtons(); - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - starRating.Colour = colours.Yellow; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // done here so everything can bind in intialization and get the first trigger - Beatmap.TriggerChange(); - } - - private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version; - - private void updateDifficultyButtons() - { - difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected); - } - - private class DifficultiesContainer : FillFlowContainer - { - public Action OnLostHover; - - protected override void OnHoverLost(InputState state) - { - base.OnHoverLost(state); - OnLostHover?.Invoke(); - } - } - - private class DifficultySelectorButton : OsuClickableContainer, IStateful - { - private const float transition_duration = 100; - private const float size = 52; - - private readonly Container bg; - private readonly DifficultyIcon icon; - - public readonly BeatmapInfo Beatmap; - - public Action OnHovered; - public Action OnClicked; - public event Action StateChanged; - - private DifficultySelectorState state; - public DifficultySelectorState State - { - get { return state; } - set - { - if (value == state) return; - state = value; - - StateChanged?.Invoke(State); - if (value == DifficultySelectorState.Selected) - fadeIn(); - else - fadeOut(); - } - } - - public DifficultySelectorButton(BeatmapInfo beatmap) - { - Beatmap = beatmap; - Size = new Vector2(size); - Margin = new MarginPadding { Horizontal = tile_spacing / 2 }; - - Children = new Drawable[] - { - bg = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 4, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, - }, - icon = new DifficultyIcon(beatmap) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(size - tile_icon_padding * 2), - Margin = new MarginPadding { Bottom = 1 }, - }, - }; - } - - protected override bool OnHover(InputState state) - { - fadeIn(); - OnHovered?.Invoke(Beatmap); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - if (State == DifficultySelectorState.NotSelected) - fadeOut(); - base.OnHoverLost(state); - } - - protected override bool OnClick(InputState state) - { - OnClicked?.Invoke(Beatmap); - return base.OnClick(state); - } - - private void fadeIn() - { - bg.FadeIn(transition_duration); - icon.FadeIn(transition_duration); - } - - private void fadeOut() - { - bg.FadeOut(); - icon.FadeTo(0.7f, transition_duration); - } - } - - private class Statistic : FillFlowContainer - { - private readonly OsuSpriteText text; - - private int value; - public int Value - { - get { return value; } - set - { - this.value = value; - text.Text = Value.ToString(@"N0"); - } - } - - public Statistic(FontAwesome icon) - { - AutoSizeAxes = Axes.Both; - Direction = FillDirection.Horizontal; - Spacing = new Vector2(2f); - - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = icon, - Shadow = true, - Size = new Vector2(13), - }, - text = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = @"Exo2.0-SemiBoldItalic", - TextSize = 14, - }, - }; - } - } - - private enum DifficultySelectorState - { - Selected, - NotSelected, - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class BeatmapPicker : Container + { + private const float tile_icon_padding = 7; + private const float tile_spacing = 2; + + private readonly DifficultiesContainer difficulties; + private readonly OsuSpriteText version, starRating; + private readonly Statistic plays, favourites; + + public readonly Bindable Beatmap = new Bindable(); + + private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet + { + get { return beatmapSet; } + set + { + if (value == beatmapSet) return; + beatmapSet = value; + + Beatmap.Value = BeatmapSet.Beatmaps.First(); + plays.Value = BeatmapSet.OnlineInfo.PlayCount; + favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; + difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b) + { + State = DifficultySelectorState.NotSelected, + OnHovered = beatmap => + { + showBeatmap(beatmap); + starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##"); + starRating.FadeIn(100); + }, + OnClicked = beatmap => + { + Beatmap.Value = beatmap; + }, + }); + + updateDifficultyButtons(); + } + } + + public BeatmapPicker() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + difficulties = new DifficultiesContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2) }, + OnLostHover = () => + { + showBeatmap(Beatmap.Value); + starRating.FadeOut(100); + }, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 10 }, + Spacing = new Vector2(5f), + Children = new[] + { + version = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + TextSize = 20, + Font = @"Exo2.0-Bold", + }, + starRating = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + TextSize = 13, + Font = @"Exo2.0-Bold", + Text = "Star Difficulty", + Alpha = 0, + Margin = new MarginPadding { Bottom = 1 }, + }, + }, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10f), + Margin = new MarginPadding { Top = 5 }, + Children = new[] + { + plays = new Statistic(FontAwesome.fa_play_circle), + favourites = new Statistic(FontAwesome.fa_heart), + }, + }, + }, + }, + }; + + Beatmap.ValueChanged += b => + { + showBeatmap(b); + updateDifficultyButtons(); + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + starRating.Colour = colours.Yellow; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // done here so everything can bind in intialization and get the first trigger + Beatmap.TriggerChange(); + } + + private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version; + + private void updateDifficultyButtons() + { + difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected); + } + + private class DifficultiesContainer : FillFlowContainer + { + public Action OnLostHover; + + protected override void OnHoverLost(InputState state) + { + base.OnHoverLost(state); + OnLostHover?.Invoke(); + } + } + + private class DifficultySelectorButton : OsuClickableContainer, IStateful + { + private const float transition_duration = 100; + private const float size = 52; + + private readonly Container bg; + private readonly DifficultyIcon icon; + + public readonly BeatmapInfo Beatmap; + + public Action OnHovered; + public Action OnClicked; + public event Action StateChanged; + + private DifficultySelectorState state; + public DifficultySelectorState State + { + get { return state; } + set + { + if (value == state) return; + state = value; + + StateChanged?.Invoke(State); + if (value == DifficultySelectorState.Selected) + fadeIn(); + else + fadeOut(); + } + } + + public DifficultySelectorButton(BeatmapInfo beatmap) + { + Beatmap = beatmap; + Size = new Vector2(size); + Margin = new MarginPadding { Horizontal = tile_spacing / 2 }; + + Children = new Drawable[] + { + bg = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + }, + icon = new DifficultyIcon(beatmap) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(size - tile_icon_padding * 2), + Margin = new MarginPadding { Bottom = 1 }, + }, + }; + } + + protected override bool OnHover(InputState state) + { + fadeIn(); + OnHovered?.Invoke(Beatmap); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (State == DifficultySelectorState.NotSelected) + fadeOut(); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + OnClicked?.Invoke(Beatmap); + return base.OnClick(state); + } + + private void fadeIn() + { + bg.FadeIn(transition_duration); + icon.FadeIn(transition_duration); + } + + private void fadeOut() + { + bg.FadeOut(); + icon.FadeTo(0.7f, transition_duration); + } + } + + private class Statistic : FillFlowContainer + { + private readonly OsuSpriteText text; + + private int value; + public int Value + { + get { return value; } + set + { + this.value = value; + text.Text = Value.ToString(@"N0"); + } + } + + public Statistic(FontAwesome icon) + { + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Horizontal; + Spacing = new Vector2(2f); + + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = icon, + Shadow = true, + Size = new Vector2(13), + }, + text = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = @"Exo2.0-SemiBoldItalic", + TextSize = 14, + }, + }; + } + } + + private enum DifficultySelectorState + { + Selected, + NotSelected, + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 7f052f443f..7f3b6d1584 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -1,120 +1,120 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Screens.Select.Details; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class Details : FillFlowContainer - { - private readonly PreviewButton preview; - private readonly BasicStats basic; - private readonly AdvancedStats advanced; - private readonly UserRatings ratings; - - private BeatmapSetInfo beatmapSet; - public BeatmapSetInfo BeatmapSet - { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - basic.BeatmapSet = preview.BeatmapSet = BeatmapSet; - } - } - - private BeatmapInfo beatmap; - public BeatmapInfo Beatmap - { - get { return beatmap; } - set - { - if (value == beatmap) return; - beatmap = value; - - basic.Beatmap = advanced.Beatmap = Beatmap; - ratings.Metrics = Beatmap.Metrics; - } - } - - public Details() - { - Width = BeatmapSetOverlay.RIGHT_WIDTH; - AutoSizeAxes = Axes.Y; - Spacing = new Vector2(1f); - - Children = new Drawable[] - { - preview = new PreviewButton - { - RelativeSizeAxes = Axes.X, - }, - new DetailBox - { - Child = basic = new BasicStats - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Vertical = 10 }, - }, - }, - new DetailBox - { - Child = advanced = new AdvancedStats - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Vertical = 7.5f }, - }, - }, - new DetailBox - { - Child = ratings = new UserRatings - { - RelativeSizeAxes = Axes.X, - Height = 95, - Margin = new MarginPadding { Top = 10 }, - }, - }, - }; - } - - public void StopPreview() => preview.Playing.Value = false; - - private class DetailBox : Container - { - private readonly Container content; - protected override Container Content => content; - - public DetailBox() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - InternalChildren = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, - content = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 15 }, - }, - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Screens.Select.Details; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class Details : FillFlowContainer + { + private readonly PreviewButton preview; + private readonly BasicStats basic; + private readonly AdvancedStats advanced; + private readonly UserRatings ratings; + + private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet + { + get { return beatmapSet; } + set + { + if (value == beatmapSet) return; + beatmapSet = value; + + basic.BeatmapSet = preview.BeatmapSet = BeatmapSet; + } + } + + private BeatmapInfo beatmap; + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + if (value == beatmap) return; + beatmap = value; + + basic.Beatmap = advanced.Beatmap = Beatmap; + ratings.Metrics = Beatmap.Metrics; + } + } + + public Details() + { + Width = BeatmapSetOverlay.RIGHT_WIDTH; + AutoSizeAxes = Axes.Y; + Spacing = new Vector2(1f); + + Children = new Drawable[] + { + preview = new PreviewButton + { + RelativeSizeAxes = Axes.X, + }, + new DetailBox + { + Child = basic = new BasicStats + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Vertical = 10 }, + }, + }, + new DetailBox + { + Child = advanced = new AdvancedStats + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Vertical = 7.5f }, + }, + }, + new DetailBox + { + Child = ratings = new UserRatings + { + RelativeSizeAxes = Axes.X, + Height = 95, + Margin = new MarginPadding { Top = 10 }, + }, + }, + }; + } + + public void StopPreview() => preview.Playing.Value = false; + + private class DetailBox : Container + { + private readonly Container content; + protected override Container Content => content; + + public DetailBox() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + content = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 15 }, + }, + }; + } + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs index 9691036b87..0c6414c718 100644 --- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs @@ -1,59 +1,59 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class DownloadButton : HeaderButton - { - public DownloadButton(string title, string subtitle) - { - Width = 120; - - Add(new Container - { - Depth = -1, - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 10 }, - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new[] - { - new OsuSpriteText - { - Text = title, - TextSize = 13, - Font = @"Exo2.0-Bold", - }, - new OsuSpriteText - { - Text = subtitle, - TextSize = 11, - Font = @"Exo2.0-Bold", - }, - }, - }, - new SpriteIcon - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Icon = FontAwesome.fa_download, - Size = new Vector2(16), - Margin = new MarginPadding { Right = 5 }, - }, - }, - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class DownloadButton : HeaderButton + { + public DownloadButton(string title, string subtitle) + { + Width = 120; + + Add(new Container + { + Depth = -1, + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 10 }, + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new[] + { + new OsuSpriteText + { + Text = title, + TextSize = 13, + Font = @"Exo2.0-Bold", + }, + new OsuSpriteText + { + Text = subtitle, + TextSize = 11, + Font = @"Exo2.0-Bold", + }, + }, + }, + new SpriteIcon + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Icon = FontAwesome.fa_download, + Size = new Vector2(16), + Margin = new MarginPadding { Right = 5 }, + }, + }, + }); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs index 51492fff44..29bc00038c 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs @@ -1,80 +1,80 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using OpenTK; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class FavouriteButton : HeaderButton - { - public readonly Bindable Favourited = new Bindable(); - - [BackgroundDependencyLoader] - private void load() - { - Container pink; - SpriteIcon icon; - AddRange(new Drawable[] - { - pink = new Container - { - RelativeSizeAxes = Axes.Both, - Alpha = 0f, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"9f015f"), - }, - new Triangles - { - RelativeSizeAxes = Axes.Both, - ColourLight = OsuColour.FromHex(@"cb2187"), - ColourDark = OsuColour.FromHex(@"9f015f"), - TriangleScale = 1.5f, - }, - }, - }, - icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.fa_heart_o, - Size = new Vector2(18), - Shadow = false, - }, - }); - - Favourited.ValueChanged += value => - { - if (value) - { - pink.FadeIn(200); - icon.Icon = FontAwesome.fa_heart; - } - else - { - pink.FadeOut(200); - icon.Icon = FontAwesome.fa_heart_o; - } - }; - - Action = () => Favourited.Value = !Favourited.Value; - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - Width = DrawHeight; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using OpenTK; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class FavouriteButton : HeaderButton + { + public readonly Bindable Favourited = new Bindable(); + + [BackgroundDependencyLoader] + private void load() + { + Container pink; + SpriteIcon icon; + AddRange(new Drawable[] + { + pink = new Container + { + RelativeSizeAxes = Axes.Both, + Alpha = 0f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"9f015f"), + }, + new Triangles + { + RelativeSizeAxes = Axes.Both, + ColourLight = OsuColour.FromHex(@"cb2187"), + ColourDark = OsuColour.FromHex(@"9f015f"), + TriangleScale = 1.5f, + }, + }, + }, + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_heart_o, + Size = new Vector2(18), + Shadow = false, + }, + }); + + Favourited.ValueChanged += value => + { + if (value) + { + pink.FadeIn(200); + icon.Icon = FontAwesome.fa_heart; + } + else + { + pink.FadeOut(200); + icon.Icon = FontAwesome.fa_heart_o; + } + }; + + Action = () => Favourited.Value = !Favourited.Value; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + Width = DrawHeight; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 8056dbff3c..755039e7bc 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -1,270 +1,270 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class Header : Container - { - private const float transition_duration = 250; - private const float tabs_height = 50; - private const float buttons_height = 45; - private const float buttons_spacing = 5; - - private readonly Box tabsBg; - private readonly Container coverContainer; - private readonly OsuSpriteText title, artist; - private readonly Container noVideoButtons; - private readonly FillFlowContainer videoButtons; - private readonly AuthorInfo author; - private readonly Container downloadButtonsContainer; - private readonly BeatmapSetOnlineStatusPill onlineStatusPill; - public Details Details; - - private BeatmapManager beatmaps; - - private DelayedLoadWrapper cover; - - public readonly BeatmapPicker Picker; - - private BeatmapSetInfo beatmapSet; - public BeatmapSetInfo BeatmapSet - { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; - title.Text = BeatmapSet.Metadata.Title; - artist.Text = BeatmapSet.Metadata.Artist; - onlineStatusPill.Status = BeatmapSet.OnlineInfo.Status; - - downloadButtonsContainer.FadeIn(); - noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration); - videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration); - - cover?.FadeOut(400, Easing.Out); - coverContainer.Add(cover = new DelayedLoadWrapper( - new BeatmapSetCover(BeatmapSet) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fill, - OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out), - }, 300) - { - RelativeSizeAxes = Axes.Both, - }); - } - } - - public Header() - { - RelativeSizeAxes = Axes.X; - Height = 400; - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.25f), - Type = EdgeEffectType.Shadow, - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Height = tabs_height, - Children = new[] - { - tabsBg = new Box - { - RelativeSizeAxes = Axes.Both, - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = tabs_height }, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - coverContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)), - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = BeatmapSetOverlay.X_PADDING }, - Child = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Height = 113, - Child = Picker = new BeatmapPicker(), - }, - title = new OsuSpriteText - { - Font = @"Exo2.0-BoldItalic", - TextSize = 37, - }, - artist = new OsuSpriteText - { - Font = @"Exo2.0-SemiBoldItalic", - TextSize = 25, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 20 }, - Child = author = new AuthorInfo(), - }, - new Container - { - RelativeSizeAxes = Axes.X, - Height = buttons_height, - Margin = new MarginPadding { Top = 10 }, - Children = new Drawable[] - { - new FavouriteButton(), - downloadButtonsContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, - Children = new Drawable[] - { - noVideoButtons = new Container - { - RelativeSizeAxes = Axes.Both, - Alpha = 0f, - Child = new DownloadButton("Download", @"") - { - Action = () => download(false), - }, - }, - videoButtons = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Spacing = new Vector2(buttons_spacing), - Alpha = 0f, - Children = new[] - { - new DownloadButton("Download", "with Video") - { - Action = () => download(false), - }, - new DownloadButton("Download", "without Video") - { - Action = () => download(true), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - new FillFlowContainer - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(10), - Children = new Drawable[] - { - onlineStatusPill = new BeatmapSetOnlineStatusPill(14, new MarginPadding { Horizontal = 25, Vertical = 8 }) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }, - Details = new Details(), - }, - }, - }, - }, - }; - - Picker.Beatmap.ValueChanged += b => Details.Beatmap = b; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapManager beatmaps) - { - tabsBg.Colour = colours.Gray3; - this.beatmaps = beatmaps; - - beatmaps.ItemAdded += handleBeatmapAdd; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - if (beatmaps != null) beatmaps.ItemAdded -= handleBeatmapAdd; - } - - private void handleBeatmapAdd(BeatmapSetInfo beatmap) => Schedule(() => - { - if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID) - downloadButtonsContainer.FadeOut(transition_duration); - }); - - private void download(bool noVideo) - { - if (beatmaps.GetExistingDownload(BeatmapSet) != null) - { - downloadButtonsContainer.MoveToX(-5, 50, Easing.OutSine).Then() - .MoveToX(5, 100, Easing.InOutSine).Then() - .MoveToX(-5, 100, Easing.InOutSine).Then() - .MoveToX(0, 50, Easing.InSine).Then(); - - return; - } - - beatmaps.Download(BeatmapSet, noVideo); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class Header : Container + { + private const float transition_duration = 250; + private const float tabs_height = 50; + private const float buttons_height = 45; + private const float buttons_spacing = 5; + + private readonly Box tabsBg; + private readonly Container coverContainer; + private readonly OsuSpriteText title, artist; + private readonly Container noVideoButtons; + private readonly FillFlowContainer videoButtons; + private readonly AuthorInfo author; + private readonly Container downloadButtonsContainer; + private readonly BeatmapSetOnlineStatusPill onlineStatusPill; + public Details Details; + + private BeatmapManager beatmaps; + + private DelayedLoadWrapper cover; + + public readonly BeatmapPicker Picker; + + private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet + { + get { return beatmapSet; } + set + { + if (value == beatmapSet) return; + beatmapSet = value; + + Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; + title.Text = BeatmapSet.Metadata.Title; + artist.Text = BeatmapSet.Metadata.Artist; + onlineStatusPill.Status = BeatmapSet.OnlineInfo.Status; + + downloadButtonsContainer.FadeIn(); + noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration); + videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration); + + cover?.FadeOut(400, Easing.Out); + coverContainer.Add(cover = new DelayedLoadWrapper( + new BeatmapSetCover(BeatmapSet) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, + OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out), + }, 300) + { + RelativeSizeAxes = Axes.Both, + }); + } + } + + public Header() + { + RelativeSizeAxes = Axes.X; + Height = 400; + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Height = tabs_height, + Children = new[] + { + tabsBg = new Box + { + RelativeSizeAxes = Axes.Both, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = tabs_height }, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + coverContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)), + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = BeatmapSetOverlay.X_PADDING }, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Height = 113, + Child = Picker = new BeatmapPicker(), + }, + title = new OsuSpriteText + { + Font = @"Exo2.0-BoldItalic", + TextSize = 37, + }, + artist = new OsuSpriteText + { + Font = @"Exo2.0-SemiBoldItalic", + TextSize = 25, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 20 }, + Child = author = new AuthorInfo(), + }, + new Container + { + RelativeSizeAxes = Axes.X, + Height = buttons_height, + Margin = new MarginPadding { Top = 10 }, + Children = new Drawable[] + { + new FavouriteButton(), + downloadButtonsContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, + Children = new Drawable[] + { + noVideoButtons = new Container + { + RelativeSizeAxes = Axes.Both, + Alpha = 0f, + Child = new DownloadButton("Download", @"") + { + Action = () => download(false), + }, + }, + videoButtons = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Spacing = new Vector2(buttons_spacing), + Alpha = 0f, + Children = new[] + { + new DownloadButton("Download", "with Video") + { + Action = () => download(false), + }, + new DownloadButton("Download", "without Video") + { + Action = () => download(true), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + Children = new Drawable[] + { + onlineStatusPill = new BeatmapSetOnlineStatusPill(14, new MarginPadding { Horizontal = 25, Vertical = 8 }) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + Details = new Details(), + }, + }, + }, + }, + }; + + Picker.Beatmap.ValueChanged += b => Details.Beatmap = b; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, BeatmapManager beatmaps) + { + tabsBg.Colour = colours.Gray3; + this.beatmaps = beatmaps; + + beatmaps.ItemAdded += handleBeatmapAdd; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + if (beatmaps != null) beatmaps.ItemAdded -= handleBeatmapAdd; + } + + private void handleBeatmapAdd(BeatmapSetInfo beatmap) => Schedule(() => + { + if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID) + downloadButtonsContainer.FadeOut(transition_duration); + }); + + private void download(bool noVideo) + { + if (beatmaps.GetExistingDownload(BeatmapSet) != null) + { + downloadButtonsContainer.MoveToX(-5, 50, Easing.OutSine).Then() + .MoveToX(5, 100, Easing.InOutSine).Then() + .MoveToX(-5, 100, Easing.InOutSine).Then() + .MoveToX(0, 50, Easing.InSine).Then(); + + return; + } + + beatmaps.Download(BeatmapSet, noVideo); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index f859c8f692..e1c4f5cc67 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Framework.Allocation; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class HeaderButton : TriangleButton - { - public HeaderButton() - { - Height = 0; - RelativeSizeAxes = Axes.Y; - } - - [BackgroundDependencyLoader] - private void load() - { - BackgroundColour = OsuColour.FromHex(@"094c5f"); - Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); - Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); - Triangles.TriangleScale = 1.5f; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Framework.Allocation; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class HeaderButton : TriangleButton + { + public HeaderButton() + { + Height = 0; + RelativeSizeAxes = Axes.Y; + } + + [BackgroundDependencyLoader] + private void load() + { + BackgroundColour = OsuColour.FromHex(@"094c5f"); + Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); + Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); + Triangles.TriangleScale = 1.5f; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index a0b6d9cefa..9a402515ae 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -1,195 +1,195 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class Info : Container - { - private const float transition_duration = 250; - private const float metadata_width = 225; - private const float spacing = 20; - - private readonly MetadataSection description, source, tags; - private readonly Box successRateBackground; - private readonly SuccessRate successRate; - - private BeatmapSetInfo beatmapSet; - public BeatmapSetInfo BeatmapSet - { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - source.Text = BeatmapSet.Metadata.Source; - tags.Text = BeatmapSet.Metadata.Tags; - } - } - - public BeatmapInfo Beatmap - { - get { return successRate.Beatmap; } - set { successRate.Beatmap = value; } - } - - public Info() - { - RelativeSizeAxes = Axes.X; - Height = 220; - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.25f), - Type = EdgeEffectType.Shadow, - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 15, Horizontal = BeatmapSetOverlay.X_PADDING }, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = metadata_width + BeatmapSetOverlay.RIGHT_WIDTH + spacing * 2 }, - Child = new Container - { - RelativeSizeAxes = Axes.Both, - Child = description = new MetadataSection("Description"), - }, - }, - new Container - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = metadata_width, - Padding = new MarginPadding { Horizontal = 10 }, - Margin = new MarginPadding { Right = BeatmapSetOverlay.RIGHT_WIDTH + spacing }, - Child = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - LayoutDuration = transition_duration, - Children = new[] - { - source = new MetadataSection("Source"), - tags = new MetadataSection("Tags"), - }, - }, - }, - new Container - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = BeatmapSetOverlay.RIGHT_WIDTH, - Children = new Drawable[] - { - successRateBackground = new Box - { - RelativeSizeAxes = Axes.Both, - }, - successRate = new SuccessRate - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 20, Horizontal = 15 }, - }, - }, - }, - }, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - successRateBackground.Colour = colours.GrayE; - source.TextColour = description.TextColour = colours.Gray5; - tags.TextColour = colours.BlueDark; - } - - private class MetadataSection : FillFlowContainer - { - private readonly OsuSpriteText header; - private readonly TextFlowContainer textFlow; - - public string Text - { - set - { - if (string.IsNullOrEmpty(value)) - { - this.FadeOut(transition_duration); - return; - } - - this.FadeIn(transition_duration); - textFlow.Clear(); - textFlow.AddText(value, s => s.TextSize = 14); - } - } - - public Color4 TextColour - { - get { return textFlow.Colour; } - set { textFlow.Colour = value; } - } - - public MetadataSection(string title) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Spacing = new Vector2(5f); - - InternalChildren = new Drawable[] - { - header = new OsuSpriteText - { - Text = title, - Font = @"Exo2.0-Bold", - TextSize = 14, - Shadow = false, - Margin = new MarginPadding { Top = 20 }, - }, - textFlow = new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - header.Colour = colours.Gray5; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class Info : Container + { + private const float transition_duration = 250; + private const float metadata_width = 225; + private const float spacing = 20; + + private readonly MetadataSection description, source, tags; + private readonly Box successRateBackground; + private readonly SuccessRate successRate; + + private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet + { + get { return beatmapSet; } + set + { + if (value == beatmapSet) return; + beatmapSet = value; + + source.Text = BeatmapSet.Metadata.Source; + tags.Text = BeatmapSet.Metadata.Tags; + } + } + + public BeatmapInfo Beatmap + { + get { return successRate.Beatmap; } + set { successRate.Beatmap = value; } + } + + public Info() + { + RelativeSizeAxes = Axes.X; + Height = 220; + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 15, Horizontal = BeatmapSetOverlay.X_PADDING }, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = metadata_width + BeatmapSetOverlay.RIGHT_WIDTH + spacing * 2 }, + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Child = description = new MetadataSection("Description"), + }, + }, + new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = metadata_width, + Padding = new MarginPadding { Horizontal = 10 }, + Margin = new MarginPadding { Right = BeatmapSetOverlay.RIGHT_WIDTH + spacing }, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + LayoutDuration = transition_duration, + Children = new[] + { + source = new MetadataSection("Source"), + tags = new MetadataSection("Tags"), + }, + }, + }, + new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = BeatmapSetOverlay.RIGHT_WIDTH, + Children = new Drawable[] + { + successRateBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + successRate = new SuccessRate + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 20, Horizontal = 15 }, + }, + }, + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + successRateBackground.Colour = colours.GrayE; + source.TextColour = description.TextColour = colours.Gray5; + tags.TextColour = colours.BlueDark; + } + + private class MetadataSection : FillFlowContainer + { + private readonly OsuSpriteText header; + private readonly TextFlowContainer textFlow; + + public string Text + { + set + { + if (string.IsNullOrEmpty(value)) + { + this.FadeOut(transition_duration); + return; + } + + this.FadeIn(transition_duration); + textFlow.Clear(); + textFlow.AddText(value, s => s.TextSize = 14); + } + } + + public Color4 TextColour + { + get { return textFlow.Colour; } + set { textFlow.Colour = value; } + } + + public MetadataSection(string title) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Spacing = new Vector2(5f); + + InternalChildren = new Drawable[] + { + header = new OsuSpriteText + { + Text = title, + Font = @"Exo2.0-Bold", + TextSize = 14, + Shadow = false, + Margin = new MarginPadding { Top = 20 }, + }, + textFlow = new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + header.Colour = colours.Gray5; + } + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/PreviewButton.cs index 4f5a6ba718..6b5ffa57ad 100644 --- a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/PreviewButton.cs @@ -1,108 +1,108 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio.Track; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Overlays.Direct; -using osu.Framework.Configuration; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class PreviewButton : OsuClickableContainer - { - private const float transition_duration = 500; - - private readonly Box bg, progress; - private readonly PlayButton playButton; - - private Track preview => playButton.Preview; - public Bindable Playing => playButton.Playing; - - public BeatmapSetInfo BeatmapSet - { - get { return playButton.BeatmapSet; } - set { playButton.BeatmapSet = value; } - } - - public PreviewButton() - { - Height = 42; - - Children = new Drawable[] - { - bg = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.25f), - }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 3, - Child = progress = new Box - { - RelativeSizeAxes = Axes.Both, - Width = 0f, - Alpha = 0f, - }, - }, - playButton = new PlayButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(18), - }, - }; - - Action = () => Playing.Value = !Playing.Value; - Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - progress.Colour = colours.Yellow; - } - - protected override void Update() - { - base.Update(); - - if (Playing.Value && preview != null) - { - // prevent negative (potential infinite) width if a track without length was loaded - progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; - } - } - - protected override void Dispose(bool isDisposing) - { - Playing.Value = false; - base.Dispose(isDisposing); - } - - protected override bool OnHover(InputState state) - { - bg.FadeColour(Color4.Black.Opacity(0.5f), 100); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - bg.FadeColour(Color4.Black.Opacity(0.25f), 100); - base.OnHoverLost(state); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Overlays.Direct; +using osu.Framework.Configuration; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class PreviewButton : OsuClickableContainer + { + private const float transition_duration = 500; + + private readonly Box bg, progress; + private readonly PlayButton playButton; + + private Track preview => playButton.Preview; + public Bindable Playing => playButton.Playing; + + public BeatmapSetInfo BeatmapSet + { + get { return playButton.BeatmapSet; } + set { playButton.BeatmapSet = value; } + } + + public PreviewButton() + { + Height = 42; + + Children = new Drawable[] + { + bg = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.25f), + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 3, + Child = progress = new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0f, + Alpha = 0f, + }, + }, + playButton = new PlayButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(18), + }, + }; + + Action = () => Playing.Value = !Playing.Value; + Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + progress.Colour = colours.Yellow; + } + + protected override void Update() + { + base.Update(); + + if (Playing.Value && preview != null) + { + // prevent negative (potential infinite) width if a track without length was loaded + progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; + } + } + + protected override void Dispose(bool isDisposing) + { + Playing.Value = false; + base.Dispose(isDisposing); + } + + protected override bool OnHover(InputState state) + { + bg.FadeColour(Color4.Black.Opacity(0.5f), 100); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + bg.FadeColour(Color4.Black.Opacity(0.25f), 100); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs b/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs index 826a7c53f3..1f1a2a68d2 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs @@ -1,62 +1,62 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Users; -using osu.Framework.Input; - -namespace osu.Game.Overlays.BeatmapSet.Scores -{ - public class ClickableUsername : OsuHoverContainer - { - private readonly OsuSpriteText text; - private UserProfileOverlay profile; - - private User user; - public User User - { - get { return user; } - set - { - if (user == value) return; - user = value; - - text.Text = user.Username; - } - } - - public float TextSize - { - set - { - if (text.TextSize == value) return; - text.TextSize = value; - } - get { return text.TextSize; } - } - - public ClickableUsername() - { - AutoSizeAxes = Axes.Both; - Child = text = new OsuSpriteText - { - Font = @"Exo2.0-BoldItalic", - }; - } - - [BackgroundDependencyLoader(true)] - private void load(UserProfileOverlay profile) - { - this.profile = profile; - } - - protected override bool OnClick(InputState state) - { - profile?.ShowUser(user); - return true; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using osu.Framework.Input; + +namespace osu.Game.Overlays.BeatmapSet.Scores +{ + public class ClickableUsername : OsuHoverContainer + { + private readonly OsuSpriteText text; + private UserProfileOverlay profile; + + private User user; + public User User + { + get { return user; } + set + { + if (user == value) return; + user = value; + + text.Text = user.Username; + } + } + + public float TextSize + { + set + { + if (text.TextSize == value) return; + text.TextSize = value; + } + get { return text.TextSize; } + } + + public ClickableUsername() + { + AutoSizeAxes = Axes.Both; + Child = text = new OsuSpriteText + { + Font = @"Exo2.0-BoldItalic", + }; + } + + [BackgroundDependencyLoader(true)] + private void load(UserProfileOverlay profile) + { + this.profile = profile; + } + + protected override bool OnClick(InputState state) + { + profile?.ShowUser(user); + return true; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs index 7ef86d349a..10e689698d 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs @@ -1,142 +1,142 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Profile.Sections.Ranks; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Select.Leaderboards; -using osu.Game.Users; - -namespace osu.Game.Overlays.BeatmapSet.Scores -{ - public class DrawableScore : Container - { - private const int fade_duration = 100; - private const float side_margin = 20; - - private readonly Box background; - - public DrawableScore(int index, OnlineScore score) - { - ScoreModsContainer modsContainer; - - RelativeSizeAxes = Axes.X; - Height = 30; - CornerRadius = 3; - Masking = true; - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = $"#{index + 1}", - Font = @"Exo2.0-RegularItalic", - Margin = new MarginPadding { Left = side_margin } - }, - new DrawableFlag(score.User.Country) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(30, 20), - Margin = new MarginPadding { Left = 60 } - }, - new ClickableUsername - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - User = score.User, - Margin = new MarginPadding { Left = 100 } - }, - modsContainer = new ScoreModsContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Width = 0.06f, - RelativePositionAxes = Axes.X, - X = 0.42f - }, - new DrawableRank(score.Rank) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(30, 20), - FillMode = FillMode.Fit, - RelativePositionAxes = Axes.X, - X = 0.55f - }, - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, - Text = $@"{score.TotalScore:N0}", - Font = @"Venera", - RelativePositionAxes = Axes.X, - X = 0.75f, - FixedWidth = true, - }, - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, - Text = $@"{score.Accuracy:P2}", - Font = @"Exo2.0-RegularItalic", - RelativePositionAxes = Axes.X, - X = 0.85f - }, - new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Text = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}", - Font = @"Exo2.0-RegularItalic", - Margin = new MarginPadding { Right = side_margin } - }, - }; - - foreach (Mod mod in score.Mods) - modsContainer.Add(new ModIcon(mod) - { - AutoSizeAxes = Axes.Both, - Scale = new Vector2(0.35f), - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.Gray4; - } - - protected override bool OnHover(InputState state) - { - background.FadeIn(fade_duration, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - background.FadeOut(fade_duration, Easing.OutQuint); - base.OnHoverLost(state); - } - - protected override bool OnClick(InputState state) => true; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections.Ranks; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Users; + +namespace osu.Game.Overlays.BeatmapSet.Scores +{ + public class DrawableScore : Container + { + private const int fade_duration = 100; + private const float side_margin = 20; + + private readonly Box background; + + public DrawableScore(int index, OnlineScore score) + { + ScoreModsContainer modsContainer; + + RelativeSizeAxes = Axes.X; + Height = 30; + CornerRadius = 3; + Masking = true; + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = $"#{index + 1}", + Font = @"Exo2.0-RegularItalic", + Margin = new MarginPadding { Left = side_margin } + }, + new DrawableFlag(score.User.Country) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(30, 20), + Margin = new MarginPadding { Left = 60 } + }, + new ClickableUsername + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + User = score.User, + Margin = new MarginPadding { Left = 100 } + }, + modsContainer = new ScoreModsContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Width = 0.06f, + RelativePositionAxes = Axes.X, + X = 0.42f + }, + new DrawableRank(score.Rank) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(30, 20), + FillMode = FillMode.Fit, + RelativePositionAxes = Axes.X, + X = 0.55f + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreRight, + Text = $@"{score.TotalScore:N0}", + Font = @"Venera", + RelativePositionAxes = Axes.X, + X = 0.75f, + FixedWidth = true, + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreRight, + Text = $@"{score.Accuracy:P2}", + Font = @"Exo2.0-RegularItalic", + RelativePositionAxes = Axes.X, + X = 0.85f + }, + new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Text = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}", + Font = @"Exo2.0-RegularItalic", + Margin = new MarginPadding { Right = side_margin } + }, + }; + + foreach (Mod mod in score.Mods) + modsContainer.Add(new ModIcon(mod) + { + AutoSizeAxes = Axes.Both, + Scale = new Vector2(0.35f), + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.Gray4; + } + + protected override bool OnHover(InputState state) + { + background.FadeIn(fade_duration, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + background.FadeOut(fade_duration, Easing.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) => true; + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs index 66d7a9e4e1..8ac79eabb4 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs @@ -1,243 +1,243 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Profile.Sections.Ranks; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Select.Leaderboards; -using osu.Game.Users; - -namespace osu.Game.Overlays.BeatmapSet.Scores -{ - public class DrawableTopScore : Container - { - private const float fade_duration = 100; - private const float height = 200; - private const float avatar_size = 80; - private const float margin = 10; - - private readonly Box background; - private readonly Box bottomBackground; - private readonly Box middleLine; - private readonly UpdateableAvatar avatar; - private readonly DrawableFlag flag; - private readonly ClickableUsername username; - private readonly OsuSpriteText rankText; - private readonly OsuSpriteText date; - private readonly DrawableRank rank; - private readonly InfoColumn totalScore; - private readonly InfoColumn accuracy; - private readonly InfoColumn statistics; - private readonly ScoreModsContainer modsContainer; - - private OnlineScore score; - public OnlineScore Score - { - get { return score; } - set - { - if (score == value) return; - score = value; - - avatar.User = username.User = score.User; - flag.Country = score.User.Country; - date.Text = $@"achieved {score.Date:MMM d, yyyy}"; - rank.UpdateRank(score.Rank); - - totalScore.Value = $@"{score.TotalScore:N0}"; - accuracy.Value = $@"{score.Accuracy:P2}"; - statistics.Value = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}"; - - modsContainer.Clear(); - foreach (Mod mod in score.Mods) - modsContainer.Add(new ModIcon(mod) - { - AutoSizeAxes = Axes.Both, - Scale = new Vector2(0.45f), - }); - } - } - - public DrawableTopScore() - { - RelativeSizeAxes = Axes.X; - Height = height; - CornerRadius = 5; - BorderThickness = 4; - Masking = true; - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, //used for correct border representation - }, - avatar = new UpdateableAvatar - { - Size = new Vector2(avatar_size), - Masking = true, - CornerRadius = 5, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.25f), - Offset = new Vector2(0, 2), - Radius = 1, - }, - Margin = new MarginPadding { Top = margin, Left = margin } - }, - flag = new DrawableFlag - { - Size = new Vector2(30, 20), - Position = new Vector2(margin * 2 + avatar_size, height / 4), - }, - username = new ClickableUsername - { - Origin = Anchor.BottomLeft, - TextSize = 30, - Position = new Vector2(margin * 2 + avatar_size, height / 4), - Margin = new MarginPadding { Bottom = 4 } - }, - rankText = new OsuSpriteText - { - Anchor = Anchor.TopRight, - Origin = Anchor.BottomRight, - Text = "#1", - TextSize = 40, - Font = @"Exo2.0-BoldItalic", - Y = height / 4, - Margin = new MarginPadding { Right = margin } - }, - date = new OsuSpriteText - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Y = height / 4, - Margin = new MarginPadding { Right = margin } - }, - new Container - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.Both, - Height = 0.5f, - Children = new Drawable[] - { - bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, - middleLine = new Box - { - RelativeSizeAxes = Axes.X, - Height = 1, - }, - rank = new DrawableRank(ScoreRank.F) - { - Origin = Anchor.BottomLeft, - Size = new Vector2(avatar_size, 40), - FillMode = FillMode.Fit, - Y = height / 4, - Margin = new MarginPadding { Left = margin } - }, - new FillFlowContainer - { - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, - Position = new Vector2(height / 2, height / 4), - Direction = FillDirection.Horizontal, - Spacing = new Vector2(15, 0), - Children = new[] - { - totalScore = new InfoColumn("Score"), - accuracy = new InfoColumn("Accuracy"), - statistics = new InfoColumn("300/100/50"), - }, - }, - modsContainer = new ScoreModsContainer - { - AutoSizeAxes = Axes.Y, - Width = 80, - Position = new Vector2(height / 2, height / 4), - } - } - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = bottomBackground.Colour = colours.Gray4; - middleLine.Colour = colours.Gray2; - date.Colour = colours.Gray9; - BorderColour = rankText.Colour = colours.Yellow; - } - - protected override bool OnHover(InputState state) - { - background.FadeIn(fade_duration, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - background.FadeOut(fade_duration, Easing.OutQuint); - base.OnHoverLost(state); - } - - private class InfoColumn : FillFlowContainer - { - private readonly OsuSpriteText headerText; - private readonly OsuSpriteText valueText; - - public string Value - { - set - { - if (valueText.Text == value) - return; - valueText.Text = value; - } - get { return valueText.Text; } - } - - public InfoColumn(string header) - { - AutoSizeAxes = Axes.Both; - Direction = FillDirection.Vertical; - Spacing = new Vector2(0, 3); - Children = new Drawable[] - { - headerText = new OsuSpriteText - { - TextSize = 14, - Text = header, - Font = @"Exo2.0-Bold", - }, - valueText = new OsuSpriteText - { - TextSize = 25, - Font = @"Exo2.0-RegularItalic", - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - headerText.Colour = colours.Gray9; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections.Ranks; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Users; + +namespace osu.Game.Overlays.BeatmapSet.Scores +{ + public class DrawableTopScore : Container + { + private const float fade_duration = 100; + private const float height = 200; + private const float avatar_size = 80; + private const float margin = 10; + + private readonly Box background; + private readonly Box bottomBackground; + private readonly Box middleLine; + private readonly UpdateableAvatar avatar; + private readonly DrawableFlag flag; + private readonly ClickableUsername username; + private readonly OsuSpriteText rankText; + private readonly OsuSpriteText date; + private readonly DrawableRank rank; + private readonly InfoColumn totalScore; + private readonly InfoColumn accuracy; + private readonly InfoColumn statistics; + private readonly ScoreModsContainer modsContainer; + + private OnlineScore score; + public OnlineScore Score + { + get { return score; } + set + { + if (score == value) return; + score = value; + + avatar.User = username.User = score.User; + flag.Country = score.User.Country; + date.Text = $@"achieved {score.Date:MMM d, yyyy}"; + rank.UpdateRank(score.Rank); + + totalScore.Value = $@"{score.TotalScore:N0}"; + accuracy.Value = $@"{score.Accuracy:P2}"; + statistics.Value = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}"; + + modsContainer.Clear(); + foreach (Mod mod in score.Mods) + modsContainer.Add(new ModIcon(mod) + { + AutoSizeAxes = Axes.Both, + Scale = new Vector2(0.45f), + }); + } + } + + public DrawableTopScore() + { + RelativeSizeAxes = Axes.X; + Height = height; + CornerRadius = 5; + BorderThickness = 4; + Masking = true; + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, //used for correct border representation + }, + avatar = new UpdateableAvatar + { + Size = new Vector2(avatar_size), + Masking = true, + CornerRadius = 5, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Offset = new Vector2(0, 2), + Radius = 1, + }, + Margin = new MarginPadding { Top = margin, Left = margin } + }, + flag = new DrawableFlag + { + Size = new Vector2(30, 20), + Position = new Vector2(margin * 2 + avatar_size, height / 4), + }, + username = new ClickableUsername + { + Origin = Anchor.BottomLeft, + TextSize = 30, + Position = new Vector2(margin * 2 + avatar_size, height / 4), + Margin = new MarginPadding { Bottom = 4 } + }, + rankText = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.BottomRight, + Text = "#1", + TextSize = 40, + Font = @"Exo2.0-BoldItalic", + Y = height / 4, + Margin = new MarginPadding { Right = margin } + }, + date = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Y = height / 4, + Margin = new MarginPadding { Right = margin } + }, + new Container + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Children = new Drawable[] + { + bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, + middleLine = new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + }, + rank = new DrawableRank(ScoreRank.F) + { + Origin = Anchor.BottomLeft, + Size = new Vector2(avatar_size, 40), + FillMode = FillMode.Fit, + Y = height / 4, + Margin = new MarginPadding { Left = margin } + }, + new FillFlowContainer + { + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Position = new Vector2(height / 2, height / 4), + Direction = FillDirection.Horizontal, + Spacing = new Vector2(15, 0), + Children = new[] + { + totalScore = new InfoColumn("Score"), + accuracy = new InfoColumn("Accuracy"), + statistics = new InfoColumn("300/100/50"), + }, + }, + modsContainer = new ScoreModsContainer + { + AutoSizeAxes = Axes.Y, + Width = 80, + Position = new Vector2(height / 2, height / 4), + } + } + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = bottomBackground.Colour = colours.Gray4; + middleLine.Colour = colours.Gray2; + date.Colour = colours.Gray9; + BorderColour = rankText.Colour = colours.Yellow; + } + + protected override bool OnHover(InputState state) + { + background.FadeIn(fade_duration, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + background.FadeOut(fade_duration, Easing.OutQuint); + base.OnHoverLost(state); + } + + private class InfoColumn : FillFlowContainer + { + private readonly OsuSpriteText headerText; + private readonly OsuSpriteText valueText; + + public string Value + { + set + { + if (valueText.Text == value) + return; + valueText.Text = value; + } + get { return valueText.Text; } + } + + public InfoColumn(string header) + { + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Vertical; + Spacing = new Vector2(0, 3); + Children = new Drawable[] + { + headerText = new OsuSpriteText + { + TextSize = 14, + Text = header, + Font = @"Exo2.0-Bold", + }, + valueText = new OsuSpriteText + { + TextSize = 25, + Font = @"Exo2.0-RegularItalic", + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + headerText.Colour = colours.Gray9; + } + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index b6efd3b3b0..d5c5bd8ddd 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -1,115 +1,115 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API.Requests; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Overlays.BeatmapSet.Scores -{ - public class ScoresContainer : Container - { - private const int spacing = 15; - private const int fade_duration = 200; - - private readonly FillFlowContainer flow; - private readonly DrawableTopScore topScore; - private readonly LoadingAnimation loadingAnimation; - private readonly Box foreground; - - private bool isLoading; - public bool IsLoading - { - get { return isLoading; } - set - { - if (isLoading == value) return; - isLoading = value; - - foreground.FadeTo(isLoading ? 1 : 0, fade_duration); - loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration); - } - } - - private IEnumerable scores; - public IEnumerable Scores - { - get { return scores; } - set - { - scores = value; - var scoresAmount = scores.Count(); - if (scoresAmount == 0) - { - CleanAllScores(); - return; - } - - topScore.Score = scores.FirstOrDefault(); - topScore.Show(); - - flow.Clear(); - - if (scoresAmount < 2) - return; - - for (int i = 1; i < scoresAmount; i++) - flow.Add(new DrawableScore(i, scores.ElementAt(i))); - } - } - - public ScoresContainer() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.95f, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, spacing), - Margin = new MarginPadding { Vertical = spacing }, - Children = new Drawable[] - { - topScore = new DrawableTopScore(), - flow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 1), - }, - } - }, - foreground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.7f), - Alpha = 0, - }, - loadingAnimation = new LoadingAnimation - { - Alpha = 0, - }, - }; - } - - public void CleanAllScores() - { - topScore.Hide(); - flow.Clear(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Overlays.BeatmapSet.Scores +{ + public class ScoresContainer : Container + { + private const int spacing = 15; + private const int fade_duration = 200; + + private readonly FillFlowContainer flow; + private readonly DrawableTopScore topScore; + private readonly LoadingAnimation loadingAnimation; + private readonly Box foreground; + + private bool isLoading; + public bool IsLoading + { + get { return isLoading; } + set + { + if (isLoading == value) return; + isLoading = value; + + foreground.FadeTo(isLoading ? 1 : 0, fade_duration); + loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration); + } + } + + private IEnumerable scores; + public IEnumerable Scores + { + get { return scores; } + set + { + scores = value; + var scoresAmount = scores.Count(); + if (scoresAmount == 0) + { + CleanAllScores(); + return; + } + + topScore.Score = scores.FirstOrDefault(); + topScore.Show(); + + flow.Clear(); + + if (scoresAmount < 2) + return; + + for (int i = 1; i < scoresAmount; i++) + flow.Add(new DrawableScore(i, scores.ElementAt(i))); + } + } + + public ScoresContainer() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.95f, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, spacing), + Margin = new MarginPadding { Vertical = spacing }, + Children = new Drawable[] + { + topScore = new DrawableTopScore(), + flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 1), + }, + } + }, + foreground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.7f), + Alpha = 0, + }, + loadingAnimation = new LoadingAnimation + { + Alpha = 0, + }, + }; + } + + public void CleanAllScores() + { + topScore.Hide(); + flow.Clear(); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index bee1e9284b..c64d3988a6 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -1,115 +1,115 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Select.Details; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class SuccessRate : Container - { - private readonly FillFlowContainer header; - private readonly OsuSpriteText successRateLabel, successPercent, graphLabel; - private readonly Bar successRate; - private readonly Container percentContainer; - private readonly FailRetryGraph graph; - - private BeatmapInfo beatmap; - public BeatmapInfo Beatmap - { - get { return beatmap; } - set - { - if (value == beatmap) return; - beatmap = value; - - int passCount = beatmap.OnlineInfo.PassCount; - int playCount = beatmap.OnlineInfo.PlayCount; - - var rate = playCount != 0 ? (float)passCount / playCount : 0; - successPercent.Text = rate.ToString("P0"); - successRate.Length = rate; - percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); - - graph.Metrics = Beatmap.Metrics; - } - } - - public SuccessRate() - { - Children = new Drawable[] - { - header = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - successRateLabel = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = "Success Rate", - TextSize = 13, - }, - successRate = new Bar - { - RelativeSizeAxes = Axes.X, - Height = 5, - Margin = new MarginPadding { Top = 5 }, - }, - percentContainer = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0f, - Child = successPercent = new OsuSpriteText - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopCentre, - Text = @"0%", - TextSize = 13, - }, - }, - graphLabel = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = "Points of Failure", - TextSize = 13, - Margin = new MarginPadding { Vertical = 20 }, - }, - }, - }, - graph = new FailRetryGraph - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5; - successRate.AccentColour = colours.Green; - successRate.BackgroundColour = colours.GrayD; - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - graph.Padding = new MarginPadding { Top = header.DrawHeight }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Select.Details; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class SuccessRate : Container + { + private readonly FillFlowContainer header; + private readonly OsuSpriteText successRateLabel, successPercent, graphLabel; + private readonly Bar successRate; + private readonly Container percentContainer; + private readonly FailRetryGraph graph; + + private BeatmapInfo beatmap; + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + if (value == beatmap) return; + beatmap = value; + + int passCount = beatmap.OnlineInfo.PassCount; + int playCount = beatmap.OnlineInfo.PlayCount; + + var rate = playCount != 0 ? (float)passCount / playCount : 0; + successPercent.Text = rate.ToString("P0"); + successRate.Length = rate; + percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); + + graph.Metrics = Beatmap.Metrics; + } + } + + public SuccessRate() + { + Children = new Drawable[] + { + header = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + successRateLabel = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Success Rate", + TextSize = 13, + }, + successRate = new Bar + { + RelativeSizeAxes = Axes.X, + Height = 5, + Margin = new MarginPadding { Top = 5 }, + }, + percentContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0f, + Child = successPercent = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopCentre, + Text = @"0%", + TextSize = 13, + }, + }, + graphLabel = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Points of Failure", + TextSize = 13, + Margin = new MarginPadding { Vertical = 20 }, + }, + }, + }, + graph = new FailRetryGraph + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5; + successRate.AccentColour = colours.Green; + successRate.BackgroundColour = colours.GrayD; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + graph.Padding = new MarginPadding { Top = header.DrawHeight }; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index f0f8a6ef10..93e10751d9 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -1,157 +1,157 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.BeatmapSet; -using osu.Game.Rulesets; -using osu.Game.Overlays.BeatmapSet.Scores; - -namespace osu.Game.Overlays -{ - public class BeatmapSetOverlay : WaveOverlayContainer - { - public const float X_PADDING = 40; - public const float RIGHT_WIDTH = 275; - - private readonly Header header; - private readonly Info info; - private readonly ScoresContainer scores; - - private APIAccess api; - private RulesetStore rulesets; - private GetScoresRequest getScoresRequest; - - private readonly ScrollContainer scroll; - - // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - - public BeatmapSetOverlay() - { - FirstWaveColour = OsuColour.Gray(0.4f); - SecondWaveColour = OsuColour.Gray(0.3f); - ThirdWaveColour = OsuColour.Gray(0.2f); - FourthWaveColour = OsuColour.Gray(0.1f); - - Anchor = Anchor.TopCentre; - Origin = Anchor.TopCentre; - RelativeSizeAxes = Axes.Both; - Width = 0.85f; - - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0), - Type = EdgeEffectType.Shadow, - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f) - }, - scroll = new ScrollContainer - { - RelativeSizeAxes = Axes.Both, - ScrollbarVisible = false, - Child = new ReverseChildIDFillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - header = new Header(), - info = new Info(), - scores = new ScoresContainer(), - }, - }, - }, - }; - - header.Picker.Beatmap.ValueChanged += b => - { - info.Beatmap = b; - updateScores(b); - }; - } - - private void updateScores(BeatmapInfo beatmap) - { - getScoresRequest?.Cancel(); - - if (!beatmap.OnlineBeatmapID.HasValue) - { - scores.CleanAllScores(); - return; - } - - scores.IsLoading = true; - - getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += r => - { - scores.Scores = r.Scores; - scores.IsLoading = false; - }; - api.Queue(getScoresRequest); - } - - [BackgroundDependencyLoader] - private void load(APIAccess api, RulesetStore rulesets) - { - this.api = api; - this.rulesets = rulesets; - } - - protected override void PopIn() - { - base.PopIn(); - FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In); - } - - protected override void PopOut() - { - base.PopOut(); - header.Details.StopPreview(); - FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); - } - - protected override bool OnClick(InputState state) - { - State = Visibility.Hidden; - return true; - } - - public void ShowBeatmapSet(int beatmapSetId) - { - // todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. - var req = new GetBeatmapSetRequest(beatmapSetId); - req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets)); - api.Queue(req); - } - - public void ShowBeatmapSet(BeatmapSetInfo set) - { - header.BeatmapSet = info.BeatmapSet = set; - Show(); - scroll.ScrollTo(0); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.BeatmapSet; +using osu.Game.Rulesets; +using osu.Game.Overlays.BeatmapSet.Scores; + +namespace osu.Game.Overlays +{ + public class BeatmapSetOverlay : WaveOverlayContainer + { + public const float X_PADDING = 40; + public const float RIGHT_WIDTH = 275; + + private readonly Header header; + private readonly Info info; + private readonly ScoresContainer scores; + + private APIAccess api; + private RulesetStore rulesets; + private GetScoresRequest getScoresRequest; + + private readonly ScrollContainer scroll; + + // receive input outside our bounds so we can trigger a close event on ourselves. + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + + public BeatmapSetOverlay() + { + FirstWaveColour = OsuColour.Gray(0.4f); + SecondWaveColour = OsuColour.Gray(0.3f); + ThirdWaveColour = OsuColour.Gray(0.2f); + FourthWaveColour = OsuColour.Gray(0.1f); + + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + RelativeSizeAxes = Axes.Both; + Width = 0.85f; + + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0), + Type = EdgeEffectType.Shadow, + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }, + scroll = new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + Child = new ReverseChildIDFillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + header = new Header(), + info = new Info(), + scores = new ScoresContainer(), + }, + }, + }, + }; + + header.Picker.Beatmap.ValueChanged += b => + { + info.Beatmap = b; + updateScores(b); + }; + } + + private void updateScores(BeatmapInfo beatmap) + { + getScoresRequest?.Cancel(); + + if (!beatmap.OnlineBeatmapID.HasValue) + { + scores.CleanAllScores(); + return; + } + + scores.IsLoading = true; + + getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); + getScoresRequest.Success += r => + { + scores.Scores = r.Scores; + scores.IsLoading = false; + }; + api.Queue(getScoresRequest); + } + + [BackgroundDependencyLoader] + private void load(APIAccess api, RulesetStore rulesets) + { + this.api = api; + this.rulesets = rulesets; + } + + protected override void PopIn() + { + base.PopIn(); + FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In); + } + + protected override void PopOut() + { + base.PopOut(); + header.Details.StopPreview(); + FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); + } + + protected override bool OnClick(InputState state) + { + State = Visibility.Hidden; + return true; + } + + public void ShowBeatmapSet(int beatmapSetId) + { + // todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. + var req = new GetBeatmapSetRequest(beatmapSetId); + req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets)); + api.Queue(req); + } + + public void ShowBeatmapSet(BeatmapSetInfo set) + { + header.BeatmapSet = info.BeatmapSet = set; + Show(); + scroll.ScrollTo(0); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs index 19418c63a8..910c87e0a8 100644 --- a/osu.Game/Overlays/Chat/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelListItem.cs @@ -1,191 +1,191 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.Chat; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Chat -{ - public class ChannelListItem : OsuClickableContainer, IFilterable - { - private const float width_padding = 5; - private const float channel_width = 150; - private const float text_size = 15; - private const float transition_duration = 100; - - private readonly Channel channel; - - private readonly Bindable joinedBind = new Bindable(); - private readonly OsuSpriteText name; - private readonly OsuSpriteText topic; - private readonly SpriteIcon joinedCheckmark; - - private Color4 joinedColour; - private Color4 topicColour; - private Color4 hoverColour; - - public IEnumerable FilterTerms => new[] { channel.Name }; - public bool MatchingFilter - { - set - { - this.FadeTo(value ? 1f : 0f, 100); - } - } - - public Action OnRequestJoin; - public Action OnRequestLeave; - - public ChannelListItem(Channel channel) - { - this.channel = channel; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Action = () => { (channel.Joined ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); }; - - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new Container - { - Children = new[] - { - joinedCheckmark = new SpriteIcon - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Icon = FontAwesome.fa_check_circle, - Size = new Vector2(text_size), - Shadow = false, - Margin = new MarginPadding { Right = 10f }, - }, - }, - }, - new Container - { - Width = channel_width, - AutoSizeAxes = Axes.Y, - Children = new[] - { - name = new OsuSpriteText - { - Text = channel.ToString(), - TextSize = text_size, - Font = @"Exo2.0-Bold", - Shadow = false, - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.X, - Width = 0.7f, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Left = width_padding }, - Children = new[] - { - topic = new OsuSpriteText - { - Text = channel.Topic, - TextSize = text_size, - Font = @"Exo2.0-SemiBold", - Shadow = false, - }, - }, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Left = width_padding }, - Spacing = new Vector2(3f, 0f), - Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.fa_user, - Size = new Vector2(text_size - 2), - Shadow = false, - Margin = new MarginPadding { Top = 1 }, - }, - new OsuSpriteText - { - Text = @"0", - TextSize = text_size, - Font = @"Exo2.0-SemiBold", - Shadow = false, - }, - }, - }, - }, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - topicColour = colours.Gray9; - joinedColour = colours.Blue; - hoverColour = colours.Yellow; - - joinedBind.ValueChanged += updateColour; - joinedBind.BindTo(channel.Joined); - - joinedBind.TriggerChange(); - FinishTransforms(true); - } - - protected override bool OnHover(InputState state) - { - if (!channel.Joined.Value) - name.FadeColour(hoverColour, 50, Easing.OutQuint); - - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - if (!channel.Joined.Value) - name.FadeColour(Color4.White, transition_duration); - } - - private void updateColour(bool joined) - { - if (joined) - { - name.FadeColour(Color4.White, transition_duration); - joinedCheckmark.FadeTo(1f, transition_duration); - topic.FadeTo(0.8f, transition_duration); - topic.FadeColour(Color4.White, transition_duration); - this.FadeColour(joinedColour, transition_duration); - } - else - { - joinedCheckmark.FadeTo(0f, transition_duration); - topic.FadeTo(1f, transition_duration); - topic.FadeColour(topicColour, transition_duration); - this.FadeColour(Color4.White, transition_duration); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelListItem : OsuClickableContainer, IFilterable + { + private const float width_padding = 5; + private const float channel_width = 150; + private const float text_size = 15; + private const float transition_duration = 100; + + private readonly Channel channel; + + private readonly Bindable joinedBind = new Bindable(); + private readonly OsuSpriteText name; + private readonly OsuSpriteText topic; + private readonly SpriteIcon joinedCheckmark; + + private Color4 joinedColour; + private Color4 topicColour; + private Color4 hoverColour; + + public IEnumerable FilterTerms => new[] { channel.Name }; + public bool MatchingFilter + { + set + { + this.FadeTo(value ? 1f : 0f, 100); + } + } + + public Action OnRequestJoin; + public Action OnRequestLeave; + + public ChannelListItem(Channel channel) + { + this.channel = channel; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Action = () => { (channel.Joined ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); }; + + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new Container + { + Children = new[] + { + joinedCheckmark = new SpriteIcon + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Icon = FontAwesome.fa_check_circle, + Size = new Vector2(text_size), + Shadow = false, + Margin = new MarginPadding { Right = 10f }, + }, + }, + }, + new Container + { + Width = channel_width, + AutoSizeAxes = Axes.Y, + Children = new[] + { + name = new OsuSpriteText + { + Text = channel.ToString(), + TextSize = text_size, + Font = @"Exo2.0-Bold", + Shadow = false, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + Width = 0.7f, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Left = width_padding }, + Children = new[] + { + topic = new OsuSpriteText + { + Text = channel.Topic, + TextSize = text_size, + Font = @"Exo2.0-SemiBold", + Shadow = false, + }, + }, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = width_padding }, + Spacing = new Vector2(3f, 0f), + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.fa_user, + Size = new Vector2(text_size - 2), + Shadow = false, + Margin = new MarginPadding { Top = 1 }, + }, + new OsuSpriteText + { + Text = @"0", + TextSize = text_size, + Font = @"Exo2.0-SemiBold", + Shadow = false, + }, + }, + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + topicColour = colours.Gray9; + joinedColour = colours.Blue; + hoverColour = colours.Yellow; + + joinedBind.ValueChanged += updateColour; + joinedBind.BindTo(channel.Joined); + + joinedBind.TriggerChange(); + FinishTransforms(true); + } + + protected override bool OnHover(InputState state) + { + if (!channel.Joined.Value) + name.FadeColour(hoverColour, 50, Easing.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (!channel.Joined.Value) + name.FadeColour(Color4.White, transition_duration); + } + + private void updateColour(bool joined) + { + if (joined) + { + name.FadeColour(Color4.White, transition_duration); + joinedCheckmark.FadeTo(1f, transition_duration); + topic.FadeTo(0.8f, transition_duration); + topic.FadeColour(Color4.White, transition_duration); + this.FadeColour(joinedColour, transition_duration); + } + else + { + joinedCheckmark.FadeTo(0f, transition_duration); + topic.FadeTo(1f, transition_duration); + topic.FadeColour(topicColour, transition_duration); + this.FadeColour(Color4.White, transition_duration); + } + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelSection.cs b/osu.Game/Overlays/Chat/ChannelSection.cs index 132891bcc0..89d9d2231c 100644 --- a/osu.Game/Overlays/Chat/ChannelSection.cs +++ b/osu.Game/Overlays/Chat/ChannelSection.cs @@ -1,63 +1,63 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.Chat; - -namespace osu.Game.Overlays.Chat -{ - public class ChannelSection : Container, IHasFilterableChildren - { - private readonly OsuSpriteText header; - - public readonly FillFlowContainer ChannelFlow; - - public IEnumerable FilterableChildren => ChannelFlow.Children; - public IEnumerable FilterTerms => new[] { Header }; - public bool MatchingFilter - { - set - { - this.FadeTo(value ? 1f : 0f, 100); - } - } - - public string Header - { - get { return header.Text; } - set { header.Text = value.ToUpper(); } - } - - public IEnumerable Channels - { - set { ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c)); } - } - - public ChannelSection() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Children = new Drawable[] - { - header = new OsuSpriteText - { - TextSize = 15, - Font = @"Exo2.0-Bold", - }, - ChannelFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 25 }, - Spacing = new Vector2(0f, 5f), - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelSection : Container, IHasFilterableChildren + { + private readonly OsuSpriteText header; + + public readonly FillFlowContainer ChannelFlow; + + public IEnumerable FilterableChildren => ChannelFlow.Children; + public IEnumerable FilterTerms => new[] { Header }; + public bool MatchingFilter + { + set + { + this.FadeTo(value ? 1f : 0f, 100); + } + } + + public string Header + { + get { return header.Text; } + set { header.Text = value.ToUpper(); } + } + + public IEnumerable Channels + { + set { ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c)); } + } + + public ChannelSection() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + header = new OsuSpriteText + { + TextSize = 15, + Font = @"Exo2.0-Bold", + }, + ChannelFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 25 }, + Spacing = new Vector2(0f, 5f), + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs index 3684c47e40..57f2cd405d 100644 --- a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs @@ -1,185 +1,185 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Chat; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Chat -{ - public class ChannelSelectionOverlay : OsuFocusedOverlayContainer - { - public static readonly float WIDTH_PADDING = 170; - - private const float transition_duration = 500; - - private readonly Box bg; - private readonly Triangles triangles; - private readonly Box headerBg; - private readonly SearchTextBox search; - private readonly SearchContainer sectionsFlow; - - public Action OnRequestJoin; - public Action OnRequestLeave; - - public IEnumerable Sections - { - set - { - sectionsFlow.ChildrenEnumerable = value; - - foreach (ChannelSection s in sectionsFlow.Children) - { - foreach (ChannelListItem c in s.ChannelFlow.Children) - { - c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); }; - c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); }; - } - } - } - } - - public ChannelSelectionOverlay() - { - RelativeSizeAxes = Axes.X; - - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] - { - bg = new Box - { - RelativeSizeAxes = Axes.Both, - }, - triangles = new Triangles - { - RelativeSizeAxes = Axes.Both, - TriangleScale = 5, - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 85, Right = WIDTH_PADDING }, - Children = new[] - { - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - Children = new[] - { - sectionsFlow = new SearchContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - LayoutDuration = 200, - LayoutEasing = Easing.OutQuint, - Spacing = new Vector2(0f, 20f), - Padding = new MarginPadding { Vertical = 20, Left = WIDTH_PADDING }, - }, - }, - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - headerBg = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0f, 10f), - Padding = new MarginPadding { Top = 10f, Bottom = 10f, Left = WIDTH_PADDING, Right = WIDTH_PADDING }, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = @"Chat Channels", - TextSize = 20, - Shadow = false, - }, - search = new HeaderSearchTextBox - { - RelativeSizeAxes = Axes.X, - PlaceholderText = @"Search", - Exit = Hide, - }, - }, - }, - }, - }, - }; - - search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - bg.Colour = colours.Gray3; - triangles.ColourDark = colours.Gray3; - triangles.ColourLight = OsuColour.FromHex(@"353535"); - - headerBg.Colour = colours.Gray2.Opacity(0.75f); - } - - protected override void OnFocus(InputState state) - { - GetContainingInputManager().ChangeFocus(search); - base.OnFocus(state); - } - - protected override void PopIn() - { - if (Alpha == 0) this.MoveToY(DrawHeight); - - this.FadeIn(transition_duration, Easing.OutQuint); - this.MoveToY(0, transition_duration, Easing.OutQuint); - - search.HoldFocus = true; - base.PopIn(); - } - - protected override void PopOut() - { - this.FadeOut(transition_duration, Easing.InSine); - this.MoveToY(DrawHeight, transition_duration, Easing.InSine); - - search.HoldFocus = false; - base.PopOut(); - } - - private class HeaderSearchTextBox : SearchTextBox - { - protected override Color4 BackgroundFocused => Color4.Black.Opacity(0.2f); - protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.2f); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Chat; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelSelectionOverlay : OsuFocusedOverlayContainer + { + public static readonly float WIDTH_PADDING = 170; + + private const float transition_duration = 500; + + private readonly Box bg; + private readonly Triangles triangles; + private readonly Box headerBg; + private readonly SearchTextBox search; + private readonly SearchContainer sectionsFlow; + + public Action OnRequestJoin; + public Action OnRequestLeave; + + public IEnumerable Sections + { + set + { + sectionsFlow.ChildrenEnumerable = value; + + foreach (ChannelSection s in sectionsFlow.Children) + { + foreach (ChannelListItem c in s.ChannelFlow.Children) + { + c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); }; + c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); }; + } + } + } + } + + public ChannelSelectionOverlay() + { + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + bg = new Box + { + RelativeSizeAxes = Axes.Both, + }, + triangles = new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 5, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 85, Right = WIDTH_PADDING }, + Children = new[] + { + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + sectionsFlow = new SearchContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + LayoutDuration = 200, + LayoutEasing = Easing.OutQuint, + Spacing = new Vector2(0f, 20f), + Padding = new MarginPadding { Vertical = 20, Left = WIDTH_PADDING }, + }, + }, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + headerBg = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Padding = new MarginPadding { Top = 10f, Bottom = 10f, Left = WIDTH_PADDING, Right = WIDTH_PADDING }, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"Chat Channels", + TextSize = 20, + Shadow = false, + }, + search = new HeaderSearchTextBox + { + RelativeSizeAxes = Axes.X, + PlaceholderText = @"Search", + Exit = Hide, + }, + }, + }, + }, + }, + }; + + search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + bg.Colour = colours.Gray3; + triangles.ColourDark = colours.Gray3; + triangles.ColourLight = OsuColour.FromHex(@"353535"); + + headerBg.Colour = colours.Gray2.Opacity(0.75f); + } + + protected override void OnFocus(InputState state) + { + GetContainingInputManager().ChangeFocus(search); + base.OnFocus(state); + } + + protected override void PopIn() + { + if (Alpha == 0) this.MoveToY(DrawHeight); + + this.FadeIn(transition_duration, Easing.OutQuint); + this.MoveToY(0, transition_duration, Easing.OutQuint); + + search.HoldFocus = true; + base.PopIn(); + } + + protected override void PopOut() + { + this.FadeOut(transition_duration, Easing.InSine); + this.MoveToY(DrawHeight, transition_duration, Easing.InSine); + + search.HoldFocus = false; + base.PopOut(); + } + + private class HeaderSearchTextBox : SearchTextBox + { + protected override Color4 BackgroundFocused => Color4.Black.Opacity(0.2f); + protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.2f); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index dd41dd5428..1fccaeb123 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -1,256 +1,256 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.Chat; -using osu.Game.Users; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Chat -{ - public class ChatLine : Container - { - private static readonly Color4[] username_colours = - { - OsuColour.FromHex("588c7e"), - OsuColour.FromHex("b2a367"), - OsuColour.FromHex("c98f65"), - OsuColour.FromHex("bc5151"), - OsuColour.FromHex("5c8bd6"), - OsuColour.FromHex("7f6ab7"), - OsuColour.FromHex("a368ad"), - OsuColour.FromHex("aa6880"), - - OsuColour.FromHex("6fad9b"), - OsuColour.FromHex("f2e394"), - OsuColour.FromHex("f2ae72"), - OsuColour.FromHex("f98f8a"), - OsuColour.FromHex("7daef4"), - OsuColour.FromHex("a691f2"), - OsuColour.FromHex("c894d3"), - OsuColour.FromHex("d895b0"), - - OsuColour.FromHex("53c4a1"), - OsuColour.FromHex("eace5c"), - OsuColour.FromHex("ea8c47"), - OsuColour.FromHex("fc4f4f"), - OsuColour.FromHex("3d94ea"), - OsuColour.FromHex("7760ea"), - OsuColour.FromHex("af52c6"), - OsuColour.FromHex("e25696"), - - OsuColour.FromHex("677c66"), - OsuColour.FromHex("9b8732"), - OsuColour.FromHex("8c5129"), - OsuColour.FromHex("8c3030"), - OsuColour.FromHex("1f5d91"), - OsuColour.FromHex("4335a5"), - OsuColour.FromHex("812a96"), - OsuColour.FromHex("992861"), - }; - - public const float LEFT_PADDING = message_padding + padding * 2; - - private const float padding = 15; - private const float message_padding = 200; - private const float action_padding = 3; - private const float text_size = 20; - - private Color4 customUsernameColour; - - private OsuSpriteText timestamp; - - public ChatLine(Message message) - { - Message = message; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding { Left = padding, Right = padding }; - } - - private Message message; - private OsuSpriteText username; - private LinkFlowContainer contentFlow; - - public LinkFlowContainer ContentFlow => contentFlow; - - public Message Message - { - get => message; - set - { - if (message == value) return; - - message = MessageFormatter.FormatMessage(value); - - if (!IsLoaded) - return; - - updateMessageContent(); - } - } - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, ChatOverlay chat) - { - this.chat = chat; - customUsernameColour = colours.ChatBlue; - } - - private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour); - - protected override void LoadComplete() - { - base.LoadComplete(); - - bool hasBackground = senderHasBackground; - - Drawable effectedUsername = username = new OsuSpriteText - { - Font = @"Exo2.0-BoldItalic", - Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length], - TextSize = text_size, - }; - - if (hasBackground) - { - // Background effect - effectedUsername = new Container - { - AutoSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 4, - EdgeEffect = new EdgeEffectParameters - { - Roundness = 1, - Offset = new Vector2(0, 3), - Radius = 3, - Colour = Color4.Black.Opacity(0.3f), - Type = EdgeEffectType.Shadow, - }, - // Drop shadow effect - Child = new Container - { - AutoSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 4, - EdgeEffect = new EdgeEffectParameters - { - Radius = 1, - Colour = OsuColour.FromHex(message.Sender.Colour), - Type = EdgeEffectType.Shadow, - }, - Padding = new MarginPadding { Left = 3, Right = 3, Bottom = 1, Top = -3 }, - Y = 3, - Child = username, - } - }; - } - - Children = new Drawable[] - { - new Container - { - Size = new Vector2(message_padding, text_size), - Children = new Drawable[] - { - timestamp = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = @"Exo2.0-SemiBold", - FixedWidth = true, - TextSize = text_size * 0.75f, - }, - new MessageSender(message.Sender) - { - AutoSizeAxes = Axes.Both, - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Child = effectedUsername, - }, - } - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = message_padding + padding }, - Children = new Drawable[] - { - contentFlow = new LinkFlowContainer(t => - { - if (Message.IsAction) - { - t.Font = @"Exo2.0-MediumItalic"; - - if (senderHasBackground) - t.Colour = OsuColour.FromHex(message.Sender.Colour); - } - - t.TextSize = text_size; - }) - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - } - } - } - }; - - updateMessageContent(); - FinishTransforms(true); - } - - private ChatOverlay chat; - - private void updateMessageContent() - { - this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint); - timestamp.FadeTo(message is LocalEchoMessage ? 0 : 1, 500, Easing.OutQuint); - - timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}"; - username.Text = $@"{message.Sender.Username}" + (senderHasBackground || message.IsAction ? "" : ":"); - - // remove non-existent channels from the link list - message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chat?.AvailableChannels.Any(c => c.Name == link.Argument) != true); - - contentFlow.Clear(); - contentFlow.AddLinks(message.DisplayContent, message.Links); - } - - private class MessageSender : OsuClickableContainer, IHasContextMenu - { - private readonly User sender; - - public MessageSender(User sender) - { - this.sender = sender; - } - - [BackgroundDependencyLoader(true)] - private void load(UserProfileOverlay profile) - { - Action = () => profile?.ShowUser(sender); - } - - public MenuItem[] ContextMenuItems => new MenuItem[] - { - new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osu.Game.Users; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Chat +{ + public class ChatLine : Container + { + private static readonly Color4[] username_colours = + { + OsuColour.FromHex("588c7e"), + OsuColour.FromHex("b2a367"), + OsuColour.FromHex("c98f65"), + OsuColour.FromHex("bc5151"), + OsuColour.FromHex("5c8bd6"), + OsuColour.FromHex("7f6ab7"), + OsuColour.FromHex("a368ad"), + OsuColour.FromHex("aa6880"), + + OsuColour.FromHex("6fad9b"), + OsuColour.FromHex("f2e394"), + OsuColour.FromHex("f2ae72"), + OsuColour.FromHex("f98f8a"), + OsuColour.FromHex("7daef4"), + OsuColour.FromHex("a691f2"), + OsuColour.FromHex("c894d3"), + OsuColour.FromHex("d895b0"), + + OsuColour.FromHex("53c4a1"), + OsuColour.FromHex("eace5c"), + OsuColour.FromHex("ea8c47"), + OsuColour.FromHex("fc4f4f"), + OsuColour.FromHex("3d94ea"), + OsuColour.FromHex("7760ea"), + OsuColour.FromHex("af52c6"), + OsuColour.FromHex("e25696"), + + OsuColour.FromHex("677c66"), + OsuColour.FromHex("9b8732"), + OsuColour.FromHex("8c5129"), + OsuColour.FromHex("8c3030"), + OsuColour.FromHex("1f5d91"), + OsuColour.FromHex("4335a5"), + OsuColour.FromHex("812a96"), + OsuColour.FromHex("992861"), + }; + + public const float LEFT_PADDING = message_padding + padding * 2; + + private const float padding = 15; + private const float message_padding = 200; + private const float action_padding = 3; + private const float text_size = 20; + + private Color4 customUsernameColour; + + private OsuSpriteText timestamp; + + public ChatLine(Message message) + { + Message = message; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding { Left = padding, Right = padding }; + } + + private Message message; + private OsuSpriteText username; + private LinkFlowContainer contentFlow; + + public LinkFlowContainer ContentFlow => contentFlow; + + public Message Message + { + get => message; + set + { + if (message == value) return; + + message = MessageFormatter.FormatMessage(value); + + if (!IsLoaded) + return; + + updateMessageContent(); + } + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, ChatOverlay chat) + { + this.chat = chat; + customUsernameColour = colours.ChatBlue; + } + + private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour); + + protected override void LoadComplete() + { + base.LoadComplete(); + + bool hasBackground = senderHasBackground; + + Drawable effectedUsername = username = new OsuSpriteText + { + Font = @"Exo2.0-BoldItalic", + Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length], + TextSize = text_size, + }; + + if (hasBackground) + { + // Background effect + effectedUsername = new Container + { + AutoSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + EdgeEffect = new EdgeEffectParameters + { + Roundness = 1, + Offset = new Vector2(0, 3), + Radius = 3, + Colour = Color4.Black.Opacity(0.3f), + Type = EdgeEffectType.Shadow, + }, + // Drop shadow effect + Child = new Container + { + AutoSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + EdgeEffect = new EdgeEffectParameters + { + Radius = 1, + Colour = OsuColour.FromHex(message.Sender.Colour), + Type = EdgeEffectType.Shadow, + }, + Padding = new MarginPadding { Left = 3, Right = 3, Bottom = 1, Top = -3 }, + Y = 3, + Child = username, + } + }; + } + + Children = new Drawable[] + { + new Container + { + Size = new Vector2(message_padding, text_size), + Children = new Drawable[] + { + timestamp = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = @"Exo2.0-SemiBold", + FixedWidth = true, + TextSize = text_size * 0.75f, + }, + new MessageSender(message.Sender) + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Child = effectedUsername, + }, + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = message_padding + padding }, + Children = new Drawable[] + { + contentFlow = new LinkFlowContainer(t => + { + if (Message.IsAction) + { + t.Font = @"Exo2.0-MediumItalic"; + + if (senderHasBackground) + t.Colour = OsuColour.FromHex(message.Sender.Colour); + } + + t.TextSize = text_size; + }) + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + } + } + } + }; + + updateMessageContent(); + FinishTransforms(true); + } + + private ChatOverlay chat; + + private void updateMessageContent() + { + this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint); + timestamp.FadeTo(message is LocalEchoMessage ? 0 : 1, 500, Easing.OutQuint); + + timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}"; + username.Text = $@"{message.Sender.Username}" + (senderHasBackground || message.IsAction ? "" : ":"); + + // remove non-existent channels from the link list + message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chat?.AvailableChannels.Any(c => c.Name == link.Argument) != true); + + contentFlow.Clear(); + contentFlow.AddLinks(message.DisplayContent, message.Links); + } + + private class MessageSender : OsuClickableContainer, IHasContextMenu + { + private readonly User sender; + + public MessageSender(User sender) + { + this.sender = sender; + } + + [BackgroundDependencyLoader(true)] + private void load(UserProfileOverlay profile) + { + Action = () => profile?.ShowUser(sender); + } + + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action), + }; + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 1d3dab249d..77a30e6389 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -1,334 +1,334 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -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.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Chat; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using System; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Chat -{ - public class ChatTabControl : OsuTabControl - { - private const float shear_width = 10; - - public Action OnRequestLeave; - - public readonly Bindable ChannelSelectorActive = new Bindable(); - - private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab; - - public ChatTabControl() - { - TabContainer.Margin = new MarginPadding { Left = 50 }; - TabContainer.Spacing = new Vector2(-shear_width, 0); - TabContainer.Masking = false; - - AddInternal(new SpriteIcon - { - Icon = FontAwesome.fa_comments, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20), - Margin = new MarginPadding(10), - }); - - AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); - - ChannelSelectorActive.BindTo(selectorTab.Active); - } - - protected override void AddTabItem(TabItem item, bool addToDropdown = true) - { - if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) - // performTabSort might've made selectorTab's position wonky, fix it - TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); - - base.AddTabItem(item, addToDropdown); - - if (SelectedTab == null) - SelectTab(item); - } - - protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; - - protected override void SelectTab(TabItem tab) - { - if (tab is ChannelTabItem.ChannelSelectorTabItem) - { - tab.Active.Toggle(); - return; - } - - selectorTab.Active.Value = false; - - base.SelectTab(tab); - } - - private void tabCloseRequested(TabItem tab) - { - int totalTabs = TabContainer.Count - 1; // account for selectorTab - int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); - - if (tab == SelectedTab && totalTabs > 1) - // Select the tab after tab-to-be-removed's index, or the tab before if current == last - SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); - else if (totalTabs == 1 && !selectorTab.Active) - // Open channel selection overlay if all channel tabs will be closed after removing this tab - SelectTab(selectorTab); - - OnRequestLeave?.Invoke(tab.Value); - } - - private class ChannelTabItem : TabItem - { - private Color4 backgroundInactive; - private Color4 backgroundHover; - private Color4 backgroundActive; - - public override bool IsRemovable => !Pinned; - - private readonly SpriteText text; - private readonly SpriteText textBold; - private readonly ClickableContainer closeButton; - private readonly Box box; - private readonly Box highlightBox; - private readonly SpriteIcon icon; - - public Action OnRequestClose; - - private void updateState() - { - if (Active) - fadeActive(); - else - fadeInactive(); - } - - private const float transition_length = 400; - - private void fadeActive() - { - this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint); - - box.FadeColour(backgroundActive, transition_length, Easing.OutQuint); - highlightBox.FadeIn(transition_length, Easing.OutQuint); - - text.FadeOut(transition_length, Easing.OutQuint); - textBold.FadeIn(transition_length, Easing.OutQuint); - } - - private void fadeInactive() - { - this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint); - - box.FadeColour(backgroundInactive, transition_length, Easing.OutQuint); - highlightBox.FadeOut(transition_length, Easing.OutQuint); - - text.FadeIn(transition_length, Easing.OutQuint); - textBold.FadeOut(transition_length, Easing.OutQuint); - } - - protected override bool OnHover(InputState state) - { - if (IsRemovable) - closeButton.FadeIn(200, Easing.OutQuint); - - if (!Active) - box.FadeColour(backgroundHover, transition_length, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(InputState state) - { - closeButton.FadeOut(200, Easing.OutQuint); - updateState(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - backgroundActive = colours.ChatBlue; - backgroundInactive = colours.Gray4; - backgroundHover = colours.Gray7; - - highlightBox.Colour = colours.Yellow; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateState(); - } - - public ChannelTabItem(Channel value) : base(value) - { - Width = 150; - - RelativeSizeAxes = Axes.Y; - - Anchor = Anchor.BottomLeft; - Origin = Anchor.BottomLeft; - - Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); - - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 10, - Colour = Color4.Black.Opacity(0.2f), - }; - - Children = new Drawable[] - { - box = new Box - { - EdgeSmoothness = new Vector2(1, 0), - RelativeSizeAxes = Axes.Both, - }, - highlightBox = new Box - { - Width = 5, - Alpha = 0, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - EdgeSmoothness = new Vector2(1, 0), - RelativeSizeAxes = Axes.Y, - }, - new Container - { - Shear = new Vector2(-shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - icon = new SpriteIcon - { - Icon = FontAwesome.fa_hashtag, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Colour = Color4.Black, - X = -10, - Alpha = 0.2f, - Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), - }, - text = new OsuSpriteText - { - Margin = new MarginPadding(5), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.ToString(), - TextSize = 18, - }, - textBold = new OsuSpriteText - { - Alpha = 0, - Margin = new MarginPadding(5), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = value.ToString(), - Font = @"Exo2.0-Bold", - TextSize = 18, - }, - closeButton = new CloseButton - { - Alpha = 0, - Margin = new MarginPadding { Right = 20 }, - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Action = delegate - { - if (IsRemovable) OnRequestClose?.Invoke(this); - }, - }, - }, - }, - }; - } - - public class CloseButton : OsuClickableContainer - { - private readonly SpriteIcon icon; - - public CloseButton() - { - Size = new Vector2(20); - - Child = icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.75f), - Icon = FontAwesome.fa_close, - RelativeSizeAxes = Axes.Both, - }; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - icon.ScaleTo(0.5f, 1000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - icon.ScaleTo(0.75f, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - - protected override bool OnHover(InputState state) - { - icon.FadeColour(Color4.Red, 200, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - icon.FadeColour(Color4.White, 200, Easing.OutQuint); - base.OnHoverLost(state); - } - } - - public class ChannelSelectorTabItem : ChannelTabItem - { - public override bool IsRemovable => false; - - public ChannelSelectorTabItem(Channel value) : base(value) - { - Depth = float.MaxValue; - Width = 45; - - icon.Alpha = 0; - - text.TextSize = 45; - textBold.TextSize = 45; - } - - [BackgroundDependencyLoader] - private new void load(OsuColour colour) - { - backgroundInactive = colour.Gray2; - backgroundActive = colour.Gray3; - } - } - - protected override void OnActivated() => updateState(); - - protected override void OnDeactivated() => updateState(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +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.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Chat; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using System; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTabControl : OsuTabControl + { + private const float shear_width = 10; + + public Action OnRequestLeave; + + public readonly Bindable ChannelSelectorActive = new Bindable(); + + private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab; + + public ChatTabControl() + { + TabContainer.Margin = new MarginPadding { Left = 50 }; + TabContainer.Spacing = new Vector2(-shear_width, 0); + TabContainer.Masking = false; + + AddInternal(new SpriteIcon + { + Icon = FontAwesome.fa_comments, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + Margin = new MarginPadding(10), + }); + + AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" })); + + ChannelSelectorActive.BindTo(selectorTab.Active); + } + + protected override void AddTabItem(TabItem item, bool addToDropdown = true) + { + if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue) + // performTabSort might've made selectorTab's position wonky, fix it + TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); + + base.AddTabItem(item, addToDropdown); + + if (SelectedTab == null) + SelectTab(item); + } + + protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + + protected override void SelectTab(TabItem tab) + { + if (tab is ChannelTabItem.ChannelSelectorTabItem) + { + tab.Active.Toggle(); + return; + } + + selectorTab.Active.Value = false; + + base.SelectTab(tab); + } + + private void tabCloseRequested(TabItem tab) + { + int totalTabs = TabContainer.Count - 1; // account for selectorTab + int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs); + + if (tab == SelectedTab && totalTabs > 1) + // Select the tab after tab-to-be-removed's index, or the tab before if current == last + SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]); + else if (totalTabs == 1 && !selectorTab.Active) + // Open channel selection overlay if all channel tabs will be closed after removing this tab + SelectTab(selectorTab); + + OnRequestLeave?.Invoke(tab.Value); + } + + private class ChannelTabItem : TabItem + { + private Color4 backgroundInactive; + private Color4 backgroundHover; + private Color4 backgroundActive; + + public override bool IsRemovable => !Pinned; + + private readonly SpriteText text; + private readonly SpriteText textBold; + private readonly ClickableContainer closeButton; + private readonly Box box; + private readonly Box highlightBox; + private readonly SpriteIcon icon; + + public Action OnRequestClose; + + private void updateState() + { + if (Active) + fadeActive(); + else + fadeInactive(); + } + + private const float transition_length = 400; + + private void fadeActive() + { + this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint); + + box.FadeColour(backgroundActive, transition_length, Easing.OutQuint); + highlightBox.FadeIn(transition_length, Easing.OutQuint); + + text.FadeOut(transition_length, Easing.OutQuint); + textBold.FadeIn(transition_length, Easing.OutQuint); + } + + private void fadeInactive() + { + this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint); + + box.FadeColour(backgroundInactive, transition_length, Easing.OutQuint); + highlightBox.FadeOut(transition_length, Easing.OutQuint); + + text.FadeIn(transition_length, Easing.OutQuint); + textBold.FadeOut(transition_length, Easing.OutQuint); + } + + protected override bool OnHover(InputState state) + { + if (IsRemovable) + closeButton.FadeIn(200, Easing.OutQuint); + + if (!Active) + box.FadeColour(backgroundHover, transition_length, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + closeButton.FadeOut(200, Easing.OutQuint); + updateState(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + backgroundActive = colours.ChatBlue; + backgroundInactive = colours.Gray4; + backgroundHover = colours.Gray7; + + highlightBox.Colour = colours.Yellow; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateState(); + } + + public ChannelTabItem(Channel value) : base(value) + { + Width = 150; + + RelativeSizeAxes = Axes.Y; + + Anchor = Anchor.BottomLeft; + Origin = Anchor.BottomLeft; + + Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0); + + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 10, + Colour = Color4.Black.Opacity(0.2f), + }; + + Children = new Drawable[] + { + box = new Box + { + EdgeSmoothness = new Vector2(1, 0), + RelativeSizeAxes = Axes.Both, + }, + highlightBox = new Box + { + Width = 5, + Alpha = 0, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + EdgeSmoothness = new Vector2(1, 0), + RelativeSizeAxes = Axes.Y, + }, + new Container + { + Shear = new Vector2(-shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0), + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + icon = new SpriteIcon + { + Icon = FontAwesome.fa_hashtag, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = Color4.Black, + X = -10, + Alpha = 0.2f, + Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT), + }, + text = new OsuSpriteText + { + Margin = new MarginPadding(5), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = value.ToString(), + TextSize = 18, + }, + textBold = new OsuSpriteText + { + Alpha = 0, + Margin = new MarginPadding(5), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = value.ToString(), + Font = @"Exo2.0-Bold", + TextSize = 18, + }, + closeButton = new CloseButton + { + Alpha = 0, + Margin = new MarginPadding { Right = 20 }, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Action = delegate + { + if (IsRemovable) OnRequestClose?.Invoke(this); + }, + }, + }, + }, + }; + } + + public class CloseButton : OsuClickableContainer + { + private readonly SpriteIcon icon; + + public CloseButton() + { + Size = new Vector2(20); + + Child = icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.75f), + Icon = FontAwesome.fa_close, + RelativeSizeAxes = Axes.Both, + }; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + icon.ScaleTo(0.5f, 1000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + icon.ScaleTo(0.75f, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + + protected override bool OnHover(InputState state) + { + icon.FadeColour(Color4.Red, 200, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + icon.FadeColour(Color4.White, 200, Easing.OutQuint); + base.OnHoverLost(state); + } + } + + public class ChannelSelectorTabItem : ChannelTabItem + { + public override bool IsRemovable => false; + + public ChannelSelectorTabItem(Channel value) : base(value) + { + Depth = float.MaxValue; + Width = 45; + + icon.Alpha = 0; + + text.TextSize = 45; + textBold.TextSize = 45; + } + + [BackgroundDependencyLoader] + private new void load(OsuColour colour) + { + backgroundInactive = colour.Gray2; + backgroundActive = colour.Gray3; + } + } + + protected override void OnActivated() => updateState(); + + protected override void OnDeactivated() => updateState(); + } + } +} diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index d12df70b74..bcc8879902 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -1,134 +1,134 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Cursor; -using osu.Game.Online.Chat; - -namespace osu.Game.Overlays.Chat -{ - public class DrawableChannel : Container - { - public readonly Channel Channel; - private readonly ChatLineContainer flow; - private readonly ScrollContainer scroll; - - public DrawableChannel(Channel channel) - { - Channel = channel; - - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - scroll = new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - // Some chat lines have effects that slightly protrude to the bottom, - // which we do not want to mask away, hence the padding. - Padding = new MarginPadding { Bottom = 5 }, - Child = new OsuContextMenuContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = flow = new ChatLineContainer - { - Padding = new MarginPadding { Left = 20, Right = 20 }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - } - }, - } - }; - - Channel.NewMessagesArrived += newMessagesArrived; - Channel.MessageRemoved += messageRemoved; - Channel.PendingMessageResolved += pendingMessageResolved; - } - - [BackgroundDependencyLoader] - private void load() - { - newMessagesArrived(Channel.Messages); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - scrollToEnd(); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - Channel.NewMessagesArrived -= newMessagesArrived; - Channel.MessageRemoved -= messageRemoved; - Channel.PendingMessageResolved -= pendingMessageResolved; - } - - private void newMessagesArrived(IEnumerable newMessages) - { - // Add up to last Channel.MAX_HISTORY messages - var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); - - flow.AddRange(displayMessages.Select(m => new ChatLine(m))); - - if (!IsLoaded) return; - - if (scroll.IsScrolledToEnd(10) || !flow.Children.Any()) - scrollToEnd(); - - var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); - int count = staleMessages.Length - Channel.MAX_HISTORY; - - for (int i = 0; i < count; i++) - { - var d = staleMessages[i]; - if (!scroll.IsScrolledToEnd(10)) - scroll.OffsetScrollPosition(-d.DrawHeight); - d.Expire(); - } - } - - private void pendingMessageResolved(Message existing, Message updated) - { - var found = flow.Children.LastOrDefault(c => c.Message == existing); - if (found != null) - { - Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID."); - - flow.Remove(found); - found.Message = updated; - flow.Add(found); - } - } - - private void messageRemoved(Message removed) - { - flow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire(); - } - - private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); - - private class ChatLineContainer : FillFlowContainer - { - protected override int Compare(Drawable x, Drawable y) - { - var xC = (ChatLine)x; - var yC = (ChatLine)y; - - return xC.Message.CompareTo(yC.Message); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Cursor; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class DrawableChannel : Container + { + public readonly Channel Channel; + private readonly ChatLineContainer flow; + private readonly ScrollContainer scroll; + + public DrawableChannel(Channel channel) + { + Channel = channel; + + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + scroll = new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + // Some chat lines have effects that slightly protrude to the bottom, + // which we do not want to mask away, hence the padding. + Padding = new MarginPadding { Bottom = 5 }, + Child = new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = flow = new ChatLineContainer + { + Padding = new MarginPadding { Left = 20, Right = 20 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + } + }, + } + }; + + Channel.NewMessagesArrived += newMessagesArrived; + Channel.MessageRemoved += messageRemoved; + Channel.PendingMessageResolved += pendingMessageResolved; + } + + [BackgroundDependencyLoader] + private void load() + { + newMessagesArrived(Channel.Messages); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + scrollToEnd(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + Channel.NewMessagesArrived -= newMessagesArrived; + Channel.MessageRemoved -= messageRemoved; + Channel.PendingMessageResolved -= pendingMessageResolved; + } + + private void newMessagesArrived(IEnumerable newMessages) + { + // Add up to last Channel.MAX_HISTORY messages + var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); + + flow.AddRange(displayMessages.Select(m => new ChatLine(m))); + + if (!IsLoaded) return; + + if (scroll.IsScrolledToEnd(10) || !flow.Children.Any()) + scrollToEnd(); + + var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); + int count = staleMessages.Length - Channel.MAX_HISTORY; + + for (int i = 0; i < count; i++) + { + var d = staleMessages[i]; + if (!scroll.IsScrolledToEnd(10)) + scroll.OffsetScrollPosition(-d.DrawHeight); + d.Expire(); + } + } + + private void pendingMessageResolved(Message existing, Message updated) + { + var found = flow.Children.LastOrDefault(c => c.Message == existing); + if (found != null) + { + Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID."); + + flow.Remove(found); + found.Message = updated; + flow.Add(found); + } + } + + private void messageRemoved(Message removed) + { + flow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire(); + } + + private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); + + private class ChatLineContainer : FillFlowContainer + { + protected override int Compare(Drawable x, Drawable y) + { + var xC = (ChatLine)x; + var yC = (ChatLine)y; + + return xC.Message.CompareTo(yC.Message); + } + } + } +} diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 210f5ce01e..331dcec4c0 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -1,558 +1,558 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Transforms; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Framework.MathUtils; -using osu.Framework.Threading; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Online.Chat; -using osu.Game.Overlays.Chat; - -namespace osu.Game.Overlays -{ - public class ChatOverlay : OsuFocusedOverlayContainer, IOnlineComponent - { - private const float textbox_height = 60; - private const float channel_selection_min_height = 0.3f; - - private ScheduledDelegate messageRequest; - - private readonly Container currentChannelContainer; - - private readonly LoadingAnimation loading; - - private readonly FocusedTextBox textbox; - - private APIAccess api; - - private const int transition_length = 500; - - public const float DEFAULT_HEIGHT = 0.4f; - - public const float TAB_AREA_HEIGHT = 50; - - private GetMessagesRequest fetchReq; - - private readonly ChatTabControl channelTabs; - - private readonly Container chatContainer; - private readonly Container tabsArea; - private readonly Box chatBackground; - private readonly Box tabBackground; - - public Bindable ChatHeight { get; set; } - - public List AvailableChannels { get; private set; } = new List(); - private readonly Container channelSelectionContainer; - private readonly ChannelSelectionOverlay channelSelection; - - public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceiveMouseInputAt(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.ReceiveMouseInputAt(screenSpacePos); - - public ChatOverlay() - { - RelativeSizeAxes = Axes.Both; - RelativePositionAxes = Axes.Both; - Anchor = Anchor.BottomLeft; - Origin = Anchor.BottomLeft; - - const float padding = 5; - - Children = new Drawable[] - { - channelSelectionContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Height = 1f - DEFAULT_HEIGHT, - Masking = true, - Children = new[] - { - channelSelection = new ChannelSelectionOverlay - { - RelativeSizeAxes = Axes.Both, - }, - }, - }, - chatContainer = new Container - { - Name = @"chat container", - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Height = DEFAULT_HEIGHT, - Children = new[] - { - new Container - { - Name = @"chat area", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = TAB_AREA_HEIGHT }, - Children = new Drawable[] - { - chatBackground = new Box - { - RelativeSizeAxes = Axes.Both, - }, - currentChannelContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Bottom = textbox_height - }, - }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = textbox_height, - Padding = new MarginPadding - { - Top = padding * 2, - Bottom = padding * 2, - Left = ChatLine.LEFT_PADDING + padding * 2, - Right = padding * 2, - }, - Children = new Drawable[] - { - textbox = new FocusedTextBox - { - RelativeSizeAxes = Axes.Both, - Height = 1, - PlaceholderText = "type your message", - Exit = () => State = Visibility.Hidden, - OnCommit = postMessage, - ReleaseFocusOnCommit = false, - HoldFocus = true, - } - } - }, - loading = new LoadingAnimation(), - } - }, - tabsArea = new Container - { - Name = @"tabs area", - RelativeSizeAxes = Axes.X, - Height = TAB_AREA_HEIGHT, - Children = new Drawable[] - { - tabBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - channelTabs = new ChatTabControl - { - RelativeSizeAxes = Axes.Both, - OnRequestLeave = removeChannel, - }, - } - }, - }, - }, - }; - - channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel; - channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; - channelSelection.StateChanged += state => - { - channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible; - - if (state == Visibility.Visible) - { - textbox.HoldFocus = false; - if (1f - ChatHeight.Value < channel_selection_min_height) - transformChatHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint); - } - else - textbox.HoldFocus = true; - }; - } - - private double startDragChatHeight; - private bool isDragging; - - public void OpenChannel(Channel channel) => addChannel(channel); - - protected override bool OnDragStart(InputState state) - { - isDragging = tabsArea.IsHovered; - - if (!isDragging) - return base.OnDragStart(state); - - startDragChatHeight = ChatHeight.Value; - return true; - } - - protected override bool OnDrag(InputState state) - { - if (isDragging) - { - Trace.Assert(state.Mouse.PositionMouseDown != null); - - // ReSharper disable once PossibleInvalidOperationException - double targetChatHeight = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y; - - // If the channel selection screen is shown, mind its minimum height - if (channelSelection.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) - targetChatHeight = 1f - channel_selection_min_height; - - ChatHeight.Value = targetChatHeight; - } - - return true; - } - - protected override bool OnDragEnd(InputState state) - { - isDragging = false; - return base.OnDragEnd(state); - } - - public void APIStateChanged(APIAccess api, APIState state) - { - switch (state) - { - case APIState.Online: - initializeChannels(); - break; - default: - messageRequest?.Cancel(); - break; - } - } - - public override bool AcceptsFocus => true; - - protected override void OnFocus(InputState state) - { - //this is necessary as textbox is masked away and therefore can't get focus :( - GetContainingInputManager().ChangeFocus(textbox); - base.OnFocus(state); - } - - protected override void PopIn() - { - this.MoveToY(0, transition_length, Easing.OutQuint); - this.FadeIn(transition_length, Easing.OutQuint); - - textbox.HoldFocus = true; - base.PopIn(); - } - - protected override void PopOut() - { - this.MoveToY(Height, transition_length, Easing.InSine); - this.FadeOut(transition_length, Easing.InSine); - - textbox.HoldFocus = false; - base.PopOut(); - } - - [BackgroundDependencyLoader] - private void load(APIAccess api, OsuConfigManager config, OsuColour colours) - { - this.api = api; - api.Register(this); - - ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); - ChatHeight.ValueChanged += h => - { - chatContainer.Height = (float)h; - channelSelectionContainer.Height = 1f - (float)h; - tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200); - }; - ChatHeight.TriggerChange(); - - chatBackground.Colour = colours.ChatBlue; - } - - private long? lastMessageId; - - private readonly List careChannels = new List(); - - private readonly List loadedChannels = new List(); - - private void initializeChannels() - { - loading.Show(); - - messageRequest?.Cancel(); - - ListChannelsRequest req = new ListChannelsRequest(); - req.Success += delegate (List channels) - { - AvailableChannels = channels; - - Scheduler.Add(delegate - { - addChannel(channels.Find(c => c.Name == @"#lazer")); - addChannel(channels.Find(c => c.Name == @"#osu")); - addChannel(channels.Find(c => c.Name == @"#lobby")); - - channelSelection.OnRequestJoin = addChannel; - channelSelection.OnRequestLeave = removeChannel; - channelSelection.Sections = new[] - { - new ChannelSection - { - Header = "All Channels", - Channels = channels, - }, - }; - }); - - messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true); - }; - - api.Queue(req); - } - - private Channel currentChannel; - - protected Channel CurrentChannel - { - get - { - return currentChannel; - } - - set - { - if (currentChannel == value) return; - - if (value == null) - { - currentChannel = null; - textbox.Current.Disabled = true; - currentChannelContainer.Clear(false); - return; - } - - currentChannel = value; - - textbox.Current.Disabled = currentChannel.ReadOnly; - channelTabs.Current.Value = value; - - var loaded = loadedChannels.Find(d => d.Channel == value); - if (loaded == null) - { - currentChannelContainer.FadeOut(500, Easing.OutQuint); - loading.Show(); - - loaded = new DrawableChannel(currentChannel); - loadedChannels.Add(loaded); - LoadComponentAsync(loaded, l => - { - if (currentChannel.Messages.Any()) - loading.Hide(); - - currentChannelContainer.Clear(false); - currentChannelContainer.Add(loaded); - currentChannelContainer.FadeIn(500, Easing.OutQuint); - }); - } - else - { - currentChannelContainer.Clear(false); - currentChannelContainer.Add(loaded); - } - } - } - - private void addChannel(Channel channel) - { - if (channel == null) return; - - // ReSharper disable once AccessToModifiedClosure - var existing = careChannels.Find(c => c.Id == channel.Id); - - if (existing != null) - { - // if we already have this channel loaded, we don't want to make a second one. - channel = existing; - } - else - { - careChannels.Add(channel); - channelTabs.AddItem(channel); - } - - // let's fetch a small number of messages to bring us up-to-date with the backlog. - fetchInitialMessages(channel); - - if (CurrentChannel == null) - CurrentChannel = channel; - - channel.Joined.Value = true; - } - - private void removeChannel(Channel channel) - { - if (channel == null) return; - - if (channel == CurrentChannel) CurrentChannel = null; - - careChannels.Remove(channel); - loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel)); - channelTabs.RemoveItem(channel); - - channel.Joined.Value = false; - } - - private void fetchInitialMessages(Channel channel) - { - var req = new GetMessagesRequest(new List { channel }, null); - - req.Success += delegate (List messages) - { - loading.Hide(); - channel.AddNewMessages(messages.ToArray()); - Debug.Write("success!"); - }; - req.Failure += delegate - { - Debug.Write("failure!"); - }; - - api.Queue(req); - } - - private void fetchNewMessages() - { - if (fetchReq != null) return; - - fetchReq = new GetMessagesRequest(careChannels, lastMessageId); - - fetchReq.Success += delegate (List messages) - { - foreach (var group in messages.Where(m => m.TargetType == TargetType.Channel).GroupBy(m => m.TargetId)) - careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); - - lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId; - - Debug.Write("success!"); - fetchReq = null; - }; - - fetchReq.Failure += delegate - { - Debug.Write("failure!"); - fetchReq = null; - }; - - api.Queue(fetchReq); - } - - private void postMessage(TextBox textbox, bool newText) - { - var postText = textbox.Text; - - textbox.Text = string.Empty; - - if (string.IsNullOrWhiteSpace(postText)) - return; - - var target = currentChannel; - - if (target == null) return; - - if (!api.IsLoggedIn) - { - target.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); - return; - } - - bool isAction = false; - - if (postText[0] == '/') - { - string[] parameters = postText.Substring(1).Split(new[] { ' ' }, 2); - string command = parameters[0]; - string content = parameters.Length == 2 ? parameters[1] : string.Empty; - - switch (command) - { - case "me": - - if (string.IsNullOrWhiteSpace(content)) - { - currentChannel.AddNewMessages(new ErrorMessage("Usage: /me [action]")); - return; - } - - isAction = true; - postText = content; - break; - - case "help": - currentChannel.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); - return; - - default: - currentChannel.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); - return; - } - } - - var message = new LocalEchoMessage - { - Sender = api.LocalUser.Value, - Timestamp = DateTimeOffset.Now, - TargetType = TargetType.Channel, //TODO: read this from channel - TargetId = target.Id, - IsAction = isAction, - Content = postText - }; - - var req = new PostMessageRequest(message); - - target.AddLocalEcho(message); - req.Failure += e => target.ReplaceMessage(message, null); - req.Success += m => target.ReplaceMessage(message, m); - - api.Queue(req); - } - - private void transformChatHeightTo(double newChatHeight, double duration = 0, Easing easing = Easing.None) - { - this.TransformTo(this.PopulateTransform(new TransformChatHeight(), newChatHeight, duration, easing)); - } - - private class TransformChatHeight : Transform - { - private double valueAt(double time) - { - if (time < StartTime) return StartValue; - if (time >= EndTime) return EndValue; - - return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); - } - - public override string TargetMember => "ChatHeight.Value"; - - protected override void Apply(ChatOverlay d, double time) => d.ChatHeight.Value = valueAt(time); - protected override void ReadIntoStartValue(ChatOverlay d) => StartValue = d.ChatHeight.Value; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Framework.Threading; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat; + +namespace osu.Game.Overlays +{ + public class ChatOverlay : OsuFocusedOverlayContainer, IOnlineComponent + { + private const float textbox_height = 60; + private const float channel_selection_min_height = 0.3f; + + private ScheduledDelegate messageRequest; + + private readonly Container currentChannelContainer; + + private readonly LoadingAnimation loading; + + private readonly FocusedTextBox textbox; + + private APIAccess api; + + private const int transition_length = 500; + + public const float DEFAULT_HEIGHT = 0.4f; + + public const float TAB_AREA_HEIGHT = 50; + + private GetMessagesRequest fetchReq; + + private readonly ChatTabControl channelTabs; + + private readonly Container chatContainer; + private readonly Container tabsArea; + private readonly Box chatBackground; + private readonly Box tabBackground; + + public Bindable ChatHeight { get; set; } + + public List AvailableChannels { get; private set; } = new List(); + private readonly Container channelSelectionContainer; + private readonly ChannelSelectionOverlay channelSelection; + + public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceiveMouseInputAt(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.ReceiveMouseInputAt(screenSpacePos); + + public ChatOverlay() + { + RelativeSizeAxes = Axes.Both; + RelativePositionAxes = Axes.Both; + Anchor = Anchor.BottomLeft; + Origin = Anchor.BottomLeft; + + const float padding = 5; + + Children = new Drawable[] + { + channelSelectionContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Height = 1f - DEFAULT_HEIGHT, + Masking = true, + Children = new[] + { + channelSelection = new ChannelSelectionOverlay + { + RelativeSizeAxes = Axes.Both, + }, + }, + }, + chatContainer = new Container + { + Name = @"chat container", + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Height = DEFAULT_HEIGHT, + Children = new[] + { + new Container + { + Name = @"chat area", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = TAB_AREA_HEIGHT }, + Children = new Drawable[] + { + chatBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + currentChannelContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Bottom = textbox_height + }, + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = textbox_height, + Padding = new MarginPadding + { + Top = padding * 2, + Bottom = padding * 2, + Left = ChatLine.LEFT_PADDING + padding * 2, + Right = padding * 2, + }, + Children = new Drawable[] + { + textbox = new FocusedTextBox + { + RelativeSizeAxes = Axes.Both, + Height = 1, + PlaceholderText = "type your message", + Exit = () => State = Visibility.Hidden, + OnCommit = postMessage, + ReleaseFocusOnCommit = false, + HoldFocus = true, + } + } + }, + loading = new LoadingAnimation(), + } + }, + tabsArea = new Container + { + Name = @"tabs area", + RelativeSizeAxes = Axes.X, + Height = TAB_AREA_HEIGHT, + Children = new Drawable[] + { + tabBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + channelTabs = new ChatTabControl + { + RelativeSizeAxes = Axes.Both, + OnRequestLeave = removeChannel, + }, + } + }, + }, + }, + }; + + channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel; + channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; + channelSelection.StateChanged += state => + { + channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible; + + if (state == Visibility.Visible) + { + textbox.HoldFocus = false; + if (1f - ChatHeight.Value < channel_selection_min_height) + transformChatHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint); + } + else + textbox.HoldFocus = true; + }; + } + + private double startDragChatHeight; + private bool isDragging; + + public void OpenChannel(Channel channel) => addChannel(channel); + + protected override bool OnDragStart(InputState state) + { + isDragging = tabsArea.IsHovered; + + if (!isDragging) + return base.OnDragStart(state); + + startDragChatHeight = ChatHeight.Value; + return true; + } + + protected override bool OnDrag(InputState state) + { + if (isDragging) + { + Trace.Assert(state.Mouse.PositionMouseDown != null); + + // ReSharper disable once PossibleInvalidOperationException + double targetChatHeight = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y; + + // If the channel selection screen is shown, mind its minimum height + if (channelSelection.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) + targetChatHeight = 1f - channel_selection_min_height; + + ChatHeight.Value = targetChatHeight; + } + + return true; + } + + protected override bool OnDragEnd(InputState state) + { + isDragging = false; + return base.OnDragEnd(state); + } + + public void APIStateChanged(APIAccess api, APIState state) + { + switch (state) + { + case APIState.Online: + initializeChannels(); + break; + default: + messageRequest?.Cancel(); + break; + } + } + + public override bool AcceptsFocus => true; + + protected override void OnFocus(InputState state) + { + //this is necessary as textbox is masked away and therefore can't get focus :( + GetContainingInputManager().ChangeFocus(textbox); + base.OnFocus(state); + } + + protected override void PopIn() + { + this.MoveToY(0, transition_length, Easing.OutQuint); + this.FadeIn(transition_length, Easing.OutQuint); + + textbox.HoldFocus = true; + base.PopIn(); + } + + protected override void PopOut() + { + this.MoveToY(Height, transition_length, Easing.InSine); + this.FadeOut(transition_length, Easing.InSine); + + textbox.HoldFocus = false; + base.PopOut(); + } + + [BackgroundDependencyLoader] + private void load(APIAccess api, OsuConfigManager config, OsuColour colours) + { + this.api = api; + api.Register(this); + + ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); + ChatHeight.ValueChanged += h => + { + chatContainer.Height = (float)h; + channelSelectionContainer.Height = 1f - (float)h; + tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200); + }; + ChatHeight.TriggerChange(); + + chatBackground.Colour = colours.ChatBlue; + } + + private long? lastMessageId; + + private readonly List careChannels = new List(); + + private readonly List loadedChannels = new List(); + + private void initializeChannels() + { + loading.Show(); + + messageRequest?.Cancel(); + + ListChannelsRequest req = new ListChannelsRequest(); + req.Success += delegate (List channels) + { + AvailableChannels = channels; + + Scheduler.Add(delegate + { + addChannel(channels.Find(c => c.Name == @"#lazer")); + addChannel(channels.Find(c => c.Name == @"#osu")); + addChannel(channels.Find(c => c.Name == @"#lobby")); + + channelSelection.OnRequestJoin = addChannel; + channelSelection.OnRequestLeave = removeChannel; + channelSelection.Sections = new[] + { + new ChannelSection + { + Header = "All Channels", + Channels = channels, + }, + }; + }); + + messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true); + }; + + api.Queue(req); + } + + private Channel currentChannel; + + protected Channel CurrentChannel + { + get + { + return currentChannel; + } + + set + { + if (currentChannel == value) return; + + if (value == null) + { + currentChannel = null; + textbox.Current.Disabled = true; + currentChannelContainer.Clear(false); + return; + } + + currentChannel = value; + + textbox.Current.Disabled = currentChannel.ReadOnly; + channelTabs.Current.Value = value; + + var loaded = loadedChannels.Find(d => d.Channel == value); + if (loaded == null) + { + currentChannelContainer.FadeOut(500, Easing.OutQuint); + loading.Show(); + + loaded = new DrawableChannel(currentChannel); + loadedChannels.Add(loaded); + LoadComponentAsync(loaded, l => + { + if (currentChannel.Messages.Any()) + loading.Hide(); + + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); + currentChannelContainer.FadeIn(500, Easing.OutQuint); + }); + } + else + { + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); + } + } + } + + private void addChannel(Channel channel) + { + if (channel == null) return; + + // ReSharper disable once AccessToModifiedClosure + var existing = careChannels.Find(c => c.Id == channel.Id); + + if (existing != null) + { + // if we already have this channel loaded, we don't want to make a second one. + channel = existing; + } + else + { + careChannels.Add(channel); + channelTabs.AddItem(channel); + } + + // let's fetch a small number of messages to bring us up-to-date with the backlog. + fetchInitialMessages(channel); + + if (CurrentChannel == null) + CurrentChannel = channel; + + channel.Joined.Value = true; + } + + private void removeChannel(Channel channel) + { + if (channel == null) return; + + if (channel == CurrentChannel) CurrentChannel = null; + + careChannels.Remove(channel); + loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel)); + channelTabs.RemoveItem(channel); + + channel.Joined.Value = false; + } + + private void fetchInitialMessages(Channel channel) + { + var req = new GetMessagesRequest(new List { channel }, null); + + req.Success += delegate (List messages) + { + loading.Hide(); + channel.AddNewMessages(messages.ToArray()); + Debug.Write("success!"); + }; + req.Failure += delegate + { + Debug.Write("failure!"); + }; + + api.Queue(req); + } + + private void fetchNewMessages() + { + if (fetchReq != null) return; + + fetchReq = new GetMessagesRequest(careChannels, lastMessageId); + + fetchReq.Success += delegate (List messages) + { + foreach (var group in messages.Where(m => m.TargetType == TargetType.Channel).GroupBy(m => m.TargetId)) + careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); + + lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId; + + Debug.Write("success!"); + fetchReq = null; + }; + + fetchReq.Failure += delegate + { + Debug.Write("failure!"); + fetchReq = null; + }; + + api.Queue(fetchReq); + } + + private void postMessage(TextBox textbox, bool newText) + { + var postText = textbox.Text; + + textbox.Text = string.Empty; + + if (string.IsNullOrWhiteSpace(postText)) + return; + + var target = currentChannel; + + if (target == null) return; + + if (!api.IsLoggedIn) + { + target.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); + return; + } + + bool isAction = false; + + if (postText[0] == '/') + { + string[] parameters = postText.Substring(1).Split(new[] { ' ' }, 2); + string command = parameters[0]; + string content = parameters.Length == 2 ? parameters[1] : string.Empty; + + switch (command) + { + case "me": + + if (string.IsNullOrWhiteSpace(content)) + { + currentChannel.AddNewMessages(new ErrorMessage("Usage: /me [action]")); + return; + } + + isAction = true; + postText = content; + break; + + case "help": + currentChannel.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); + return; + + default: + currentChannel.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); + return; + } + } + + var message = new LocalEchoMessage + { + Sender = api.LocalUser.Value, + Timestamp = DateTimeOffset.Now, + TargetType = TargetType.Channel, //TODO: read this from channel + TargetId = target.Id, + IsAction = isAction, + Content = postText + }; + + var req = new PostMessageRequest(message); + + target.AddLocalEcho(message); + req.Failure += e => target.ReplaceMessage(message, null); + req.Success += m => target.ReplaceMessage(message, m); + + api.Queue(req); + } + + private void transformChatHeightTo(double newChatHeight, double duration = 0, Easing easing = Easing.None) + { + this.TransformTo(this.PopulateTransform(new TransformChatHeight(), newChatHeight, duration, easing)); + } + + private class TransformChatHeight : Transform + { + private double valueAt(double time) + { + if (time < StartTime) return StartValue; + if (time >= EndTime) return EndValue; + + return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); + } + + public override string TargetMember => "ChatHeight.Value"; + + protected override void Apply(ChatOverlay d, double time) => d.ChatHeight.Value = valueAt(time); + protected override void ReadIntoStartValue(ChatOverlay d) => StartValue = d.ChatHeight.Value; + } + } +} diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 790c097789..5f90ec67ad 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -1,247 +1,247 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Dialog -{ - public class PopupDialog : OsuFocusedOverlayContainer - { - public static readonly float ENTER_DURATION = 500; - public static readonly float EXIT_DURATION = 200; - private readonly Vector2 ringSize = new Vector2(100f); - private readonly Vector2 ringMinifiedSize = new Vector2(20f); - private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f); - - private readonly Container content; - private readonly Container ring; - private readonly FillFlowContainer buttonsContainer; - private readonly SpriteIcon icon; - private readonly SpriteText header; - private readonly TextFlowContainer body; - - public FontAwesome Icon - { - get { return icon.Icon; } - set { icon.Icon = value; } - } - - public string HeaderText - { - get { return header.Text; } - set { header.Text = value; } - } - - public string BodyText - { - set { body.Text = value; } - } - - public IEnumerable Buttons - { - get { return buttonsContainer.Children; } - set - { - buttonsContainer.ChildrenEnumerable = value; - foreach (PopupDialogButton b in value) - { - var action = b.Action; - b.Action = () => - { - Hide(); - action?.Invoke(); - }; - } - } - } - - private void pressButtonAtIndex(int index) - { - if (index < Buttons.Count()) - Buttons.Skip(index).First().TriggerOnClick(); - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (args.Repeat) return false; - - if (args.Key == Key.Enter || args.Key == Key.KeypadEnter) - { - Buttons.OfType().FirstOrDefault()?.TriggerOnClick(); - return true; - } - - // press button at number if 1-9 on number row or keypad are pressed - var k = args.Key; - if (k >= Key.Number1 && k <= Key.Number9) - { - pressButtonAtIndex(k - Key.Number1); - return true; - } - - if (k >= Key.Keypad1 && k <= Key.Keypad9) - { - pressButtonAtIndex(k - Key.Keypad1); - return true; - } - - return base.OnKeyDown(state, args); - } - - protected override void PopIn() - { - base.PopIn(); - - // Reset various animations but only if the dialog animation fully completed - if (content.Alpha == 0) - { - buttonsContainer.TransformSpacingTo(buttonsEnterSpacing); - buttonsContainer.MoveToY(buttonsEnterSpacing.Y); - ring.ResizeTo(ringMinifiedSize); - } - - content.FadeIn(ENTER_DURATION, Easing.OutQuint); - ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint); - buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint); - buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint); - } - - protected override void PopOut() - { - base.PopOut(); - - content.FadeOut(EXIT_DURATION, Easing.InSine); - } - - public PopupDialog() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - content = new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Width = 0.4f, - Alpha = 0f, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.5f), - Radius = 8, - }, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"221a21"), - }, - new Triangles - { - RelativeSizeAxes = Axes.Both, - ColourLight = OsuColour.FromHex(@"271e26"), - ColourDark = OsuColour.FromHex(@"1e171e"), - TriangleScale = 4, - }, - }, - }, - new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Position = new Vector2(0f, -50f), - Direction = FillDirection.Vertical, - Spacing = new Vector2(0f, 10f), - Children = new Drawable[] - { - new Container - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Size = ringSize, - Margin = new MarginPadding - { - Bottom = 30, - }, - Children = new Drawable[] - { - ring = new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 5f, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0), - }, - icon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Icon = FontAwesome.fa_close, - Size = new Vector2(50), - }, - }, - }, - }, - }, - header = new OsuSpriteText - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - TextSize = 25, - Shadow = true, - }, - body = new OsuTextFlowContainer(t => t.TextSize = 18) - { - Padding = new MarginPadding(15), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - TextAnchor = Anchor.TopCentre, - }, - }, - }, - buttonsContainer = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - }, - }, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialog : OsuFocusedOverlayContainer + { + public static readonly float ENTER_DURATION = 500; + public static readonly float EXIT_DURATION = 200; + private readonly Vector2 ringSize = new Vector2(100f); + private readonly Vector2 ringMinifiedSize = new Vector2(20f); + private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f); + + private readonly Container content; + private readonly Container ring; + private readonly FillFlowContainer buttonsContainer; + private readonly SpriteIcon icon; + private readonly SpriteText header; + private readonly TextFlowContainer body; + + public FontAwesome Icon + { + get { return icon.Icon; } + set { icon.Icon = value; } + } + + public string HeaderText + { + get { return header.Text; } + set { header.Text = value; } + } + + public string BodyText + { + set { body.Text = value; } + } + + public IEnumerable Buttons + { + get { return buttonsContainer.Children; } + set + { + buttonsContainer.ChildrenEnumerable = value; + foreach (PopupDialogButton b in value) + { + var action = b.Action; + b.Action = () => + { + Hide(); + action?.Invoke(); + }; + } + } + } + + private void pressButtonAtIndex(int index) + { + if (index < Buttons.Count()) + Buttons.Skip(index).First().TriggerOnClick(); + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) return false; + + if (args.Key == Key.Enter || args.Key == Key.KeypadEnter) + { + Buttons.OfType().FirstOrDefault()?.TriggerOnClick(); + return true; + } + + // press button at number if 1-9 on number row or keypad are pressed + var k = args.Key; + if (k >= Key.Number1 && k <= Key.Number9) + { + pressButtonAtIndex(k - Key.Number1); + return true; + } + + if (k >= Key.Keypad1 && k <= Key.Keypad9) + { + pressButtonAtIndex(k - Key.Keypad1); + return true; + } + + return base.OnKeyDown(state, args); + } + + protected override void PopIn() + { + base.PopIn(); + + // Reset various animations but only if the dialog animation fully completed + if (content.Alpha == 0) + { + buttonsContainer.TransformSpacingTo(buttonsEnterSpacing); + buttonsContainer.MoveToY(buttonsEnterSpacing.Y); + ring.ResizeTo(ringMinifiedSize); + } + + content.FadeIn(ENTER_DURATION, Easing.OutQuint); + ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint); + buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint); + buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + + content.FadeOut(EXIT_DURATION, Easing.InSine); + } + + public PopupDialog() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + content = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Width = 0.4f, + Alpha = 0f, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.5f), + Radius = 8, + }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"221a21"), + }, + new Triangles + { + RelativeSizeAxes = Axes.Both, + ColourLight = OsuColour.FromHex(@"271e26"), + ColourDark = OsuColour.FromHex(@"1e171e"), + TriangleScale = 4, + }, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Position = new Vector2(0f, -50f), + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + new Container + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Size = ringSize, + Margin = new MarginPadding + { + Bottom = 30, + }, + Children = new Drawable[] + { + ring = new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 5f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0), + }, + icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Icon = FontAwesome.fa_close, + Size = new Vector2(50), + }, + }, + }, + }, + }, + header = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + TextSize = 25, + Shadow = true, + }, + body = new OsuTextFlowContainer(t => t.TextSize = 18) + { + Padding = new MarginPadding(15), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + TextAnchor = Anchor.TopCentre, + }, + }, + }, + buttonsContainer = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Dialog/PopupDialogButton.cs b/osu.Game/Overlays/Dialog/PopupDialogButton.cs index 40200e6417..8c382c6f56 100644 --- a/osu.Game/Overlays/Dialog/PopupDialogButton.cs +++ b/osu.Game/Overlays/Dialog/PopupDialogButton.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Dialog -{ - public class PopupDialogButton : DialogButton - { - public PopupDialogButton() - { - Height = 50; - BackgroundColour = OsuColour.FromHex(@"150e14"); - TextSize = 18; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialogButton : DialogButton + { + public PopupDialogButton() + { + Height = 50; + BackgroundColour = OsuColour.FromHex(@"150e14"); + TextSize = 18; + } + } +} diff --git a/osu.Game/Overlays/Dialog/PopupDialogCancelButton.cs b/osu.Game/Overlays/Dialog/PopupDialogCancelButton.cs index 38c5ea34c5..45a8dfd29e 100644 --- a/osu.Game/Overlays/Dialog/PopupDialogCancelButton.cs +++ b/osu.Game/Overlays/Dialog/PopupDialogCancelButton.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Dialog -{ - public class PopupDialogCancelButton : PopupDialogButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - ButtonColour = colours.Blue; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialogCancelButton : PopupDialogButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + ButtonColour = colours.Blue; + } + } +} diff --git a/osu.Game/Overlays/Dialog/PopupDialogOkButton.cs b/osu.Game/Overlays/Dialog/PopupDialogOkButton.cs index fb21ffc00a..6c5abbe161 100644 --- a/osu.Game/Overlays/Dialog/PopupDialogOkButton.cs +++ b/osu.Game/Overlays/Dialog/PopupDialogOkButton.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Dialog -{ - public class PopupDialogOkButton : PopupDialogButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - ButtonColour = colours.Pink; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialogOkButton : PopupDialogButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + ButtonColour = colours.Pink; + } + } +} diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index ef4d2bdc00..7caab0dd6c 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -1,81 +1,81 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Overlays.Dialog; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays -{ - public class DialogOverlay : OsuFocusedOverlayContainer - { - private readonly Container dialogContainer; - private PopupDialog currentDialog; - - public void Push(PopupDialog dialog) - { - if (dialog == currentDialog) return; - - currentDialog?.Hide(); - currentDialog = dialog; - - dialogContainer.Add(currentDialog); - - currentDialog.Show(); - currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state); - State = Visibility.Visible; - } - - private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) - { - if (v != Visibility.Hidden) return; - - //handle the dialog being dismissed. - dialog.Delay(PopupDialog.EXIT_DURATION).Expire(); - - if (dialog == currentDialog) - State = Visibility.Hidden; - } - - protected override void PopIn() - { - base.PopIn(); - this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint); - } - - protected override void PopOut() - { - base.PopOut(); - this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); - } - - public DialogOverlay() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, - }, - }, - dialogContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Dialog; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays +{ + public class DialogOverlay : OsuFocusedOverlayContainer + { + private readonly Container dialogContainer; + private PopupDialog currentDialog; + + public void Push(PopupDialog dialog) + { + if (dialog == currentDialog) return; + + currentDialog?.Hide(); + currentDialog = dialog; + + dialogContainer.Add(currentDialog); + + currentDialog.Show(); + currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state); + State = Visibility.Visible; + } + + private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) + { + if (v != Visibility.Hidden) return; + + //handle the dialog being dismissed. + dialog.Delay(PopupDialog.EXIT_DURATION).Expire(); + + if (dialog == currentDialog) + State = Visibility.Hidden; + } + + protected override void PopIn() + { + base.PopIn(); + this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); + } + + public DialogOverlay() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + }, + }, + dialogContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index d893c027c7..9715615d14 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -1,237 +1,237 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; - -namespace osu.Game.Overlays.Direct -{ - public class DirectGridPanel : DirectPanel - { - private const float horizontal_padding = 10; - private const float vertical_padding = 5; - - private FillFlowContainer bottomPanel, statusContainer; - private PlayButton playButton; - private Box progressBar; - - protected override PlayButton PlayButton => playButton; - protected override Box PreviewBar => progressBar; - - public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap) - { - Width = 400; - Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image) - } - - protected override void LoadComplete() - { - base.LoadComplete(); - bottomPanel.LayoutDuration = 200; - bottomPanel.LayoutEasing = Easing.Out; - bottomPanel.Origin = Anchor.BottomLeft; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, LocalisationEngine localisation) - { - Content.CornerRadius = 4; - - AddRange(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, - bottomPanel = new FillFlowContainer - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, vertical_padding), - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding }, - Direction = FillDirection.Vertical, - Children = new[] - { - new OsuSpriteText - { - Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), - TextSize = 18, - Font = @"Exo2.0-BoldItalic", - }, - new OsuSpriteText - { - Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), - Font = @"Exo2.0-BoldItalic", - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - }, - progressBar = new Box - { - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - BypassAutoSizeAxes = Axes.Both, - Size = new Vector2(0, 3), - Alpha = 0, - Colour = colours.Yellow, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Padding = new MarginPadding - { - Top = vertical_padding, - Bottom = vertical_padding, - Left = horizontal_padding, - Right = horizontal_padding, - }, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new[] - { - new OsuSpriteText - { - Text = "mapped by ", - TextSize = 14, - Shadow = false, - Colour = colours.Gray5, - }, - new OsuSpriteText - { - Text = SetInfo.Metadata.Author.Username, - TextSize = 14, - Font = @"Exo2.0-SemiBoldItalic", - Shadow = false, - Colour = colours.BlueDark, - }, - }, - }, - new Container - { - AutoSizeAxes = Axes.X, - Height = 14, - Children = new[] - { - new OsuSpriteText - { - Text = $"from {SetInfo.Metadata.Source}", - TextSize = 14, - Shadow = false, - Colour = colours.Gray5, - Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, - }, - }, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.X, - Height = 20, - Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, - Children = GetDifficultyIcons(), - }, - }, - }, - new DownloadButton - { - Size = new Vector2(30), - Margin = new MarginPadding(horizontal_padding), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Colour = colours.Gray5, - Action = StartDownload - }, - }, - }, - }, - }, - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding }, - Children = new[] - { - new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0) - { - Margin = new MarginPadding { Right = 1 }, - }, - new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), - }, - }, - playButton = new PlayButton(SetInfo) - { - Margin = new MarginPadding { Top = 5, Left = 10 }, - Size = new Vector2(30), - Alpha = 0, - }, - statusContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 5, Left = 5 }, - Spacing = new Vector2(5), - }, - }); - - if (SetInfo.OnlineInfo?.HasVideo ?? false) - { - statusContainer.Add(new IconPill(FontAwesome.fa_film)); - } - - statusContainer.Add(new BeatmapSetOnlineStatusPill(12, new MarginPadding { Horizontal = 10, Vertical = 5 }) - { - Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, - }); - } - - protected override bool OnHover(InputState state) - { - statusContainer.FadeOut(120, Easing.InOutQuint); - - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - base.OnHoverLost(state); - - statusContainer.FadeIn(120, Easing.InOutQuint); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; + +namespace osu.Game.Overlays.Direct +{ + public class DirectGridPanel : DirectPanel + { + private const float horizontal_padding = 10; + private const float vertical_padding = 5; + + private FillFlowContainer bottomPanel, statusContainer; + private PlayButton playButton; + private Box progressBar; + + protected override PlayButton PlayButton => playButton; + protected override Box PreviewBar => progressBar; + + public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap) + { + Width = 400; + Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image) + } + + protected override void LoadComplete() + { + base.LoadComplete(); + bottomPanel.LayoutDuration = 200; + bottomPanel.LayoutEasing = Easing.Out; + bottomPanel.Origin = Anchor.BottomLeft; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, LocalisationEngine localisation) + { + Content.CornerRadius = 4; + + AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + bottomPanel = new FillFlowContainer + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, vertical_padding), + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding }, + Direction = FillDirection.Vertical, + Children = new[] + { + new OsuSpriteText + { + Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + TextSize = 18, + Font = @"Exo2.0-BoldItalic", + }, + new OsuSpriteText + { + Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = @"Exo2.0-BoldItalic", + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + }, + progressBar = new Box + { + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + BypassAutoSizeAxes = Axes.Both, + Size = new Vector2(0, 3), + Alpha = 0, + Colour = colours.Yellow, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding + { + Top = vertical_padding, + Bottom = vertical_padding, + Left = horizontal_padding, + Right = horizontal_padding, + }, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = "mapped by ", + TextSize = 14, + Shadow = false, + Colour = colours.Gray5, + }, + new OsuSpriteText + { + Text = SetInfo.Metadata.Author.Username, + TextSize = 14, + Font = @"Exo2.0-SemiBoldItalic", + Shadow = false, + Colour = colours.BlueDark, + }, + }, + }, + new Container + { + AutoSizeAxes = Axes.X, + Height = 14, + Children = new[] + { + new OsuSpriteText + { + Text = $"from {SetInfo.Metadata.Source}", + TextSize = 14, + Shadow = false, + Colour = colours.Gray5, + Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, + }, + }, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + Height = 20, + Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, + Children = GetDifficultyIcons(), + }, + }, + }, + new DownloadButton + { + Size = new Vector2(30), + Margin = new MarginPadding(horizontal_padding), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Colour = colours.Gray5, + Action = StartDownload + }, + }, + }, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding }, + Children = new[] + { + new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0) + { + Margin = new MarginPadding { Right = 1 }, + }, + new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), + }, + }, + playButton = new PlayButton(SetInfo) + { + Margin = new MarginPadding { Top = 5, Left = 10 }, + Size = new Vector2(30), + Alpha = 0, + }, + statusContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 5, Left = 5 }, + Spacing = new Vector2(5), + }, + }); + + if (SetInfo.OnlineInfo?.HasVideo ?? false) + { + statusContainer.Add(new IconPill(FontAwesome.fa_film)); + } + + statusContainer.Add(new BeatmapSetOnlineStatusPill(12, new MarginPadding { Horizontal = 10, Vertical = 5 }) + { + Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, + }); + } + + protected override bool OnHover(InputState state) + { + statusContainer.FadeOut(120, Easing.InOutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + base.OnHoverLost(state); + + statusContainer.FadeIn(120, Easing.InOutQuint); + } + } +} diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 403eeb7e10..13398a4a32 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -1,169 +1,169 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Colour; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Framework.Allocation; -using osu.Framework.Localisation; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; - -namespace osu.Game.Overlays.Direct -{ - public class DirectListPanel : DirectPanel - { - private const float horizontal_padding = 10; - private const float vertical_padding = 5; - private const float height = 70; - - public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap) - { - RelativeSizeAxes = Axes.X; - Height = height; - } - - private PlayButton playButton; - private Box progressBar; - - protected override PlayButton PlayButton => playButton; - protected override Box PreviewBar => progressBar; - - [BackgroundDependencyLoader] - private void load(LocalisationEngine localisation, OsuColour colours) - { - Content.CornerRadius = 5; - - AddRange(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.25f), Color4.Black.Opacity(0.75f)), - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding, Left = horizontal_padding, Right = vertical_padding }, - Children = new Drawable[] - { - new FillFlowContainer - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - LayoutEasing = Easing.OutQuint, - LayoutDuration = 120, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - playButton = new PlayButton(SetInfo) - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Size = new Vector2(height / 2), - FillMode = FillMode.Fit, - Alpha = 0, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuSpriteText - { - Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), - TextSize = 18, - Font = @"Exo2.0-BoldItalic", - }, - new OsuSpriteText - { - Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), - Font = @"Exo2.0-BoldItalic", - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.X, - Height = 20, - Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, - Children = GetDifficultyIcons(), - }, - }, - }, - } - }, - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Margin = new MarginPadding { Right = height - vertical_padding * 2 + vertical_padding }, - Children = new Drawable[] - { - new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0) - { - Margin = new MarginPadding { Right = 1 }, - }, - new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new[] - { - new OsuSpriteText - { - Text = "mapped by ", - TextSize = 14, - }, - new OsuSpriteText - { - Text = SetInfo.Metadata.Author.Username, - TextSize = 14, - Font = @"Exo2.0-SemiBoldItalic", - }, - }, - }, - new OsuSpriteText - { - Text = $"from {SetInfo.Metadata.Source}", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - TextSize = 14, - Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, - }, - }, - }, - new DownloadButton - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Size = new Vector2(height - vertical_padding * 2), - Action = StartDownload - }, - }, - }, - progressBar = new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - BypassAutoSizeAxes = Axes.Y, - Size = new Vector2(0, 3), - Alpha = 0, - Colour = colours.Yellow, - }, - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Colour; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Framework.Allocation; +using osu.Framework.Localisation; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; + +namespace osu.Game.Overlays.Direct +{ + public class DirectListPanel : DirectPanel + { + private const float horizontal_padding = 10; + private const float vertical_padding = 5; + private const float height = 70; + + public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap) + { + RelativeSizeAxes = Axes.X; + Height = height; + } + + private PlayButton playButton; + private Box progressBar; + + protected override PlayButton PlayButton => playButton; + protected override Box PreviewBar => progressBar; + + [BackgroundDependencyLoader] + private void load(LocalisationEngine localisation, OsuColour colours) + { + Content.CornerRadius = 5; + + AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.25f), Color4.Black.Opacity(0.75f)), + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding, Left = horizontal_padding, Right = vertical_padding }, + Children = new Drawable[] + { + new FillFlowContainer + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + LayoutEasing = Easing.OutQuint, + LayoutDuration = 120, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + playButton = new PlayButton(SetInfo) + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Size = new Vector2(height / 2), + FillMode = FillMode.Fit, + Alpha = 0, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + TextSize = 18, + Font = @"Exo2.0-BoldItalic", + }, + new OsuSpriteText + { + Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = @"Exo2.0-BoldItalic", + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + Height = 20, + Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, + Children = GetDifficultyIcons(), + }, + }, + }, + } + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding { Right = height - vertical_padding * 2 + vertical_padding }, + Children = new Drawable[] + { + new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0) + { + Margin = new MarginPadding { Right = 1 }, + }, + new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = "mapped by ", + TextSize = 14, + }, + new OsuSpriteText + { + Text = SetInfo.Metadata.Author.Username, + TextSize = 14, + Font = @"Exo2.0-SemiBoldItalic", + }, + }, + }, + new OsuSpriteText + { + Text = $"from {SetInfo.Metadata.Source}", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 14, + Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, + }, + }, + }, + new DownloadButton + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Size = new Vector2(height - vertical_padding * 2), + Action = StartDownload + }, + }, + }, + progressBar = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + BypassAutoSizeAxes = Axes.Y, + Size = new Vector2(0, 3), + Alpha = 0, + Colour = colours.Yellow, + }, + }); + } + } +} diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 6f6bf2d868..cc0123dabc 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -1,278 +1,278 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK.Graphics; -using osu.Framework.Input; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API.Requests; -using osu.Framework.Configuration; -using osu.Framework.Audio.Track; - -namespace osu.Game.Overlays.Direct -{ - public abstract class DirectPanel : Container - { - public readonly BeatmapSetInfo SetInfo; - - protected Box BlackBackground; - - private const double hover_transition_time = 400; - - private Container content; - - private ProgressBar progressBar; - private BeatmapManager beatmaps; - private BeatmapSetOverlay beatmapSetOverlay; - - public Track Preview => PlayButton.Preview; - public Bindable PreviewPlaying => PlayButton.Playing; - protected abstract PlayButton PlayButton { get; } - protected abstract Box PreviewBar { get; } - - protected override Container Content => content; - - protected DirectPanel(BeatmapSetInfo setInfo) - { - SetInfo = setInfo; - } - - private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 2f, - Colour = Color4.Black.Opacity(0.25f), - }; - - private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 5f), - Radius = 10f, - Colour = Color4.Black.Opacity(0.3f), - }; - - private OsuColour colours; - - [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) - { - this.beatmaps = beatmaps; - this.beatmapSetOverlay = beatmapSetOverlay; - this.colours = colours; - - AddInternal(content = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - EdgeEffect = edgeEffectNormal, - Children = new[] - { - // temporary blackness until the actual background loads. - BlackBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - CreateBackground(), - progressBar = new ProgressBar - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Height = 0, - Alpha = 0, - BackgroundColour = Color4.Black.Opacity(0.7f), - FillColour = colours.Blue, - Depth = -1, - }, - } - }); - - var downloadRequest = beatmaps.GetExistingDownload(SetInfo); - - if (downloadRequest != null) - attachDownload(downloadRequest); - - beatmaps.BeatmapDownloadBegan += attachDownload; - } - - public override bool DisposeOnDeathRemoval => true; - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - beatmaps.BeatmapDownloadBegan -= attachDownload; - } - - protected override void Update() - { - base.Update(); - - if (PreviewPlaying && Preview != null && Preview.IsLoaded) - { - PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length); - } - } - - protected override bool OnHover(InputState state) - { - content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint); - content.MoveToY(-4, hover_transition_time, Easing.OutQuint); - PlayButton.FadeIn(120, Easing.InOutQuint); - - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint); - content.MoveToY(0, hover_transition_time, Easing.OutQuint); - if (!PreviewPlaying) - PlayButton.FadeOut(120, Easing.InOutQuint); - - base.OnHoverLost(state); - } - - protected override bool OnClick(InputState state) - { - ShowInformation(); - PreviewPlaying.Value = false; - return true; - } - - protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo); - - protected void StartDownload() - { - if (beatmaps.GetExistingDownload(SetInfo) != null) - { - // we already have an active download running. - content.MoveToX(-5, 50, Easing.OutSine).Then() - .MoveToX(5, 100, Easing.InOutSine).Then() - .MoveToX(-5, 100, Easing.InOutSine).Then() - .MoveToX(0, 50, Easing.InSine).Then(); - - return; - } - - beatmaps.Download(SetInfo); - } - - private void attachDownload(DownloadBeatmapSetRequest request) - { - if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID) - return; - - progressBar.FadeIn(400, Easing.OutQuint); - progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); - - progressBar.Current.Value = 0; - - request.Failure += e => - { - progressBar.Current.Value = 0; - progressBar.FadeOut(500); - }; - - request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress); - - request.Success += data => - { - progressBar.Current.Value = 1; - progressBar.FillColour = colours.Yellow; - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - this.FadeInFromZero(200, Easing.Out); - - PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered ? 1 : 0, 120, Easing.InOutQuint); - PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint); - } - - protected List GetDifficultyIcons() - { - var icons = new List(); - - foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) - icons.Add(new DifficultyIcon(b)); - - return icons; - } - - protected Drawable CreateBackground() => new DelayedLoadWrapper( - new BeatmapSetCover(SetInfo) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fill, - OnLoadComplete = d => - { - d.FadeInFromZero(400, Easing.Out); - BlackBackground.Delay(400).FadeOut(); - }, - }, 300) - { - RelativeSizeAxes = Axes.Both, - }; - - public class Statistic : FillFlowContainer - { - private readonly SpriteText text; - - private int value; - - public int Value - { - get { return value; } - set - { - this.value = value; - text.Text = Value.ToString(@"N0"); - } - } - - public Statistic(FontAwesome icon, int value = 0) - { - Anchor = Anchor.TopRight; - Origin = Anchor.TopRight; - AutoSizeAxes = Axes.Both; - Direction = FillDirection.Horizontal; - Spacing = new Vector2(5f, 0f); - - Children = new Drawable[] - { - text = new OsuSpriteText - { - Font = @"Exo2.0-SemiBoldItalic", - }, - new SpriteIcon - { - Icon = icon, - Shadow = true, - Size = new Vector2(14), - Margin = new MarginPadding { Top = 1 }, - }, - }; - - Value = value; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; +using osu.Framework.Input; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests; +using osu.Framework.Configuration; +using osu.Framework.Audio.Track; + +namespace osu.Game.Overlays.Direct +{ + public abstract class DirectPanel : Container + { + public readonly BeatmapSetInfo SetInfo; + + protected Box BlackBackground; + + private const double hover_transition_time = 400; + + private Container content; + + private ProgressBar progressBar; + private BeatmapManager beatmaps; + private BeatmapSetOverlay beatmapSetOverlay; + + public Track Preview => PlayButton.Preview; + public Bindable PreviewPlaying => PlayButton.Playing; + protected abstract PlayButton PlayButton { get; } + protected abstract Box PreviewBar { get; } + + protected override Container Content => content; + + protected DirectPanel(BeatmapSetInfo setInfo) + { + SetInfo = setInfo; + } + + private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 2f, + Colour = Color4.Black.Opacity(0.25f), + }; + + private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 5f), + Radius = 10f, + Colour = Color4.Black.Opacity(0.3f), + }; + + private OsuColour colours; + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) + { + this.beatmaps = beatmaps; + this.beatmapSetOverlay = beatmapSetOverlay; + this.colours = colours; + + AddInternal(content = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = edgeEffectNormal, + Children = new[] + { + // temporary blackness until the actual background loads. + BlackBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + CreateBackground(), + progressBar = new ProgressBar + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Height = 0, + Alpha = 0, + BackgroundColour = Color4.Black.Opacity(0.7f), + FillColour = colours.Blue, + Depth = -1, + }, + } + }); + + var downloadRequest = beatmaps.GetExistingDownload(SetInfo); + + if (downloadRequest != null) + attachDownload(downloadRequest); + + beatmaps.BeatmapDownloadBegan += attachDownload; + } + + public override bool DisposeOnDeathRemoval => true; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + beatmaps.BeatmapDownloadBegan -= attachDownload; + } + + protected override void Update() + { + base.Update(); + + if (PreviewPlaying && Preview != null && Preview.IsLoaded) + { + PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length); + } + } + + protected override bool OnHover(InputState state) + { + content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint); + content.MoveToY(-4, hover_transition_time, Easing.OutQuint); + PlayButton.FadeIn(120, Easing.InOutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint); + content.MoveToY(0, hover_transition_time, Easing.OutQuint); + if (!PreviewPlaying) + PlayButton.FadeOut(120, Easing.InOutQuint); + + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + ShowInformation(); + PreviewPlaying.Value = false; + return true; + } + + protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo); + + protected void StartDownload() + { + if (beatmaps.GetExistingDownload(SetInfo) != null) + { + // we already have an active download running. + content.MoveToX(-5, 50, Easing.OutSine).Then() + .MoveToX(5, 100, Easing.InOutSine).Then() + .MoveToX(-5, 100, Easing.InOutSine).Then() + .MoveToX(0, 50, Easing.InSine).Then(); + + return; + } + + beatmaps.Download(SetInfo); + } + + private void attachDownload(DownloadBeatmapSetRequest request) + { + if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID) + return; + + progressBar.FadeIn(400, Easing.OutQuint); + progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); + + progressBar.Current.Value = 0; + + request.Failure += e => + { + progressBar.Current.Value = 0; + progressBar.FadeOut(500); + }; + + request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress); + + request.Success += data => + { + progressBar.Current.Value = 1; + progressBar.FillColour = colours.Yellow; + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.FadeInFromZero(200, Easing.Out); + + PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered ? 1 : 0, 120, Easing.InOutQuint); + PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint); + } + + protected List GetDifficultyIcons() + { + var icons = new List(); + + foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) + icons.Add(new DifficultyIcon(b)); + + return icons; + } + + protected Drawable CreateBackground() => new DelayedLoadWrapper( + new BeatmapSetCover(SetInfo) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, + OnLoadComplete = d => + { + d.FadeInFromZero(400, Easing.Out); + BlackBackground.Delay(400).FadeOut(); + }, + }, 300) + { + RelativeSizeAxes = Axes.Both, + }; + + public class Statistic : FillFlowContainer + { + private readonly SpriteText text; + + private int value; + + public int Value + { + get { return value; } + set + { + this.value = value; + text.Text = Value.ToString(@"N0"); + } + } + + public Statistic(FontAwesome icon, int value = 0) + { + Anchor = Anchor.TopRight; + Origin = Anchor.TopRight; + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Horizontal; + Spacing = new Vector2(5f, 0f); + + Children = new Drawable[] + { + text = new OsuSpriteText + { + Font = @"Exo2.0-SemiBoldItalic", + }, + new SpriteIcon + { + Icon = icon, + Shadow = true, + Size = new Vector2(14), + Margin = new MarginPadding { Top = 1 }, + }, + }; + + Value = value; + } + } + } +} diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 2b29b9c5f9..f01c9dac59 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -1,53 +1,53 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Overlays.Direct -{ - public class DownloadButton : OsuClickableContainer - { - private readonly SpriteIcon icon; - - public DownloadButton() - { - Children = new Drawable[] - { - icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(30), - Icon = FontAwesome.fa_osu_chevron_down_o, - }, - }; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - icon.ScaleTo(0.9f, 1000, Easing.Out); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - icon.ScaleTo(1f, 500, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - - protected override bool OnHover(InputState state) - { - icon.ScaleTo(1.1f, 500, Easing.OutElastic); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - icon.ScaleTo(1f, 500, Easing.OutElastic); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Overlays.Direct +{ + public class DownloadButton : OsuClickableContainer + { + private readonly SpriteIcon icon; + + public DownloadButton() + { + Children = new Drawable[] + { + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(30), + Icon = FontAwesome.fa_osu_chevron_down_o, + }, + }; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + icon.ScaleTo(0.9f, 1000, Easing.Out); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + icon.ScaleTo(1f, 500, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + + protected override bool OnHover(InputState state) + { + icon.ScaleTo(1.1f, 500, Easing.OutElastic); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + icon.ScaleTo(1f, 500, Easing.OutElastic); + } + } +} diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 84a09547aa..c77994efb2 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -1,117 +1,117 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Overlays.SearchableList; -using osu.Game.Rulesets; - -namespace osu.Game.Overlays.Direct -{ - public class FilterControl : SearchableListFilterControl - { - public readonly Bindable Ruleset = new Bindable(); - private FillFlowContainer modeButtons; - - protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); - protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; - protected override Drawable CreateSupplementaryControls() - { - modeButtons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10f, 0f), - }; - - return modeButtons; - } - - [BackgroundDependencyLoader(true)] - private void load(OsuGame game, RulesetStore rulesets, OsuColour colours) - { - DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; - - Ruleset.BindTo(game?.Ruleset ?? new Bindable { Value = rulesets.GetRuleset(0) }); - foreach (var r in rulesets.AvailableRulesets) - { - modeButtons.Add(new RulesetToggleButton(Ruleset, r)); - } - } - - private class RulesetToggleButton : OsuClickableContainer - { - private Drawable icon - { - get { return iconContainer.Icon; } - set { iconContainer.Icon = value; } - } - - private RulesetInfo ruleset; - public RulesetInfo Ruleset - { - get { return ruleset; } - set - { - ruleset = value; - icon = Ruleset.CreateInstance().CreateIcon(); - } - } - - private readonly Bindable bindable; - - private readonly ConstrainedIconContainer iconContainer; - - private void Bindable_ValueChanged(RulesetInfo obj) - { - iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); - } - - public RulesetToggleButton(Bindable bindable, RulesetInfo ruleset) - { - this.bindable = bindable; - AutoSizeAxes = Axes.Both; - - Children = new[] - { - iconContainer = new ConstrainedIconContainer - { - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Size = new Vector2(32), - } - }; - - Ruleset = ruleset; - bindable.ValueChanged += Bindable_ValueChanged; - Bindable_ValueChanged(bindable.Value); - Action = () => bindable.Value = Ruleset; - } - - protected override void Dispose(bool isDisposing) - { - if (bindable != null) - bindable.ValueChanged -= Bindable_ValueChanged; - base.Dispose(isDisposing); - } - } - } - - public enum DirectSortCriteria - { - Relevance, - Title, - Artist, - Creator, - Difficulty, - Ranked, - Rating, - Plays, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Overlays.SearchableList; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.Direct +{ + public class FilterControl : SearchableListFilterControl + { + public readonly Bindable Ruleset = new Bindable(); + private FillFlowContainer modeButtons; + + protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); + protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; + protected override Drawable CreateSupplementaryControls() + { + modeButtons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10f, 0f), + }; + + return modeButtons; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuGame game, RulesetStore rulesets, OsuColour colours) + { + DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; + + Ruleset.BindTo(game?.Ruleset ?? new Bindable { Value = rulesets.GetRuleset(0) }); + foreach (var r in rulesets.AvailableRulesets) + { + modeButtons.Add(new RulesetToggleButton(Ruleset, r)); + } + } + + private class RulesetToggleButton : OsuClickableContainer + { + private Drawable icon + { + get { return iconContainer.Icon; } + set { iconContainer.Icon = value; } + } + + private RulesetInfo ruleset; + public RulesetInfo Ruleset + { + get { return ruleset; } + set + { + ruleset = value; + icon = Ruleset.CreateInstance().CreateIcon(); + } + } + + private readonly Bindable bindable; + + private readonly ConstrainedIconContainer iconContainer; + + private void Bindable_ValueChanged(RulesetInfo obj) + { + iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); + } + + public RulesetToggleButton(Bindable bindable, RulesetInfo ruleset) + { + this.bindable = bindable; + AutoSizeAxes = Axes.Both; + + Children = new[] + { + iconContainer = new ConstrainedIconContainer + { + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + Size = new Vector2(32), + } + }; + + Ruleset = ruleset; + bindable.ValueChanged += Bindable_ValueChanged; + Bindable_ValueChanged(bindable.Value); + Action = () => bindable.Value = Ruleset; + } + + protected override void Dispose(bool isDisposing) + { + if (bindable != null) + bindable.ValueChanged -= Bindable_ValueChanged; + base.Dispose(isDisposing); + } + } + } + + public enum DirectSortCriteria + { + Relevance, + Title, + Artist, + Creator, + Difficulty, + Ranked, + Rating, + Plays, + } +} diff --git a/osu.Game/Overlays/Direct/Header.cs b/osu.Game/Overlays/Direct/Header.cs index 252e732614..01180f1fde 100644 --- a/osu.Game/Overlays/Direct/Header.cs +++ b/osu.Game/Overlays/Direct/Header.cs @@ -1,38 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.SearchableList; - -namespace osu.Game.Overlays.Direct -{ - public class Header : SearchableListHeader - { - protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a"); - - protected override DirectTab DefaultTab => DirectTab.Search; - protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 }; - protected override FontAwesome Icon => FontAwesome.fa_osu_chevron_down_o; - - public Header() - { - Tabs.Current.Value = DirectTab.NewestMaps; - Tabs.Current.TriggerChange(); - } - } - - public enum DirectTab - { - Search, - [Description("Newest Maps")] - NewestMaps = DirectSortCriteria.Ranked, - [Description("Top Rated")] - TopRated = DirectSortCriteria.Rating, - [Description("Most Played")] - MostPlayed = DirectSortCriteria.Plays, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.SearchableList; + +namespace osu.Game.Overlays.Direct +{ + public class Header : SearchableListHeader + { + protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a"); + + protected override DirectTab DefaultTab => DirectTab.Search; + protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 }; + protected override FontAwesome Icon => FontAwesome.fa_osu_chevron_down_o; + + public Header() + { + Tabs.Current.Value = DirectTab.NewestMaps; + Tabs.Current.TriggerChange(); + } + } + + public enum DirectTab + { + Search, + [Description("Newest Maps")] + NewestMaps = DirectSortCriteria.Ranked, + [Description("Top Rated")] + TopRated = DirectSortCriteria.Rating, + [Description("Most Played")] + MostPlayed = DirectSortCriteria.Plays, + } +} diff --git a/osu.Game/Overlays/Direct/IconPill.cs b/osu.Game/Overlays/Direct/IconPill.cs index 33b67bdf13..61a0c63814 100644 --- a/osu.Game/Overlays/Direct/IconPill.cs +++ b/osu.Game/Overlays/Direct/IconPill.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Direct -{ - public class IconPill : CircularContainer - { - public IconPill(FontAwesome icon) - { - AutoSizeAxes = Axes.Both; - Masking = true; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f, - }, - new Container - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding(5), - Child = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = icon, - Size = new Vector2(12), - }, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Direct +{ + public class IconPill : CircularContainer + { + public IconPill(FontAwesome icon) + { + AutoSizeAxes = Axes.Both; + Masking = true; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new Container + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding(5), + Child = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = icon, + Size = new Vector2(12), + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 0fb988ead7..9f36d5acb7 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -1,213 +1,213 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Track; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.IO.Stores; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Direct -{ - public class PlayButton : Container - { - public readonly Bindable Playing = new Bindable(); - public Track Preview { get; private set; } - - private BeatmapSetInfo beatmapSet; - - public BeatmapSetInfo BeatmapSet - { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - Playing.Value = false; - trackLoader = null; - Preview = null; - } - } - - private Color4 hoverColour; - private readonly SpriteIcon icon; - private readonly LoadingAnimation loadingAnimation; - - private readonly BindableDouble muteBindable = new BindableDouble(); - - private const float transition_duration = 500; - - private bool loading - { - set - { - if (value) - { - loadingAnimation.Show(); - icon.FadeOut(transition_duration * 5, Easing.OutQuint); - } - else - { - loadingAnimation.Hide(); - icon.FadeIn(transition_duration, Easing.OutQuint); - } - } - } - - public PlayButton(BeatmapSetInfo setInfo = null) - { - BeatmapSet = setInfo; - AddRange(new Drawable[] - { - icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.fa_play, - }, - loadingAnimation = new LoadingAnimation(), - }); - - Playing.ValueChanged += playing => - { - icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play; - icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); - updatePreviewTrack(playing); - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour, AudioManager audio) - { - hoverColour = colour.Yellow; - this.audio = audio; - } - - protected override bool OnClick(InputState state) - { - Playing.Value = !Playing.Value; - return true; - } - - protected override bool OnHover(InputState state) - { - icon.FadeColour(hoverColour, 120, Easing.InOutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - if (!Playing.Value) - icon.FadeColour(Color4.White, 120, Easing.InOutQuint); - base.OnHoverLost(state); - } - - protected override void Update() - { - base.Update(); - - if (Preview?.HasCompleted ?? false) - { - Playing.Value = false; - Preview = null; - } - } - - private void updatePreviewTrack(bool playing) - { - if (playing) - { - if (Preview == null) - { - beginAudioLoad(); - return; - } - - Preview.Restart(); - - audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); - } - else - { - audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); - - Preview?.Stop(); - loading = false; - } - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - Playing.Value = false; - } - - private TrackLoader trackLoader; - private AudioManager audio; - - private void beginAudioLoad() - { - if (trackLoader != null) - { - Preview = trackLoader.Preview; - Playing.TriggerChange(); - return; - } - - loading = true; - - LoadComponentAsync(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3"), - d => - { - // We may have been replaced by another loader - if (trackLoader != d) return; - - Preview = d?.Preview; - Playing.TriggerChange(); - loading = false; - Add(trackLoader); - }); - } - - private class TrackLoader : Drawable - { - private readonly string preview; - - public Track Preview; - private TrackManager trackManager; - - public TrackLoader(string preview) - { - this.preview = preview; - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio, FrameworkConfigManager config) - { - // create a local trackManager to bypass the mute we are applying above. - audio.AddItem(trackManager = new TrackManager(new OnlineStore())); - - // add back the user's music volume setting (since we are no longer in the global TrackManager's hierarchy). - config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); - - Preview = trackManager.Get(preview); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - trackManager?.Dispose(); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.IO.Stores; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Direct +{ + public class PlayButton : Container + { + public readonly Bindable Playing = new Bindable(); + public Track Preview { get; private set; } + + private BeatmapSetInfo beatmapSet; + + public BeatmapSetInfo BeatmapSet + { + get { return beatmapSet; } + set + { + if (value == beatmapSet) return; + beatmapSet = value; + + Playing.Value = false; + trackLoader = null; + Preview = null; + } + } + + private Color4 hoverColour; + private readonly SpriteIcon icon; + private readonly LoadingAnimation loadingAnimation; + + private readonly BindableDouble muteBindable = new BindableDouble(); + + private const float transition_duration = 500; + + private bool loading + { + set + { + if (value) + { + loadingAnimation.Show(); + icon.FadeOut(transition_duration * 5, Easing.OutQuint); + } + else + { + loadingAnimation.Hide(); + icon.FadeIn(transition_duration, Easing.OutQuint); + } + } + } + + public PlayButton(BeatmapSetInfo setInfo = null) + { + BeatmapSet = setInfo; + AddRange(new Drawable[] + { + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_play, + }, + loadingAnimation = new LoadingAnimation(), + }); + + Playing.ValueChanged += playing => + { + icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play; + icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); + updatePreviewTrack(playing); + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour, AudioManager audio) + { + hoverColour = colour.Yellow; + this.audio = audio; + } + + protected override bool OnClick(InputState state) + { + Playing.Value = !Playing.Value; + return true; + } + + protected override bool OnHover(InputState state) + { + icon.FadeColour(hoverColour, 120, Easing.InOutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (!Playing.Value) + icon.FadeColour(Color4.White, 120, Easing.InOutQuint); + base.OnHoverLost(state); + } + + protected override void Update() + { + base.Update(); + + if (Preview?.HasCompleted ?? false) + { + Playing.Value = false; + Preview = null; + } + } + + private void updatePreviewTrack(bool playing) + { + if (playing) + { + if (Preview == null) + { + beginAudioLoad(); + return; + } + + Preview.Restart(); + + audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable); + } + else + { + audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); + + Preview?.Stop(); + loading = false; + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + Playing.Value = false; + } + + private TrackLoader trackLoader; + private AudioManager audio; + + private void beginAudioLoad() + { + if (trackLoader != null) + { + Preview = trackLoader.Preview; + Playing.TriggerChange(); + return; + } + + loading = true; + + LoadComponentAsync(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3"), + d => + { + // We may have been replaced by another loader + if (trackLoader != d) return; + + Preview = d?.Preview; + Playing.TriggerChange(); + loading = false; + Add(trackLoader); + }); + } + + private class TrackLoader : Drawable + { + private readonly string preview; + + public Track Preview; + private TrackManager trackManager; + + public TrackLoader(string preview) + { + this.preview = preview; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio, FrameworkConfigManager config) + { + // create a local trackManager to bypass the mute we are applying above. + audio.AddItem(trackManager = new TrackManager(new OnlineStore())); + + // add back the user's music volume setting (since we are no longer in the global TrackManager's hierarchy). + config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); + + Preview = trackManager.Get(preview); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + trackManager?.Dispose(); + } + } + } +} diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 3f1aa04c36..6c9433836a 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -1,348 +1,348 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Threading; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Direct; -using osu.Game.Overlays.SearchableList; -using osu.Game.Rulesets; -using OpenTK.Graphics; - -namespace osu.Game.Overlays -{ - public class DirectOverlay : SearchableListOverlay - { - private const float panel_padding = 10f; - - private APIAccess api; - private RulesetStore rulesets; - private BeatmapManager beatmaps; - - private readonly FillFlowContainer resultCountsContainer; - private readonly OsuSpriteText resultCountsText; - private FillFlowContainer panels; - private DirectPanel playing; - - protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74"); - protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71"); - protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265"); - - protected override SearchableListHeader CreateHeader() => new Header(); - protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); - - private IEnumerable beatmapSets; - - public IEnumerable BeatmapSets - { - get { return beatmapSets; } - set - { - if (beatmapSets?.Equals(value) ?? false) return; - - beatmapSets = value?.ToList(); - - if (beatmapSets == null) return; - - var artists = new List(); - var songs = new List(); - var tags = new List(); - foreach (var s in beatmapSets) - { - artists.Add(s.Metadata.Artist); - songs.Add(s.Metadata.Title); - tags.AddRange(s.Metadata.Tags.Split(' ')); - } - - ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags)); - } - } - - private ResultCounts resultAmounts; - - public ResultCounts ResultAmounts - { - get { return resultAmounts; } - set - { - if (value == ResultAmounts) return; - resultAmounts = value; - - updateResultCounts(); - } - } - - public DirectOverlay() - { - RelativeSizeAxes = Axes.Both; - - // osu!direct colours are not part of the standard palette - - FirstWaveColour = OsuColour.FromHex(@"19b0e2"); - SecondWaveColour = OsuColour.FromHex(@"2280a2"); - ThirdWaveColour = OsuColour.FromHex(@"005774"); - FourthWaveColour = OsuColour.FromHex(@"003a4e"); - - ScrollFlow.Children = new Drawable[] - { - resultCountsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Top = 5 }, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Found ", - TextSize = 15, - }, - resultCountsText = new OsuSpriteText - { - TextSize = 15, - Font = @"Exo2.0-Bold", - }, - } - }, - }; - - Filter.Search.Current.ValueChanged += text => - { - if (text != string.Empty) - { - Header.Tabs.Current.Value = DirectTab.Search; - - if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked) - Filter.Tabs.Current.Value = DirectSortCriteria.Relevance; - } - else - { - Header.Tabs.Current.Value = DirectTab.NewestMaps; - - if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance) - Filter.Tabs.Current.Value = DirectSortCriteria.Ranked; - } - }; - ((FilterControl)Filter).Ruleset.ValueChanged += ruleset => Scheduler.AddOnce(updateSearch); - Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels; - Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += rankStatus => Scheduler.AddOnce(updateSearch); - - Header.Tabs.Current.ValueChanged += tab => - { - if (tab != DirectTab.Search) - { - currentQuery.Value = string.Empty; - Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value; - Scheduler.AddOnce(updateSearch); - } - }; - - currentQuery.ValueChanged += v => - { - queryChangedDebounce?.Cancel(); - - if (string.IsNullOrEmpty(v)) - Scheduler.AddOnce(updateSearch); - else - { - BeatmapSets = null; - ResultAmounts = null; - - queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500); - } - }; - - currentQuery.BindTo(Filter.Search.Current); - - Filter.Tabs.Current.ValueChanged += sortCriteria => - { - if (Header.Tabs.Current.Value != DirectTab.Search && sortCriteria != (DirectSortCriteria)Header.Tabs.Current.Value) - Header.Tabs.Current.Value = DirectTab.Search; - - Scheduler.AddOnce(updateSearch); - }; - - updateResultCounts(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps) - { - this.api = api; - this.rulesets = rulesets; - this.beatmaps = beatmaps; - - resultCountsContainer.Colour = colours.Yellow; - - beatmaps.ItemAdded += setAdded; - } - - private void setAdded(BeatmapSetInfo set) => Schedule(() => - { - // if a new map was imported, we should remove it from search results (download completed etc.) - panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire(); - BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID); - }); - - private void updateResultCounts() - { - resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint); - if (ResultAmounts == null) return; - - resultCountsText.Text = pluralize("Artist", ResultAmounts.Artists) + ", " + - pluralize("Song", ResultAmounts.Songs) + ", " + - pluralize("Tag", ResultAmounts.Tags); - } - - private string pluralize(string prefix, int value) - { - return $@"{value} {prefix}" + (value == 1 ? string.Empty : @"s"); - } - - private void recreatePanels(PanelDisplayStyle displayStyle) - { - if (panels != null) - { - panels.FadeOut(200); - panels.Expire(); - panels = null; - - if (playing != null) - { - playing.PreviewPlaying.Value = false; - playing = null; - } - } - - if (BeatmapSets == null) return; - - var newPanels = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(panel_padding), - Margin = new MarginPadding { Top = 10 }, - ChildrenEnumerable = BeatmapSets.Select(b => - { - switch (displayStyle) - { - case PanelDisplayStyle.Grid: - return new DirectGridPanel(b) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }; - default: - return new DirectListPanel(b); - } - }) - }; - - LoadComponentAsync(newPanels, p => - { - if (panels != null) ScrollFlow.Remove(panels); - ScrollFlow.Add(panels = newPanels); - - foreach (DirectPanel panel in p.Children) - panel.PreviewPlaying.ValueChanged += newValue => - { - if (newValue) - { - if (playing != null && playing != panel) - playing.PreviewPlaying.Value = false; - playing = panel; - } - }; - }); - } - - private SearchBeatmapSetsRequest getSetsRequest; - - private readonly Bindable currentQuery = new Bindable(); - - private ScheduledDelegate queryChangedDebounce; - - private void updateSearch() - { - queryChangedDebounce?.Cancel(); - - if (!IsLoaded) return; - - BeatmapSets = null; - ResultAmounts = null; - - getSetsRequest?.Cancel(); - - if (api == null) return; - - if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return; - - getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty, - ((FilterControl)Filter).Ruleset.Value, - Filter.DisplayStyleControl.Dropdown.Current.Value, - Filter.Tabs.Current.Value); //todo: sort direction (?) - - getSetsRequest.Success += response => - { - Task.Run(() => - { - var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList(); - var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID) && !s.DeletePending).Select(r => r.OnlineBeatmapSetID).ToList(); - var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList(); - - // may not need scheduling; loads async internally. - Schedule(() => - { - BeatmapSets = sets; - recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); - }); - }); - }; - - api.Queue(getSetsRequest); - } - - protected override void PopOut() - { - base.PopOut(); - - if (playing != null) - playing.PreviewPlaying.Value = false; - } - - private int distinctCount(List list) => list.Distinct().ToArray().Length; - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (beatmaps != null) - beatmaps.ItemAdded -= setAdded; - } - - public class ResultCounts - { - public readonly int Artists; - public readonly int Songs; - public readonly int Tags; - - public ResultCounts(int artists, int songs, int tags) - { - Artists = artists; - Songs = songs; - Tags = tags; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Threading; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Direct; +using osu.Game.Overlays.SearchableList; +using osu.Game.Rulesets; +using OpenTK.Graphics; + +namespace osu.Game.Overlays +{ + public class DirectOverlay : SearchableListOverlay + { + private const float panel_padding = 10f; + + private APIAccess api; + private RulesetStore rulesets; + private BeatmapManager beatmaps; + + private readonly FillFlowContainer resultCountsContainer; + private readonly OsuSpriteText resultCountsText; + private FillFlowContainer panels; + private DirectPanel playing; + + protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74"); + protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71"); + protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265"); + + protected override SearchableListHeader CreateHeader() => new Header(); + protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); + + private IEnumerable beatmapSets; + + public IEnumerable BeatmapSets + { + get { return beatmapSets; } + set + { + if (beatmapSets?.Equals(value) ?? false) return; + + beatmapSets = value?.ToList(); + + if (beatmapSets == null) return; + + var artists = new List(); + var songs = new List(); + var tags = new List(); + foreach (var s in beatmapSets) + { + artists.Add(s.Metadata.Artist); + songs.Add(s.Metadata.Title); + tags.AddRange(s.Metadata.Tags.Split(' ')); + } + + ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags)); + } + } + + private ResultCounts resultAmounts; + + public ResultCounts ResultAmounts + { + get { return resultAmounts; } + set + { + if (value == ResultAmounts) return; + resultAmounts = value; + + updateResultCounts(); + } + } + + public DirectOverlay() + { + RelativeSizeAxes = Axes.Both; + + // osu!direct colours are not part of the standard palette + + FirstWaveColour = OsuColour.FromHex(@"19b0e2"); + SecondWaveColour = OsuColour.FromHex(@"2280a2"); + ThirdWaveColour = OsuColour.FromHex(@"005774"); + FourthWaveColour = OsuColour.FromHex(@"003a4e"); + + ScrollFlow.Children = new Drawable[] + { + resultCountsContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Top = 5 }, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Found ", + TextSize = 15, + }, + resultCountsText = new OsuSpriteText + { + TextSize = 15, + Font = @"Exo2.0-Bold", + }, + } + }, + }; + + Filter.Search.Current.ValueChanged += text => + { + if (text != string.Empty) + { + Header.Tabs.Current.Value = DirectTab.Search; + + if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked) + Filter.Tabs.Current.Value = DirectSortCriteria.Relevance; + } + else + { + Header.Tabs.Current.Value = DirectTab.NewestMaps; + + if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance) + Filter.Tabs.Current.Value = DirectSortCriteria.Ranked; + } + }; + ((FilterControl)Filter).Ruleset.ValueChanged += ruleset => Scheduler.AddOnce(updateSearch); + Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels; + Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += rankStatus => Scheduler.AddOnce(updateSearch); + + Header.Tabs.Current.ValueChanged += tab => + { + if (tab != DirectTab.Search) + { + currentQuery.Value = string.Empty; + Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value; + Scheduler.AddOnce(updateSearch); + } + }; + + currentQuery.ValueChanged += v => + { + queryChangedDebounce?.Cancel(); + + if (string.IsNullOrEmpty(v)) + Scheduler.AddOnce(updateSearch); + else + { + BeatmapSets = null; + ResultAmounts = null; + + queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500); + } + }; + + currentQuery.BindTo(Filter.Search.Current); + + Filter.Tabs.Current.ValueChanged += sortCriteria => + { + if (Header.Tabs.Current.Value != DirectTab.Search && sortCriteria != (DirectSortCriteria)Header.Tabs.Current.Value) + Header.Tabs.Current.Value = DirectTab.Search; + + Scheduler.AddOnce(updateSearch); + }; + + updateResultCounts(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps) + { + this.api = api; + this.rulesets = rulesets; + this.beatmaps = beatmaps; + + resultCountsContainer.Colour = colours.Yellow; + + beatmaps.ItemAdded += setAdded; + } + + private void setAdded(BeatmapSetInfo set) => Schedule(() => + { + // if a new map was imported, we should remove it from search results (download completed etc.) + panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire(); + BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID); + }); + + private void updateResultCounts() + { + resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint); + if (ResultAmounts == null) return; + + resultCountsText.Text = pluralize("Artist", ResultAmounts.Artists) + ", " + + pluralize("Song", ResultAmounts.Songs) + ", " + + pluralize("Tag", ResultAmounts.Tags); + } + + private string pluralize(string prefix, int value) + { + return $@"{value} {prefix}" + (value == 1 ? string.Empty : @"s"); + } + + private void recreatePanels(PanelDisplayStyle displayStyle) + { + if (panels != null) + { + panels.FadeOut(200); + panels.Expire(); + panels = null; + + if (playing != null) + { + playing.PreviewPlaying.Value = false; + playing = null; + } + } + + if (BeatmapSets == null) return; + + var newPanels = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(panel_padding), + Margin = new MarginPadding { Top = 10 }, + ChildrenEnumerable = BeatmapSets.Select(b => + { + switch (displayStyle) + { + case PanelDisplayStyle.Grid: + return new DirectGridPanel(b) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }; + default: + return new DirectListPanel(b); + } + }) + }; + + LoadComponentAsync(newPanels, p => + { + if (panels != null) ScrollFlow.Remove(panels); + ScrollFlow.Add(panels = newPanels); + + foreach (DirectPanel panel in p.Children) + panel.PreviewPlaying.ValueChanged += newValue => + { + if (newValue) + { + if (playing != null && playing != panel) + playing.PreviewPlaying.Value = false; + playing = panel; + } + }; + }); + } + + private SearchBeatmapSetsRequest getSetsRequest; + + private readonly Bindable currentQuery = new Bindable(); + + private ScheduledDelegate queryChangedDebounce; + + private void updateSearch() + { + queryChangedDebounce?.Cancel(); + + if (!IsLoaded) return; + + BeatmapSets = null; + ResultAmounts = null; + + getSetsRequest?.Cancel(); + + if (api == null) return; + + if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return; + + getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty, + ((FilterControl)Filter).Ruleset.Value, + Filter.DisplayStyleControl.Dropdown.Current.Value, + Filter.Tabs.Current.Value); //todo: sort direction (?) + + getSetsRequest.Success += response => + { + Task.Run(() => + { + var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList(); + var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID) && !s.DeletePending).Select(r => r.OnlineBeatmapSetID).ToList(); + var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList(); + + // may not need scheduling; loads async internally. + Schedule(() => + { + BeatmapSets = sets; + recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); + }); + }); + }; + + api.Queue(getSetsRequest); + } + + protected override void PopOut() + { + base.PopOut(); + + if (playing != null) + playing.PreviewPlaying.Value = false; + } + + private int distinctCount(List list) => list.Distinct().ToArray().Length; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmaps != null) + beatmaps.ItemAdded -= setAdded; + } + + public class ResultCounts + { + public readonly int Artists; + public readonly int Songs; + public readonly int Tags; + + public ResultCounts(int artists, int songs, int tags) + { + Artists = artists; + Songs = songs; + Tags = tags; + } + } + } +} diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index a4c1621266..b939483cd8 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; -using osu.Game.Input.Bindings; -using osu.Game.Overlays.Settings; - -namespace osu.Game.Overlays.KeyBinding -{ - public class GlobalKeyBindingsSection : SettingsSection - { - public override FontAwesome Icon => FontAwesome.fa_osu_hot; - public override string Header => "Global"; - - public GlobalKeyBindingsSection(GlobalActionContainer manager) - { - Add(new DefaultBindingsSubsection(manager)); - Add(new InGameKeyBindingsSubsection(manager)); - } - - - private class DefaultBindingsSubsection : KeyBindingsSubsection - { - protected override string Header => string.Empty; - - public DefaultBindingsSubsection(GlobalActionContainer manager) - : base(null) - { - Defaults = manager.GlobalKeyBindings; - } - } - - private class InGameKeyBindingsSubsection : KeyBindingsSubsection - { - protected override string Header => "In Game"; - - public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null) - { - Defaults = manager.InGameKeyBindings; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using osu.Game.Input.Bindings; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Overlays.KeyBinding +{ + public class GlobalKeyBindingsSection : SettingsSection + { + public override FontAwesome Icon => FontAwesome.fa_osu_hot; + public override string Header => "Global"; + + public GlobalKeyBindingsSection(GlobalActionContainer manager) + { + Add(new DefaultBindingsSubsection(manager)); + Add(new InGameKeyBindingsSubsection(manager)); + } + + + private class DefaultBindingsSubsection : KeyBindingsSubsection + { + protected override string Header => string.Empty; + + public DefaultBindingsSubsection(GlobalActionContainer manager) + : base(null) + { + Defaults = manager.GlobalKeyBindings; + } + } + + private class InGameKeyBindingsSubsection : KeyBindingsSubsection + { + protected override string Header => "In Game"; + + public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null) + { + Defaults = manager.InGameKeyBindings; + } + } + } +} diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 71c346d404..e80a469f91 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -1,374 +1,374 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Framework.Input.Bindings; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Input; -using OpenTK.Graphics; -using OpenTK.Input; - -namespace osu.Game.Overlays.KeyBinding -{ - public class KeyBindingRow : Container, IFilterable - { - private readonly object action; - private readonly IEnumerable bindings; - - private const float transition_time = 150; - - private const float height = 20; - - private const float padding = 5; - - private bool matchingFilter; - - public bool MatchingFilter - { - get { return matchingFilter; } - set - { - matchingFilter = value; - this.FadeTo(!matchingFilter ? 0 : 1); - } - } - - private OsuSpriteText text; - private OsuSpriteText pressAKey; - - private FillFlowContainer buttons; - - public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text); - - public KeyBindingRow(object action, IEnumerable bindings) - { - this.action = action; - this.bindings = bindings; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Masking = true; - CornerRadius = padding; - } - - private KeyBindingStore store; - - [BackgroundDependencyLoader] - private void load(OsuColour colours, KeyBindingStore store) - { - this.store = store; - - EdgeEffect = new EdgeEffectParameters - { - Radius = 2, - Colour = colours.YellowDark.Opacity(0), - Type = EdgeEffectType.Shadow, - Hollow = true, - }; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.6f, - }, - text = new OsuSpriteText - { - Text = action.GetDescription(), - Margin = new MarginPadding(padding), - }, - buttons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }, - pressAKey = new OsuSpriteText - { - Text = "Press a key to change binding, DEL to delete, ESC to cancel.", - Y = height, - Margin = new MarginPadding(padding), - Alpha = 0, - Colour = colours.YellowDark - } - }; - - foreach (var b in bindings) - buttons.Add(new KeyButton(b)); - } - - public void RestoreDefaults() - { - int i = 0; - foreach (var d in Defaults) - { - var button = buttons[i++]; - button.UpdateKeyCombination(d); - store.Update(button.KeyBinding); - } - } - - protected override bool OnHover(InputState state) - { - FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); - - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); - - base.OnHoverLost(state); - } - - public override bool AcceptsFocus => bindTarget == null; - - private KeyButton bindTarget; - - public bool AllowMainMouseButtons; - - public IEnumerable Defaults; - - private bool isModifier(Key k) => k < Key.F1; - - protected override bool OnClick(InputState state) => true; - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - if (!HasFocus || !bindTarget.IsHovered) - return base.OnMouseDown(state, args); - - if (!AllowMainMouseButtons) - { - switch (args.Button) - { - case MouseButton.Left: - case MouseButton.Right: - return true; - } - } - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); - return true; - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - // don't do anything until the last button is released. - if (!HasFocus || state.Mouse.Buttons.Any()) - return base.OnMouseUp(state, args); - - if (bindTarget.IsHovered) - finalise(); - else - updateBindTarget(); - return true; - } - - protected override bool OnWheel(InputState state) - { - if (HasFocus) - { - if (bindTarget.IsHovered) - { - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); - finalise(); - return true; - } - } - - return base.OnWheel(state); - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (!HasFocus) - return false; - - switch (args.Key) - { - case Key.Escape: - finalise(); - return true; - case Key.Delete: - bindTarget.UpdateKeyCombination(InputKey.None); - finalise(); - return true; - } - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); - if (!isModifier(args.Key)) finalise(); - - return true; - } - - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) - { - if (!HasFocus) return base.OnKeyUp(state, args); - - finalise(); - return true; - } - - private void finalise() - { - if (bindTarget != null) - { - store.Update(bindTarget.KeyBinding); - - bindTarget.IsBinding = false; - Schedule(() => - { - // schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.) - bindTarget = null; - }); - } - - if (HasFocus) - GetContainingInputManager().ChangeFocus(null); - - pressAKey.FadeOut(300, Easing.OutQuint); - pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight }; - } - - protected override void OnFocus(InputState state) - { - AutoSizeDuration = 500; - AutoSizeEasing = Easing.OutQuint; - - pressAKey.FadeIn(300, Easing.OutQuint); - pressAKey.Padding = new MarginPadding(); - - updateBindTarget(); - base.OnFocus(state); - } - - protected override void OnFocusLost(InputState state) - { - finalise(); - base.OnFocusLost(state); - } - - private void updateBindTarget() - { - if (bindTarget != null) bindTarget.IsBinding = false; - bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); - if (bindTarget != null) bindTarget.IsBinding = true; - } - - private class KeyButton : Container - { - public readonly Framework.Input.Bindings.KeyBinding KeyBinding; - - private readonly Box box; - public readonly OsuSpriteText Text; - - private Color4 hoverColour; - - private bool isBinding; - - public bool IsBinding - { - get { return isBinding; } - set - { - if (value == isBinding) return; - isBinding = value; - - updateHoverState(); - } - } - - public KeyButton(Framework.Input.Bindings.KeyBinding keyBinding) - { - KeyBinding = keyBinding; - - Margin = new MarginPadding(padding); - - // todo: use this in a meaningful way - // var isDefault = keyBinding.Action is Enum; - - Masking = true; - CornerRadius = padding; - - Height = height; - AutoSizeAxes = Axes.X; - - Children = new Drawable[] - { - new Container - { - AlwaysPresent = true, - Width = 80, - Height = height, - }, - box = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black - }, - Text = new OsuSpriteText - { - Font = "Venera", - TextSize = 10, - Margin = new MarginPadding(5), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = keyBinding.KeyCombination.ReadableString(), - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoverColour = colours.YellowDark; - } - - protected override bool OnHover(InputState state) - { - updateHoverState(); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - updateHoverState(); - base.OnHoverLost(state); - } - - private void updateHoverState() - { - if (isBinding) - { - box.FadeColour(Color4.White, transition_time, Easing.OutQuint); - Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); - } - else - { - box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint); - Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint); - } - } - - public void UpdateKeyCombination(KeyCombination newCombination) - { - KeyBinding.KeyCombination = newCombination; - Text.Text = KeyBinding.KeyCombination.ReadableString(); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Input; +using OpenTK.Graphics; +using OpenTK.Input; + +namespace osu.Game.Overlays.KeyBinding +{ + public class KeyBindingRow : Container, IFilterable + { + private readonly object action; + private readonly IEnumerable bindings; + + private const float transition_time = 150; + + private const float height = 20; + + private const float padding = 5; + + private bool matchingFilter; + + public bool MatchingFilter + { + get { return matchingFilter; } + set + { + matchingFilter = value; + this.FadeTo(!matchingFilter ? 0 : 1); + } + } + + private OsuSpriteText text; + private OsuSpriteText pressAKey; + + private FillFlowContainer buttons; + + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text); + + public KeyBindingRow(object action, IEnumerable bindings) + { + this.action = action; + this.bindings = bindings; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Masking = true; + CornerRadius = padding; + } + + private KeyBindingStore store; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, KeyBindingStore store) + { + this.store = store; + + EdgeEffect = new EdgeEffectParameters + { + Radius = 2, + Colour = colours.YellowDark.Opacity(0), + Type = EdgeEffectType.Shadow, + Hollow = true, + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f, + }, + text = new OsuSpriteText + { + Text = action.GetDescription(), + Margin = new MarginPadding(padding), + }, + buttons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + pressAKey = new OsuSpriteText + { + Text = "Press a key to change binding, DEL to delete, ESC to cancel.", + Y = height, + Margin = new MarginPadding(padding), + Alpha = 0, + Colour = colours.YellowDark + } + }; + + foreach (var b in bindings) + buttons.Add(new KeyButton(b)); + } + + public void RestoreDefaults() + { + int i = 0; + foreach (var d in Defaults) + { + var button = buttons[i++]; + button.UpdateKeyCombination(d); + store.Update(button.KeyBinding); + } + } + + protected override bool OnHover(InputState state) + { + FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); + + base.OnHoverLost(state); + } + + public override bool AcceptsFocus => bindTarget == null; + + private KeyButton bindTarget; + + public bool AllowMainMouseButtons; + + public IEnumerable Defaults; + + private bool isModifier(Key k) => k < Key.F1; + + protected override bool OnClick(InputState state) => true; + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + if (!HasFocus || !bindTarget.IsHovered) + return base.OnMouseDown(state, args); + + if (!AllowMainMouseButtons) + { + switch (args.Button) + { + case MouseButton.Left: + case MouseButton.Right: + return true; + } + } + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); + return true; + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + // don't do anything until the last button is released. + if (!HasFocus || state.Mouse.Buttons.Any()) + return base.OnMouseUp(state, args); + + if (bindTarget.IsHovered) + finalise(); + else + updateBindTarget(); + return true; + } + + protected override bool OnWheel(InputState state) + { + if (HasFocus) + { + if (bindTarget.IsHovered) + { + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); + finalise(); + return true; + } + } + + return base.OnWheel(state); + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!HasFocus) + return false; + + switch (args.Key) + { + case Key.Escape: + finalise(); + return true; + case Key.Delete: + bindTarget.UpdateKeyCombination(InputKey.None); + finalise(); + return true; + } + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); + if (!isModifier(args.Key)) finalise(); + + return true; + } + + protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + { + if (!HasFocus) return base.OnKeyUp(state, args); + + finalise(); + return true; + } + + private void finalise() + { + if (bindTarget != null) + { + store.Update(bindTarget.KeyBinding); + + bindTarget.IsBinding = false; + Schedule(() => + { + // schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.) + bindTarget = null; + }); + } + + if (HasFocus) + GetContainingInputManager().ChangeFocus(null); + + pressAKey.FadeOut(300, Easing.OutQuint); + pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight }; + } + + protected override void OnFocus(InputState state) + { + AutoSizeDuration = 500; + AutoSizeEasing = Easing.OutQuint; + + pressAKey.FadeIn(300, Easing.OutQuint); + pressAKey.Padding = new MarginPadding(); + + updateBindTarget(); + base.OnFocus(state); + } + + protected override void OnFocusLost(InputState state) + { + finalise(); + base.OnFocusLost(state); + } + + private void updateBindTarget() + { + if (bindTarget != null) bindTarget.IsBinding = false; + bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); + if (bindTarget != null) bindTarget.IsBinding = true; + } + + private class KeyButton : Container + { + public readonly Framework.Input.Bindings.KeyBinding KeyBinding; + + private readonly Box box; + public readonly OsuSpriteText Text; + + private Color4 hoverColour; + + private bool isBinding; + + public bool IsBinding + { + get { return isBinding; } + set + { + if (value == isBinding) return; + isBinding = value; + + updateHoverState(); + } + } + + public KeyButton(Framework.Input.Bindings.KeyBinding keyBinding) + { + KeyBinding = keyBinding; + + Margin = new MarginPadding(padding); + + // todo: use this in a meaningful way + // var isDefault = keyBinding.Action is Enum; + + Masking = true; + CornerRadius = padding; + + Height = height; + AutoSizeAxes = Axes.X; + + Children = new Drawable[] + { + new Container + { + AlwaysPresent = true, + Width = 80, + Height = height, + }, + box = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + Text = new OsuSpriteText + { + Font = "Venera", + TextSize = 10, + Margin = new MarginPadding(5), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = keyBinding.KeyCombination.ReadableString(), + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverColour = colours.YellowDark; + } + + protected override bool OnHover(InputState state) + { + updateHoverState(); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + updateHoverState(); + base.OnHoverLost(state); + } + + private void updateHoverState() + { + if (isBinding) + { + box.FadeColour(Color4.White, transition_time, Easing.OutQuint); + Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); + } + else + { + box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint); + Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint); + } + } + + public void UpdateKeyCombination(KeyCombination newCombination) + { + KeyBinding.KeyCombination = newCombination; + Text.Text = KeyBinding.KeyCombination.ReadableString(); + } + } + } +} diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index 6cbc926fc3..895dda872a 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -1,75 +1,75 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Input; -using osu.Game.Overlays.Settings; -using osu.Game.Rulesets; -using OpenTK; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.KeyBinding -{ - public abstract class KeyBindingsSubsection : SettingsSubsection - { - protected IEnumerable Defaults; - - protected RulesetInfo Ruleset; - - private readonly int? variant; - - protected KeyBindingsSubsection(int? variant) - { - this.variant = variant; - - FlowContent.Spacing = new Vector2(0, 1); - FlowContent.Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS }; - } - - [BackgroundDependencyLoader] - private void load(KeyBindingStore store) - { - var bindings = store.Query(Ruleset?.ID, variant); - - foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) - { - int intKey = (int)defaultGroup.Key; - - // one row per valid action. - Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals(intKey))) - { - AllowMainMouseButtons = Ruleset != null, - Defaults = defaultGroup.Select(d => d.KeyCombination) - }); - } - - Add(new ResetButton - { - Action = () => Children.OfType().ForEach(k => k.RestoreDefaults()) - }); - } - } - - public class ResetButton : TriangleButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Text = "Reset"; - RelativeSizeAxes = Axes.X; - Margin = new MarginPadding { Top = 5 }; - Height = 20; - - Content.CornerRadius = 5; - - BackgroundColour = colours.PinkDark; - Triangles.ColourDark = colours.PinkDarker; - Triangles.ColourLight = colours.Pink; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Input; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets; +using OpenTK; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.KeyBinding +{ + public abstract class KeyBindingsSubsection : SettingsSubsection + { + protected IEnumerable Defaults; + + protected RulesetInfo Ruleset; + + private readonly int? variant; + + protected KeyBindingsSubsection(int? variant) + { + this.variant = variant; + + FlowContent.Spacing = new Vector2(0, 1); + FlowContent.Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS }; + } + + [BackgroundDependencyLoader] + private void load(KeyBindingStore store) + { + var bindings = store.Query(Ruleset?.ID, variant); + + foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) + { + int intKey = (int)defaultGroup.Key; + + // one row per valid action. + Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals(intKey))) + { + AllowMainMouseButtons = Ruleset != null, + Defaults = defaultGroup.Select(d => d.KeyCombination) + }); + } + + Add(new ResetButton + { + Action = () => Children.OfType().ForEach(k => k.RestoreDefaults()) + }); + } + } + + public class ResetButton : TriangleButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Text = "Reset"; + RelativeSizeAxes = Axes.X; + Margin = new MarginPadding { Top = 5 }; + Height = 20; + + Content.CornerRadius = 5; + + BackgroundColour = colours.PinkDark; + Triangles.ColourDark = colours.PinkDarker; + Triangles.ColourLight = colours.Pink; + } + } +} diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs index c2a4ea8a12..c4405216e3 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; -using osu.Game.Overlays.Settings; -using osu.Game.Rulesets; - -namespace osu.Game.Overlays.KeyBinding -{ - public class RulesetBindingsSection : SettingsSection - { - public override FontAwesome Icon => FontAwesome.fa_osu_hot; - public override string Header => ruleset.Name; - - private readonly RulesetInfo ruleset; - - public RulesetBindingsSection(RulesetInfo ruleset) - { - this.ruleset = ruleset; - - var r = ruleset.CreateInstance(); - - foreach (var variant in r.AvailableVariants) - Add(new VariantBindingsSubsection(ruleset, variant)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.KeyBinding +{ + public class RulesetBindingsSection : SettingsSection + { + public override FontAwesome Icon => FontAwesome.fa_osu_hot; + public override string Header => ruleset.Name; + + private readonly RulesetInfo ruleset; + + public RulesetBindingsSection(RulesetInfo ruleset) + { + this.ruleset = ruleset; + + var r = ruleset.CreateInstance(); + + foreach (var variant in r.AvailableVariants) + Add(new VariantBindingsSubsection(ruleset, variant)); + } + } +} diff --git a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs index 85a587b003..4f33695e5d 100644 --- a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets; - -namespace osu.Game.Overlays.KeyBinding -{ - public class VariantBindingsSubsection : KeyBindingsSubsection - { - protected override string Header => variantName; - private readonly string variantName; - - public VariantBindingsSubsection(RulesetInfo ruleset, int variant) - : base(variant) - { - Ruleset = ruleset; - - var rulesetInstance = ruleset.CreateInstance(); - - variantName = rulesetInstance.GetVariantName(variant); - Defaults = rulesetInstance.GetDefaultKeyBindings(variant); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.KeyBinding +{ + public class VariantBindingsSubsection : KeyBindingsSubsection + { + protected override string Header => variantName; + private readonly string variantName; + + public VariantBindingsSubsection(RulesetInfo ruleset, int variant) + : base(variant) + { + Ruleset = ruleset; + + var rulesetInstance = ruleset.CreateInstance(); + + variantName = rulesetInstance.GetVariantName(variant); + Defaults = rulesetInstance.GetDefaultKeyBindings(variant); + } + } +} diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs index b311ee68c0..06432cfcea 100644 --- a/osu.Game/Overlays/KeyBindingOverlay.cs +++ b/osu.Game/Overlays/KeyBindingOverlay.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Input.Bindings; -using osu.Game.Overlays.KeyBinding; -using osu.Game.Overlays.Settings; -using osu.Game.Rulesets; - -namespace osu.Game.Overlays -{ - public class KeyBindingOverlay : SettingsOverlay - { - protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!"); - - [BackgroundDependencyLoader(permitNulls: true)] - private void load(RulesetStore rulesets, GlobalActionContainer global) - { - AddSection(new GlobalKeyBindingsSection(global)); - - foreach (var ruleset in rulesets.AvailableRulesets) - AddSection(new RulesetBindingsSection(ruleset)); - } - - public KeyBindingOverlay() - : base(false) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Input.Bindings; +using osu.Game.Overlays.KeyBinding; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays +{ + public class KeyBindingOverlay : SettingsOverlay + { + protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!"); + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(RulesetStore rulesets, GlobalActionContainer global) + { + AddSection(new GlobalKeyBindingsSection(global)); + + foreach (var ruleset in rulesets.AvailableRulesets) + AddSection(new RulesetBindingsSection(ruleset)); + } + + public KeyBindingOverlay() + : base(false) + { + } + } +} diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs index cc784d954d..731c5ee973 100644 --- a/osu.Game/Overlays/LoginOverlay.cs +++ b/osu.Game/Overlays/LoginOverlay.cs @@ -1,92 +1,92 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.General; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Cursor; - -namespace osu.Game.Overlays -{ - public class LoginOverlay : OsuFocusedOverlayContainer - { - private LoginSettings settingsSection; - - private const float transition_time = 400; - - public LoginOverlay() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Children = new Drawable[] - { - new OsuContextMenuContainer - { - Width = 360, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.6f, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Masking = true, - AutoSizeDuration = transition_time, - AutoSizeEasing = Easing.OutQuint, - Children = new Drawable[] - { - settingsSection = new LoginSettings - { - Padding = new MarginPadding(10), - RequestHide = Hide, - }, - new Box - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Height = 3, - Colour = colours.Yellow, - Alpha = 1, - }, - } - } - } - } - }; - } - - protected override void PopIn() - { - base.PopIn(); - - settingsSection.Bounding = true; - this.FadeIn(transition_time, Easing.OutQuint); - - GetContainingInputManager().ChangeFocus(settingsSection); - } - - protected override void PopOut() - { - base.PopOut(); - - settingsSection.Bounding = false; - this.FadeOut(transition_time); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.General; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Cursor; + +namespace osu.Game.Overlays +{ + public class LoginOverlay : OsuFocusedOverlayContainer + { + private LoginSettings settingsSection; + + private const float transition_time = 400; + + public LoginOverlay() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + new OsuContextMenuContainer + { + Width = 360, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Masking = true, + AutoSizeDuration = transition_time, + AutoSizeEasing = Easing.OutQuint, + Children = new Drawable[] + { + settingsSection = new LoginSettings + { + Padding = new MarginPadding(10), + RequestHide = Hide, + }, + new Box + { + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Height = 3, + Colour = colours.Yellow, + Alpha = 1, + }, + } + } + } + } + }; + } + + protected override void PopIn() + { + base.PopIn(); + + settingsSection.Bounding = true; + this.FadeIn(transition_time, Easing.OutQuint); + + GetContainingInputManager().ChangeFocus(settingsSection); + } + + protected override void PopOut() + { + base.PopOut(); + + settingsSection.Bounding = false; + this.FadeOut(transition_time); + } + } +} diff --git a/osu.Game/Overlays/MainSettings.cs b/osu.Game/Overlays/MainSettings.cs index be822f9b06..09b5be6a85 100644 --- a/osu.Game/Overlays/MainSettings.cs +++ b/osu.Game/Overlays/MainSettings.cs @@ -1,151 +1,151 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.Settings; -using osu.Game.Overlays.Settings.Sections; -using osu.Game.Screens.Ranking; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays -{ - public class MainSettings : SettingsOverlay - { - private readonly KeyBindingOverlay keyBindingOverlay; - private BackButton backButton; - - protected override IEnumerable CreateSections() => new SettingsSection[] - { - new GeneralSection(), - new GraphicsSection(), - new GameplaySection(), - new AudioSection(), - new SkinSection(), - new InputSection(keyBindingOverlay), - new OnlineSection(), - new MaintenanceSection(), - new DebugSection(), - }; - - protected override Drawable CreateHeader() => new SettingsHeader("settings", "Change the way osu! behaves"); - protected override Drawable CreateFooter() => new SettingsFooter(); - - public MainSettings() - : base(true) - { - keyBindingOverlay = new KeyBindingOverlay - { - Depth = 1, - Anchor = Anchor.TopRight, - }; - keyBindingOverlay.StateChanged += keyBindingOverlay_StateChanged; - } - - public override bool AcceptsFocus => keyBindingOverlay.State != Visibility.Visible; - - private const float hidden_width = 120; - - private void keyBindingOverlay_StateChanged(Visibility visibility) - { - switch (visibility) - { - case Visibility.Visible: - Background.FadeTo(0.9f, 300, Easing.OutQuint); - Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint); - - SectionsContainer.FadeOut(300, Easing.OutQuint); - ContentContainer.MoveToX(hidden_width - WIDTH, 500, Easing.OutQuint); - - backButton.Delay(100).FadeIn(100); - break; - case Visibility.Hidden: - Background.FadeTo(0.6f, 500, Easing.OutQuint); - Sidebar?.FadeColour(Color4.White, 300, Easing.OutQuint); - - SectionsContainer.FadeIn(500, Easing.OutQuint); - ContentContainer.MoveToX(0, 500, Easing.OutQuint); - - backButton.FadeOut(100); - break; - } - } - - protected override float ExpandedPosition => keyBindingOverlay.State == Visibility.Visible ? hidden_width - WIDTH : base.ExpandedPosition; - - [BackgroundDependencyLoader] - private void load() - { - ContentContainer.Add(keyBindingOverlay); - - ContentContainer.Add(backButton = new BackButton - { - Alpha = 0, - Width = hidden_width, - RelativeSizeAxes = Axes.Y, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Action = () => keyBindingOverlay.Hide() - }); - } - - private class BackButton : OsuClickableContainer - { - private AspectContainer aspect; - - [BackgroundDependencyLoader] - private void load() - { - Children = new Drawable[] - { - aspect = new AspectContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = -15, - Size = new Vector2(15), - Shadow = true, - Icon = FontAwesome.fa_chevron_left - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = 15, - TextSize = 12, - Font = @"Exo2.0-Bold", - Text = @"back", - }, - } - } - }; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - aspect.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections; +using osu.Game.Screens.Ranking; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays +{ + public class MainSettings : SettingsOverlay + { + private readonly KeyBindingOverlay keyBindingOverlay; + private BackButton backButton; + + protected override IEnumerable CreateSections() => new SettingsSection[] + { + new GeneralSection(), + new GraphicsSection(), + new GameplaySection(), + new AudioSection(), + new SkinSection(), + new InputSection(keyBindingOverlay), + new OnlineSection(), + new MaintenanceSection(), + new DebugSection(), + }; + + protected override Drawable CreateHeader() => new SettingsHeader("settings", "Change the way osu! behaves"); + protected override Drawable CreateFooter() => new SettingsFooter(); + + public MainSettings() + : base(true) + { + keyBindingOverlay = new KeyBindingOverlay + { + Depth = 1, + Anchor = Anchor.TopRight, + }; + keyBindingOverlay.StateChanged += keyBindingOverlay_StateChanged; + } + + public override bool AcceptsFocus => keyBindingOverlay.State != Visibility.Visible; + + private const float hidden_width = 120; + + private void keyBindingOverlay_StateChanged(Visibility visibility) + { + switch (visibility) + { + case Visibility.Visible: + Background.FadeTo(0.9f, 300, Easing.OutQuint); + Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint); + + SectionsContainer.FadeOut(300, Easing.OutQuint); + ContentContainer.MoveToX(hidden_width - WIDTH, 500, Easing.OutQuint); + + backButton.Delay(100).FadeIn(100); + break; + case Visibility.Hidden: + Background.FadeTo(0.6f, 500, Easing.OutQuint); + Sidebar?.FadeColour(Color4.White, 300, Easing.OutQuint); + + SectionsContainer.FadeIn(500, Easing.OutQuint); + ContentContainer.MoveToX(0, 500, Easing.OutQuint); + + backButton.FadeOut(100); + break; + } + } + + protected override float ExpandedPosition => keyBindingOverlay.State == Visibility.Visible ? hidden_width - WIDTH : base.ExpandedPosition; + + [BackgroundDependencyLoader] + private void load() + { + ContentContainer.Add(keyBindingOverlay); + + ContentContainer.Add(backButton = new BackButton + { + Alpha = 0, + Width = hidden_width, + RelativeSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Action = () => keyBindingOverlay.Hide() + }); + } + + private class BackButton : OsuClickableContainer + { + private AspectContainer aspect; + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + aspect = new AspectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = -15, + Size = new Vector2(15), + Shadow = true, + Icon = FontAwesome.fa_chevron_left + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 15, + TextSize = 12, + Font = @"Exo2.0-Bold", + Text = @"back", + }, + } + } + }; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + aspect.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + } + } +} diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs index bd6adffdc7..97a9217b23 100644 --- a/osu.Game/Overlays/MedalOverlay.cs +++ b/osu.Game/Overlays/MedalOverlay.cs @@ -1,309 +1,309 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Sprites; -using osu.Game.Users; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Overlays.MedalSplash; -using osu.Framework.Allocation; -using osu.Framework.Audio.Sample; -using osu.Framework.Audio; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input; -using OpenTK.Input; -using System.Linq; -using osu.Framework.Graphics.Shapes; -using System; -using osu.Framework.MathUtils; - -namespace osu.Game.Overlays -{ - public class MedalOverlay : FocusedOverlayContainer - { - public const float DISC_SIZE = 400; - - private const float border_width = 5; - - private readonly Medal medal; - private readonly Box background; - private readonly Container backgroundStrip, particleContainer; - private readonly BackgroundStrip leftStrip, rightStrip; - private readonly CircularContainer disc; - private readonly Sprite innerSpin, outerSpin; - private DrawableMedal drawableMedal; - - private SampleChannel getSample; - - public MedalOverlay(Medal medal) - { - this.medal = medal; - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(60), - }, - outerSpin = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(DISC_SIZE + 500), - Alpha = 0f, - }, - backgroundStrip = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Height = border_width, - Alpha = 0f, - Children = new[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.CentreRight, - Width = 0.5f, - Padding = new MarginPadding { Right = DISC_SIZE / 2 }, - Children = new[] - { - leftStrip = new BackgroundStrip(0f, 1f) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.CentreLeft, - Width = 0.5f, - Padding = new MarginPadding { Left = DISC_SIZE / 2 }, - Children = new[] - { - rightStrip = new BackgroundStrip(1f, 0f), - }, - }, - }, - }, - particleContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Alpha = 0f, - }, - disc = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Alpha = 0f, - Masking = true, - AlwaysPresent = true, - BorderColour = Color4.White, - BorderThickness = border_width, - Size = new Vector2(DISC_SIZE), - Scale = new Vector2(0.8f), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"05262f"), - }, - new Triangles - { - RelativeSizeAxes = Axes.Both, - TriangleScale = 2, - ColourDark = OsuColour.FromHex(@"04222b"), - ColourLight = OsuColour.FromHex(@"052933"), - }, - innerSpin = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(1.05f), - Alpha = 0.25f, - }, - }, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, TextureStore textures, AudioManager audio) - { - getSample = audio.Sample.Get(@"MedalSplash/medal-get"); - innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin"); - - disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = colours.Blue.Opacity(0.5f), - Radius = 50, - }; - - disc.Add(drawableMedal = new DrawableMedal(medal) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Show(); - } - - protected override void Update() - { - base.Update(); - - particleContainer.Add(new MedalParticle(RNG.Next(0, 359))); - } - - protected override bool OnClick(InputState state) - { - dismiss(); - return true; - } - - protected override void OnFocusLost(InputState state) - { - if (state.Keyboard.Keys.Contains(Key.Escape)) dismiss(); - } - - private const double initial_duration = 400; - private const double step_duration = 900; - - protected override void PopIn() - { - base.PopIn(); - - this.FadeIn(200); - background.FlashColour(Color4.White.Opacity(0.25f), 400); - - getSample.Play(); - - innerSpin.Spin(20000, RotationDirection.Clockwise); - outerSpin.Spin(40000, RotationDirection.Clockwise); - - using (BeginDelayedSequence(200, true)) - { - disc.FadeIn(initial_duration) - .ScaleTo(1f, initial_duration * 2, Easing.OutElastic); - - particleContainer.FadeIn(initial_duration); - outerSpin.FadeTo(0.1f, initial_duration * 2); - - using (BeginDelayedSequence(initial_duration + 200, true)) - { - backgroundStrip.FadeIn(step_duration); - leftStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint); - rightStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint); - - this.Animate().Schedule(() => - { - if (drawableMedal.State != DisplayState.Full) - drawableMedal.State = DisplayState.Icon; - }) - .Delay(step_duration).Schedule(() => - { - if (drawableMedal.State != DisplayState.Full) - drawableMedal.State = DisplayState.MedalUnlocked; - }) - .Delay(step_duration).Schedule(() => - { - if (drawableMedal.State != DisplayState.Full) - drawableMedal.State = DisplayState.Full; - }); - } - } - } - - protected override void PopOut() - { - base.PopOut(); - this.FadeOut(200); - } - - private void dismiss() - { - if (drawableMedal.State != DisplayState.Full) - { - // if we haven't yet, play out the animation fully - drawableMedal.State = DisplayState.Full; - FinishTransforms(true); - return; - } - - Hide(); - Expire(); - } - - private class BackgroundStrip : Container - { - public BackgroundStrip(float start, float end) - { - RelativeSizeAxes = Axes.Both; - Width = 0f; - Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(start), Color4.White.Opacity(end)); - Masking = true; - - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - } - }; - } - } - - private class MedalParticle : CircularContainer - { - private readonly float direction; - - private Vector2 positionForOffset(float offset) => new Vector2((float)(offset * Math.Sin(direction)), (float)(offset * Math.Cos(direction))); - - public MedalParticle(float direction) - { - this.direction = direction; - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - Position = positionForOffset(DISC_SIZE / 2); - Masking = true; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = colours.Blue.Opacity(0.5f), - Radius = 5, - }; - - this.MoveTo(positionForOffset(DISC_SIZE / 2 + 200), 500); - this.FadeOut(500); - Expire(); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Sprites; +using osu.Game.Users; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Overlays.MedalSplash; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Audio; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input; +using OpenTK.Input; +using System.Linq; +using osu.Framework.Graphics.Shapes; +using System; +using osu.Framework.MathUtils; + +namespace osu.Game.Overlays +{ + public class MedalOverlay : FocusedOverlayContainer + { + public const float DISC_SIZE = 400; + + private const float border_width = 5; + + private readonly Medal medal; + private readonly Box background; + private readonly Container backgroundStrip, particleContainer; + private readonly BackgroundStrip leftStrip, rightStrip; + private readonly CircularContainer disc; + private readonly Sprite innerSpin, outerSpin; + private DrawableMedal drawableMedal; + + private SampleChannel getSample; + + public MedalOverlay(Medal medal) + { + this.medal = medal; + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(60), + }, + outerSpin = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(DISC_SIZE + 500), + Alpha = 0f, + }, + backgroundStrip = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Height = border_width, + Alpha = 0f, + Children = new[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + Width = 0.5f, + Padding = new MarginPadding { Right = DISC_SIZE / 2 }, + Children = new[] + { + leftStrip = new BackgroundStrip(0f, 1f) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + Width = 0.5f, + Padding = new MarginPadding { Left = DISC_SIZE / 2 }, + Children = new[] + { + rightStrip = new BackgroundStrip(1f, 0f), + }, + }, + }, + }, + particleContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Alpha = 0f, + }, + disc = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0f, + Masking = true, + AlwaysPresent = true, + BorderColour = Color4.White, + BorderThickness = border_width, + Size = new Vector2(DISC_SIZE), + Scale = new Vector2(0.8f), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"05262f"), + }, + new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 2, + ColourDark = OsuColour.FromHex(@"04222b"), + ColourLight = OsuColour.FromHex(@"052933"), + }, + innerSpin = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(1.05f), + Alpha = 0.25f, + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, TextureStore textures, AudioManager audio) + { + getSample = audio.Sample.Get(@"MedalSplash/medal-get"); + innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin"); + + disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colours.Blue.Opacity(0.5f), + Radius = 50, + }; + + disc.Add(drawableMedal = new DrawableMedal(medal) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Show(); + } + + protected override void Update() + { + base.Update(); + + particleContainer.Add(new MedalParticle(RNG.Next(0, 359))); + } + + protected override bool OnClick(InputState state) + { + dismiss(); + return true; + } + + protected override void OnFocusLost(InputState state) + { + if (state.Keyboard.Keys.Contains(Key.Escape)) dismiss(); + } + + private const double initial_duration = 400; + private const double step_duration = 900; + + protected override void PopIn() + { + base.PopIn(); + + this.FadeIn(200); + background.FlashColour(Color4.White.Opacity(0.25f), 400); + + getSample.Play(); + + innerSpin.Spin(20000, RotationDirection.Clockwise); + outerSpin.Spin(40000, RotationDirection.Clockwise); + + using (BeginDelayedSequence(200, true)) + { + disc.FadeIn(initial_duration) + .ScaleTo(1f, initial_duration * 2, Easing.OutElastic); + + particleContainer.FadeIn(initial_duration); + outerSpin.FadeTo(0.1f, initial_duration * 2); + + using (BeginDelayedSequence(initial_duration + 200, true)) + { + backgroundStrip.FadeIn(step_duration); + leftStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint); + rightStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint); + + this.Animate().Schedule(() => + { + if (drawableMedal.State != DisplayState.Full) + drawableMedal.State = DisplayState.Icon; + }) + .Delay(step_duration).Schedule(() => + { + if (drawableMedal.State != DisplayState.Full) + drawableMedal.State = DisplayState.MedalUnlocked; + }) + .Delay(step_duration).Schedule(() => + { + if (drawableMedal.State != DisplayState.Full) + drawableMedal.State = DisplayState.Full; + }); + } + } + } + + protected override void PopOut() + { + base.PopOut(); + this.FadeOut(200); + } + + private void dismiss() + { + if (drawableMedal.State != DisplayState.Full) + { + // if we haven't yet, play out the animation fully + drawableMedal.State = DisplayState.Full; + FinishTransforms(true); + return; + } + + Hide(); + Expire(); + } + + private class BackgroundStrip : Container + { + public BackgroundStrip(float start, float end) + { + RelativeSizeAxes = Axes.Both; + Width = 0f; + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(start), Color4.White.Opacity(end)); + Masking = true; + + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + } + }; + } + } + + private class MedalParticle : CircularContainer + { + private readonly float direction; + + private Vector2 positionForOffset(float offset) => new Vector2((float)(offset * Math.Sin(direction)), (float)(offset * Math.Cos(direction))); + + public MedalParticle(float direction) + { + this.direction = direction; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Position = positionForOffset(DISC_SIZE / 2); + Masking = true; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colours.Blue.Opacity(0.5f), + Radius = 5, + }; + + this.MoveTo(positionForOffset(DISC_SIZE / 2 + 200), 500); + this.FadeOut(500); + Expire(); + } + } + } +} diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs index 8edfdf9d95..ce79e70b1c 100644 --- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs +++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs @@ -1,196 +1,196 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework; -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Users; - -namespace osu.Game.Overlays.MedalSplash -{ - public class DrawableMedal : Container, IStateful - { - private const float scale_when_unlocked = 0.76f; - private const float scale_when_full = 0.6f; - - public event Action StateChanged; - - private readonly Medal medal; - private readonly Container medalContainer; - private readonly Sprite medalSprite, medalGlow; - private readonly OsuSpriteText unlocked, name; - private readonly TextFlowContainer description; - private DisplayState state; - public DrawableMedal(Medal medal) - { - this.medal = medal; - Position = new Vector2(0f, MedalOverlay.DISC_SIZE / 2); - - FillFlowContainer infoFlow; - Children = new Drawable[] - { - medalContainer = new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Alpha = 0f, - Children = new Drawable[] - { - medalSprite = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.81f), - }, - medalGlow = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - }, - }, - unlocked = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = "Medal Unlocked".ToUpper(), - TextSize = 24, - Font = @"Exo2.0-Light", - Alpha = 0f, - Scale = new Vector2(1f / scale_when_unlocked), - }, - infoFlow = new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.6f, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0f, 5f), - Children = new Drawable[] - { - name = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = medal.Name, - TextSize = 20, - Font = @"Exo2.0-Bold", - Alpha = 0f, - Scale = new Vector2(1f / scale_when_full), - }, - description = new OsuTextFlowContainer - { - TextAnchor = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Alpha = 0f, - Scale = new Vector2(1f / scale_when_full), - }, - }, - }, - }; - - description.AddText(medal.Description, s => - { - s.Anchor = Anchor.TopCentre; - s.Origin = Anchor.TopCentre; - s.TextSize = 16; - }); - - medalContainer.OnLoadComplete = d => - { - unlocked.Position = new Vector2(0f, medalContainer.DrawSize.Y / 2 + 10); - infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90); - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, TextureStore textures) - { - medalSprite.Texture = textures.Get(medal.ImageUrl); - medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow"); - description.Colour = colours.BlueLight; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateState(); - } - - public DisplayState State - { - get { return state; } - set - { - if (state == value) return; - - state = value; - updateState(); - - StateChanged?.Invoke(State); - } - } - - private void updateState() - { - if (!IsLoaded) return; - - const double duration = 900; - - switch (state) - { - case DisplayState.None: - medalContainer.ScaleTo(0); - break; - case DisplayState.Icon: - medalContainer - .FadeIn(duration) - .ScaleTo(1, duration, Easing.OutElastic); - break; - case DisplayState.MedalUnlocked: - medalContainer - .FadeTo(1) - .ScaleTo(1); - - this.ScaleTo(scale_when_unlocked, duration, Easing.OutExpo); - this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 30, duration, Easing.OutExpo); - unlocked.FadeInFromZero(duration); - break; - case DisplayState.Full: - medalContainer - .FadeTo(1) - .ScaleTo(1); - - this.ScaleTo(scale_when_full, duration, Easing.OutExpo); - this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 60, duration, Easing.OutExpo); - unlocked.Show(); - name.FadeInFromZero(duration + 100); - description.FadeInFromZero(duration * 2); - break; - } - } - } - - public enum DisplayState - { - None, - Icon, - MedalUnlocked, - Full, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; + +namespace osu.Game.Overlays.MedalSplash +{ + public class DrawableMedal : Container, IStateful + { + private const float scale_when_unlocked = 0.76f; + private const float scale_when_full = 0.6f; + + public event Action StateChanged; + + private readonly Medal medal; + private readonly Container medalContainer; + private readonly Sprite medalSprite, medalGlow; + private readonly OsuSpriteText unlocked, name; + private readonly TextFlowContainer description; + private DisplayState state; + public DrawableMedal(Medal medal) + { + this.medal = medal; + Position = new Vector2(0f, MedalOverlay.DISC_SIZE / 2); + + FillFlowContainer infoFlow; + Children = new Drawable[] + { + medalContainer = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Alpha = 0f, + Children = new Drawable[] + { + medalSprite = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.81f), + }, + medalGlow = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + }, + }, + unlocked = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Medal Unlocked".ToUpper(), + TextSize = 24, + Font = @"Exo2.0-Light", + Alpha = 0f, + Scale = new Vector2(1f / scale_when_unlocked), + }, + infoFlow = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.6f, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 5f), + Children = new Drawable[] + { + name = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = medal.Name, + TextSize = 20, + Font = @"Exo2.0-Bold", + Alpha = 0f, + Scale = new Vector2(1f / scale_when_full), + }, + description = new OsuTextFlowContainer + { + TextAnchor = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0f, + Scale = new Vector2(1f / scale_when_full), + }, + }, + }, + }; + + description.AddText(medal.Description, s => + { + s.Anchor = Anchor.TopCentre; + s.Origin = Anchor.TopCentre; + s.TextSize = 16; + }); + + medalContainer.OnLoadComplete = d => + { + unlocked.Position = new Vector2(0f, medalContainer.DrawSize.Y / 2 + 10); + infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90); + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, TextureStore textures) + { + medalSprite.Texture = textures.Get(medal.ImageUrl); + medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow"); + description.Colour = colours.BlueLight; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateState(); + } + + public DisplayState State + { + get { return state; } + set + { + if (state == value) return; + + state = value; + updateState(); + + StateChanged?.Invoke(State); + } + } + + private void updateState() + { + if (!IsLoaded) return; + + const double duration = 900; + + switch (state) + { + case DisplayState.None: + medalContainer.ScaleTo(0); + break; + case DisplayState.Icon: + medalContainer + .FadeIn(duration) + .ScaleTo(1, duration, Easing.OutElastic); + break; + case DisplayState.MedalUnlocked: + medalContainer + .FadeTo(1) + .ScaleTo(1); + + this.ScaleTo(scale_when_unlocked, duration, Easing.OutExpo); + this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 30, duration, Easing.OutExpo); + unlocked.FadeInFromZero(duration); + break; + case DisplayState.Full: + medalContainer + .FadeTo(1) + .ScaleTo(1); + + this.ScaleTo(scale_when_full, duration, Easing.OutExpo); + this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 60, duration, Easing.OutExpo); + unlocked.Show(); + name.FadeInFromZero(duration + 100); + description.FadeInFromZero(duration * 2); + break; + } + } + } + + public enum DisplayState + { + None, + Icon, + MedalUnlocked, + Full, + } +} diff --git a/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs b/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs index 1d9fdab8d5..d7d9a90e77 100644 --- a/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs +++ b/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Input; -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Overlays.Mods -{ - public class DifficultyIncreaseSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; - public override ModType ModType => ModType.DifficultyIncrease; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - SelectedColour = colours.YellowLight; - } - - public DifficultyIncreaseSection() - { - Header = @"Difficulty Increase"; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Overlays.Mods +{ + public class DifficultyIncreaseSection : ModSection + { + protected override Key[] ToggleKeys => new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; + public override ModType ModType => ModType.DifficultyIncrease; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + SelectedColour = colours.YellowLight; + } + + public DifficultyIncreaseSection() + { + Header = @"Difficulty Increase"; + } + } +} diff --git a/osu.Game/Overlays/Mods/DifficultyReductionSection.cs b/osu.Game/Overlays/Mods/DifficultyReductionSection.cs index 651fc222b5..013deea579 100644 --- a/osu.Game/Overlays/Mods/DifficultyReductionSection.cs +++ b/osu.Game/Overlays/Mods/DifficultyReductionSection.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Input; -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Overlays.Mods -{ - public class DifficultyReductionSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; - public override ModType ModType => ModType.DifficultyReduction; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - SelectedColour = colours.GreenLight; - } - - public DifficultyReductionSection() - { - Header = @"Difficulty Reduction"; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Overlays.Mods +{ + public class DifficultyReductionSection : ModSection + { + protected override Key[] ToggleKeys => new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; + public override ModType ModType => ModType.DifficultyReduction; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + SelectedColour = colours.GreenLight; + } + + public DifficultyReductionSection() + { + Header = @"Difficulty Reduction"; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index a4cc79bca6..2a4f243606 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -1,276 +1,276 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; -using System; -using System.Linq; -using osu.Framework.Graphics.Cursor; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Mods -{ - /// - /// Represents a clickable button which can cycle through one of more mods. - /// - public class ModButton : ModButtonEmpty, IHasTooltip - { - private ModIcon foregroundIcon; - private ModIcon backgroundIcon; - private readonly SpriteText text; - private readonly Container iconsContainer; - - /// - /// Fired when the selection changes. - /// - public Action SelectionChanged; - - public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty; - - private const Easing mod_switch_easing = Easing.InOutSine; - private const double mod_switch_duration = 120; - - // A selected index of -1 means not selected. - private int selectedIndex = -1; - - /// - /// Change the selected mod index of this button. - /// - /// The new index. - /// Whether the selection changed. - private bool changeSelectedIndex(int newIndex) - { - if (newIndex == selectedIndex) return false; - - int direction = newIndex < selectedIndex ? -1 : 1; - bool beforeSelected = Selected; - - Mod modBefore = SelectedMod ?? Mods[0]; - - if (newIndex >= Mods.Length) - newIndex = -1; - else if (newIndex < -1) - newIndex = Mods.Length - 1; - - if (newIndex >= 0 && !Mods[newIndex].HasImplementation) - return false; - - selectedIndex = newIndex; - Mod modAfter = SelectedMod ?? Mods[0]; - - if (beforeSelected != Selected) - { - iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); - iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); - } - - if (modBefore != modAfter) - { - const float rotate_angle = 16; - - 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; - using (BeginDelayedSequence(mod_switch_duration, true)) - { - foregroundIcon - .RotateTo(-rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - backgroundIcon - .RotateTo(rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - Schedule(() => displayMod(modAfter)); - } - } - - foregroundIcon.Highlighted = Selected; - - SelectionChanged?.Invoke(SelectedMod); - return true; - } - - public bool Selected => selectedIndex != -1; - - private Color4 selectedColour; - - public Color4 SelectedColour - { - get { return selectedColour; } - set - { - if (value == selectedColour) return; - selectedColour = value; - if (Selected) foregroundIcon.Colour = value; - } - } - - private Mod mod; - - public Mod Mod - { - get { return mod; } - set - { - mod = value; - - if (mod == null) - { - Mods = Array.Empty(); - Alpha = 0; - } - else - { - Mods = (mod as MultiMod)?.Mods ?? new[] { mod }; - Alpha = 1; - } - - createIcons(); - if (Mods.Length > 0) - { - displayMod(Mods[0]); - } - } - } - - public Mod[] Mods { get; private set; } - - public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - switch (args.Button) - { - case MouseButton.Left: - SelectNext(1); - break; - case MouseButton.Right: - SelectNext(-1); - break; - } - - return true; - } - - /// - /// Select the next available mod in a specified direction. - /// - /// 1 for forwards, -1 for backwards. - public void SelectNext(int direction) - { - int start = selectedIndex + direction; - // wrap around if we are at an extremity. - if (start >= Mods.Length) - start = -1; - else if (start < -1) - start = Mods.Length - 1; - - for (int i = start; i < Mods.Length && i >= 0; i += direction) - if (SelectAt(i)) return; - - Deselect(); - } - - public bool SelectAt(int index) - { - if (!Mods[index].HasImplementation) return false; - - changeSelectedIndex(index); - return true; - } - - public void Deselect() => changeSelectedIndex(-1); - - private void displayMod(Mod mod) - { - if (backgroundIcon != null) - backgroundIcon.Icon = foregroundIcon.Icon; - foregroundIcon.Icon = mod.Icon; - text.Text = mod.Name; - Colour = mod.HasImplementation ? Color4.White : Color4.Gray; - } - - private void createIcons() - { - iconsContainer.Clear(); - if (Mods.Length > 1) - { - iconsContainer.AddRange(new[] - { - backgroundIcon = new PassThroughTooltipModIcon(Mods[1]) - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - Position = new Vector2(1.5f), - }, - foregroundIcon = new PassThroughTooltipModIcon(Mods[0]) - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - Position = new Vector2(-1.5f), - }, - }); - } - else - { - iconsContainer.Add(foregroundIcon = new PassThroughTooltipModIcon(Mod) - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - }); - } - } - - public ModButton(Mod mod) - { - Children = new Drawable[] - { - new Container - { - Size = new Vector2(77f, 80f), - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - iconsContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - } - }, - text = new OsuSpriteText - { - Y = 75, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - TextSize = 18, - }, - new HoverClickSounds() - }; - - Mod = mod; - } - - private class PassThroughTooltipModIcon : ModIcon - { - public override string TooltipText => null; - - public PassThroughTooltipModIcon(Mod mod) - : base(mod) - { - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using System; +using System.Linq; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Mods +{ + /// + /// Represents a clickable button which can cycle through one of more mods. + /// + public class ModButton : ModButtonEmpty, IHasTooltip + { + private ModIcon foregroundIcon; + private ModIcon backgroundIcon; + private readonly SpriteText text; + private readonly Container iconsContainer; + + /// + /// Fired when the selection changes. + /// + public Action SelectionChanged; + + public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty; + + private const Easing mod_switch_easing = Easing.InOutSine; + private const double mod_switch_duration = 120; + + // A selected index of -1 means not selected. + private int selectedIndex = -1; + + /// + /// Change the selected mod index of this button. + /// + /// The new index. + /// Whether the selection changed. + private bool changeSelectedIndex(int newIndex) + { + if (newIndex == selectedIndex) return false; + + int direction = newIndex < selectedIndex ? -1 : 1; + bool beforeSelected = Selected; + + Mod modBefore = SelectedMod ?? Mods[0]; + + if (newIndex >= Mods.Length) + newIndex = -1; + else if (newIndex < -1) + newIndex = Mods.Length - 1; + + if (newIndex >= 0 && !Mods[newIndex].HasImplementation) + return false; + + selectedIndex = newIndex; + Mod modAfter = SelectedMod ?? Mods[0]; + + if (beforeSelected != Selected) + { + iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); + iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); + } + + if (modBefore != modAfter) + { + const float rotate_angle = 16; + + 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; + using (BeginDelayedSequence(mod_switch_duration, true)) + { + foregroundIcon + .RotateTo(-rotate_angle * direction) + .RotateTo(0f, mod_switch_duration, mod_switch_easing); + + backgroundIcon + .RotateTo(rotate_angle * direction) + .RotateTo(0f, mod_switch_duration, mod_switch_easing); + + Schedule(() => displayMod(modAfter)); + } + } + + foregroundIcon.Highlighted = Selected; + + SelectionChanged?.Invoke(SelectedMod); + return true; + } + + public bool Selected => selectedIndex != -1; + + private Color4 selectedColour; + + public Color4 SelectedColour + { + get { return selectedColour; } + set + { + if (value == selectedColour) return; + selectedColour = value; + if (Selected) foregroundIcon.Colour = value; + } + } + + private Mod mod; + + public Mod Mod + { + get { return mod; } + set + { + mod = value; + + if (mod == null) + { + Mods = Array.Empty(); + Alpha = 0; + } + else + { + Mods = (mod as MultiMod)?.Mods ?? new[] { mod }; + Alpha = 1; + } + + createIcons(); + if (Mods.Length > 0) + { + displayMod(Mods[0]); + } + } + } + + public Mod[] Mods { get; private set; } + + public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + switch (args.Button) + { + case MouseButton.Left: + SelectNext(1); + break; + case MouseButton.Right: + SelectNext(-1); + break; + } + + return true; + } + + /// + /// Select the next available mod in a specified direction. + /// + /// 1 for forwards, -1 for backwards. + public void SelectNext(int direction) + { + int start = selectedIndex + direction; + // wrap around if we are at an extremity. + if (start >= Mods.Length) + start = -1; + else if (start < -1) + start = Mods.Length - 1; + + for (int i = start; i < Mods.Length && i >= 0; i += direction) + if (SelectAt(i)) return; + + Deselect(); + } + + public bool SelectAt(int index) + { + if (!Mods[index].HasImplementation) return false; + + changeSelectedIndex(index); + return true; + } + + public void Deselect() => changeSelectedIndex(-1); + + private void displayMod(Mod mod) + { + if (backgroundIcon != null) + backgroundIcon.Icon = foregroundIcon.Icon; + foregroundIcon.Icon = mod.Icon; + text.Text = mod.Name; + Colour = mod.HasImplementation ? Color4.White : Color4.Gray; + } + + private void createIcons() + { + iconsContainer.Clear(); + if (Mods.Length > 1) + { + iconsContainer.AddRange(new[] + { + backgroundIcon = new PassThroughTooltipModIcon(Mods[1]) + { + Origin = Anchor.BottomRight, + Anchor = Anchor.BottomRight, + Position = new Vector2(1.5f), + }, + foregroundIcon = new PassThroughTooltipModIcon(Mods[0]) + { + Origin = Anchor.BottomRight, + Anchor = Anchor.BottomRight, + Position = new Vector2(-1.5f), + }, + }); + } + else + { + iconsContainer.Add(foregroundIcon = new PassThroughTooltipModIcon(Mod) + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + }); + } + } + + public ModButton(Mod mod) + { + Children = new Drawable[] + { + new Container + { + Size = new Vector2(77f, 80f), + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Children = new Drawable[] + { + iconsContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + } + } + }, + text = new OsuSpriteText + { + Y = 75, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + TextSize = 18, + }, + new HoverClickSounds() + }; + + Mod = mod; + } + + private class PassThroughTooltipModIcon : ModIcon + { + public override string TooltipText => null; + + public PassThroughTooltipModIcon(Mod mod) + : base(mod) + { + } + } + } +} diff --git a/osu.Game/Overlays/Mods/ModButtonEmpty.cs b/osu.Game/Overlays/Mods/ModButtonEmpty.cs index e9e2683816..9129856c8f 100644 --- a/osu.Game/Overlays/Mods/ModButtonEmpty.cs +++ b/osu.Game/Overlays/Mods/ModButtonEmpty.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Overlays.Mods -{ - /// - /// A mod button used exclusively for providing an empty space the size of a mod button. - /// - public class ModButtonEmpty : Container - { - public ModButtonEmpty() - { - Size = new Vector2(100f); - AlwaysPresent = true; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Overlays.Mods +{ + /// + /// A mod button used exclusively for providing an empty space the size of a mod button. + /// + public class ModButtonEmpty : Container + { + public ModButtonEmpty() + { + Size = new Vector2(100f); + AlwaysPresent = true; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 4765787caf..48f51f72ca 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -1,152 +1,152 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using System; -using System.Linq; -using System.Collections.Generic; - -namespace osu.Game.Overlays.Mods -{ - public abstract class ModSection : Container - { - private readonly OsuSpriteText headerLabel; - - public FillFlowContainer ButtonsContainer { get; } - - public Action Action; - protected abstract Key[] ToggleKeys { get; } - public abstract ModType ModType { get; } - - public string Header - { - get => headerLabel.Text; - set => headerLabel.Text = value; - } - - public IEnumerable SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null); - - public IEnumerable Mods - { - set - { - var modContainers = value.Select(m => - { - if (m == null) - return new ModButtonEmpty(); - - return new ModButton(m) - { - SelectedColour = selectedColour, - SelectionChanged = Action, - }; - }).ToArray(); - - ButtonsContainer.Children = modContainers; - buttons = modContainers.OfType().ToArray(); - } - } - - private ModButton[] buttons = { }; - - private Color4 selectedColour = Color4.White; - public Color4 SelectedColour - { - get => selectedColour; - set - { - if (value == selectedColour) return; - selectedColour = value; - - foreach (ModButton button in buttons) - button.SelectedColour = value; - } - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - var index = Array.IndexOf(ToggleKeys, args.Key); - if (index > -1 && index < buttons.Length) - buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1); - - return base.OnKeyDown(state, args); - } - - public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); - - /// - /// Deselect one or more mods in this section. - /// - /// The types of s which should be deselected. - /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(IEnumerable modTypes, bool immediate = false) - { - int delay = 0; - foreach (var button in buttons) - { - Mod selected = button.SelectedMod; - if (selected == null) continue; - foreach (var type in modTypes) - if (type.IsInstanceOfType(selected)) - { - if (immediate) - button.Deselect(); - else - Scheduler.AddDelayed(button.Deselect, delay += 50); - } - } - } - - /// - /// Select one or more mods in this section and deselects all other ones. - /// - /// The types of s which should be selected. - public void SelectTypes(IEnumerable modTypes) - { - foreach (var button in buttons) - { - int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m))); - - if (i >= 0) - button.SelectAt(i); - else - button.Deselect(); - } - } - - protected ModSection() - { - AutoSizeAxes = Axes.Y; - - Children = new Drawable[] - { - headerLabel = new OsuSpriteText - { - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Position = new Vector2(0f, 0f), - Font = @"Exo2.0-Bold" - }, - ButtonsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Spacing = new Vector2(50f, 0f), - Margin = new MarginPadding - { - Top = 6, - }, - AlwaysPresent = true - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; +using System; +using System.Linq; +using System.Collections.Generic; + +namespace osu.Game.Overlays.Mods +{ + public abstract class ModSection : Container + { + private readonly OsuSpriteText headerLabel; + + public FillFlowContainer ButtonsContainer { get; } + + public Action Action; + protected abstract Key[] ToggleKeys { get; } + public abstract ModType ModType { get; } + + public string Header + { + get => headerLabel.Text; + set => headerLabel.Text = value; + } + + public IEnumerable SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null); + + public IEnumerable Mods + { + set + { + var modContainers = value.Select(m => + { + if (m == null) + return new ModButtonEmpty(); + + return new ModButton(m) + { + SelectedColour = selectedColour, + SelectionChanged = Action, + }; + }).ToArray(); + + ButtonsContainer.Children = modContainers; + buttons = modContainers.OfType().ToArray(); + } + } + + private ModButton[] buttons = { }; + + private Color4 selectedColour = Color4.White; + public Color4 SelectedColour + { + get => selectedColour; + set + { + if (value == selectedColour) return; + selectedColour = value; + + foreach (ModButton button in buttons) + button.SelectedColour = value; + } + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + var index = Array.IndexOf(ToggleKeys, args.Key); + if (index > -1 && index < buttons.Length) + buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1); + + return base.OnKeyDown(state, args); + } + + public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); + + /// + /// Deselect one or more mods in this section. + /// + /// The types of s which should be deselected. + /// Set to true to bypass animations and update selections immediately. + public void DeselectTypes(IEnumerable modTypes, bool immediate = false) + { + int delay = 0; + foreach (var button in buttons) + { + Mod selected = button.SelectedMod; + if (selected == null) continue; + foreach (var type in modTypes) + if (type.IsInstanceOfType(selected)) + { + if (immediate) + button.Deselect(); + else + Scheduler.AddDelayed(button.Deselect, delay += 50); + } + } + } + + /// + /// Select one or more mods in this section and deselects all other ones. + /// + /// The types of s which should be selected. + public void SelectTypes(IEnumerable modTypes) + { + foreach (var button in buttons) + { + int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m))); + + if (i >= 0) + button.SelectAt(i); + else + button.Deselect(); + } + } + + protected ModSection() + { + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + headerLabel = new OsuSpriteText + { + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + Position = new Vector2(0f, 0f), + Font = @"Exo2.0-Bold" + }, + ButtonsContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Spacing = new Vector2(50f, 0f), + Margin = new MarginPadding + { + Top = 6, + }, + AlwaysPresent = true + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d8c95da94f..48c38c467e 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -1,383 +1,383 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Mods -{ - public class ModSelectOverlay : WaveOverlayContainer - { - private const float content_width = 0.8f; - - protected Color4 LowMultiplierColour, HighMultiplierColour; - - protected readonly TriangleButton DeselectAllButton; - protected readonly OsuSpriteText MultiplierLabel; - private readonly FillFlowContainer footerContainer; - - protected override bool BlockPassThroughKeyboard => false; - - protected readonly FillFlowContainer ModSectionsContainer; - - public readonly Bindable> SelectedMods = new Bindable>(); - - public readonly Bindable Ruleset = new Bindable(); - - private void rulesetChanged(RulesetInfo newRuleset) - { - var instance = newRuleset.CreateInstance(); - - foreach (ModSection section in ModSectionsContainer.Children) - section.Mods = instance.GetModsFor(section.ModType); - refreshSelectedMods(); - } - - [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets, AudioManager audio) - { - SelectedMods.ValueChanged += selectedModsChanged; - - LowMultiplierColour = colours.Red; - HighMultiplierColour = colours.Green; - - if (osu != null) - Ruleset.BindTo(osu.Ruleset); - else - Ruleset.Value = rulesets.AvailableRulesets.First(); - - Ruleset.ValueChanged += rulesetChanged; - Ruleset.TriggerChange(); - - sampleOn = audio.Sample.Get(@"UI/check-on"); - sampleOff = audio.Sample.Get(@"UI/check-off"); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - Ruleset.UnbindAll(); - SelectedMods.UnbindAll(); - } - - private void selectedModsChanged(IEnumerable obj) - { - foreach (ModSection section in ModSectionsContainer.Children) - section.SelectTypes(obj.Select(m => m.GetType()).ToList()); - - updateMods(); - } - - private void updateMods() - { - double multiplier = 1.0; - bool ranked = true; - - foreach (Mod mod in SelectedMods.Value) - { - multiplier *= mod.ScoreMultiplier; - ranked &= mod.Ranked; - } - - MultiplierLabel.Text = $"{multiplier:N2}x"; - if (!ranked) - MultiplierLabel.Text += " (Unranked)"; - - if (multiplier > 1.0) - MultiplierLabel.FadeColour(HighMultiplierColour, 200); - else if (multiplier < 1.0) - MultiplierLabel.FadeColour(LowMultiplierColour, 200); - else - MultiplierLabel.FadeColour(Color4.White, 200); - } - - protected override void PopOut() - { - base.PopOut(); - - footerContainer.MoveToX(footerContainer.DrawSize.X, DISAPPEAR_DURATION, Easing.InSine); - footerContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine); - - foreach (ModSection section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.MoveToX(100f, DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine); - } - } - - protected override void PopIn() - { - base.PopIn(); - - footerContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint); - footerContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint); - - foreach (ModSection section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint); - } - } - - public void DeselectAll() - { - foreach (ModSection section in ModSectionsContainer.Children) - section.DeselectAll(); - - refreshSelectedMods(); - } - - /// - /// Deselect one or more mods. - /// - /// The types of s which should be deselected. - /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(Type[] modTypes, bool immediate = false) - { - if (modTypes.Length == 0) return; - foreach (ModSection section in ModSectionsContainer.Children) - section.DeselectTypes(modTypes, immediate); - } - - - private SampleChannel sampleOn, sampleOff; - - private void modButtonPressed(Mod selectedMod) - { - if (selectedMod != null) - { - if (State == Visibility.Visible) sampleOn?.Play(); - DeselectTypes(selectedMod.IncompatibleMods, true); - } - else - { - if (State == Visibility.Visible) sampleOff?.Play(); - } - - refreshSelectedMods(); - } - - private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); - - public ModSelectOverlay() - { - FirstWaveColour = OsuColour.FromHex(@"19b0e2"); - SecondWaveColour = OsuColour.FromHex(@"2280a2"); - ThirdWaveColour = OsuColour.FromHex(@"005774"); - FourthWaveColour = OsuColour.FromHex(@"003a4e"); - - Height = 510; - Content.RelativeSizeAxes = Axes.X; - Content.AutoSizeAxes = Axes.Y; - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(36, 50, 68, 255) - }, - new Triangles - { - TriangleScale = 5, - RelativeSizeAxes = Axes.X, - Height = Height, //set the height from the start to ensure correct triangle density. - ColourLight = new Color4(53, 66, 82, 255), - ColourDark = new Color4(41, 54, 70, 255), - }, - }, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0f, 10f), - Children = new Drawable[] - { - // Header - new Container - { - RelativeSizeAxes = Axes.X, - Height = 82, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(10).Opacity(100), - }, - new FillFlowContainer - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Width = content_width, - Padding = new MarginPadding - { - Top = 10, - Bottom = 10, - }, - Children = new Drawable[] - { - new OsuSpriteText - { - Font = @"Exo2.0-Bold", - Text = @"Gameplay Mods", - TextSize = 22, - Shadow = true, - Margin = new MarginPadding - { - Bottom = 4, - }, - }, - new OsuSpriteText - { - Text = @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.", - TextSize = 18, - Shadow = true, - }, - new OsuSpriteText - { - Text = @"Others are just for fun.", - TextSize = 18, - Shadow = true, - }, - }, - }, - }, - }, - // Body - ModSectionsContainer = new FillFlowContainer - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, 10f), - Width = content_width, - Children = new ModSection[] - { - new DifficultyReductionSection - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Action = modButtonPressed, - }, - new DifficultyIncreaseSection - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Action = modButtonPressed, - }, - new SpecialSection - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Action = modButtonPressed, - }, - } - }, - // Footer - new Container - { - RelativeSizeAxes = Axes.X, - Height = 70, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(172, 20, 116, 255), - Alpha = 0.5f, - }, - footerContainer = new FillFlowContainer - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Width = content_width, - Direction = FillDirection.Horizontal, - Padding = new MarginPadding - { - Vertical = 15 - }, - Children = new Drawable[] - { - DeselectAllButton = new TriangleButton - { - Width = 180, - Text = "Deselect All", - Action = DeselectAll, - Margin = new MarginPadding - { - Right = 20 - } - }, - new OsuSpriteText - { - Text = @"Score Multiplier: ", - TextSize = 30, - Shadow = true, - Margin = new MarginPadding - { - Top = 5 - } - }, - MultiplierLabel = new OsuSpriteText - { - Font = @"Exo2.0-Bold", - TextSize = 30, - Shadow = true, - Margin = new MarginPadding - { - Top = 5 - } - } - } - } - }, - }, - }, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Mods +{ + public class ModSelectOverlay : WaveOverlayContainer + { + private const float content_width = 0.8f; + + protected Color4 LowMultiplierColour, HighMultiplierColour; + + protected readonly TriangleButton DeselectAllButton; + protected readonly OsuSpriteText MultiplierLabel; + private readonly FillFlowContainer footerContainer; + + protected override bool BlockPassThroughKeyboard => false; + + protected readonly FillFlowContainer ModSectionsContainer; + + public readonly Bindable> SelectedMods = new Bindable>(); + + public readonly Bindable Ruleset = new Bindable(); + + private void rulesetChanged(RulesetInfo newRuleset) + { + var instance = newRuleset.CreateInstance(); + + foreach (ModSection section in ModSectionsContainer.Children) + section.Mods = instance.GetModsFor(section.ModType); + refreshSelectedMods(); + } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets, AudioManager audio) + { + SelectedMods.ValueChanged += selectedModsChanged; + + LowMultiplierColour = colours.Red; + HighMultiplierColour = colours.Green; + + if (osu != null) + Ruleset.BindTo(osu.Ruleset); + else + Ruleset.Value = rulesets.AvailableRulesets.First(); + + Ruleset.ValueChanged += rulesetChanged; + Ruleset.TriggerChange(); + + sampleOn = audio.Sample.Get(@"UI/check-on"); + sampleOff = audio.Sample.Get(@"UI/check-off"); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + Ruleset.UnbindAll(); + SelectedMods.UnbindAll(); + } + + private void selectedModsChanged(IEnumerable obj) + { + foreach (ModSection section in ModSectionsContainer.Children) + section.SelectTypes(obj.Select(m => m.GetType()).ToList()); + + updateMods(); + } + + private void updateMods() + { + double multiplier = 1.0; + bool ranked = true; + + foreach (Mod mod in SelectedMods.Value) + { + multiplier *= mod.ScoreMultiplier; + ranked &= mod.Ranked; + } + + MultiplierLabel.Text = $"{multiplier:N2}x"; + if (!ranked) + MultiplierLabel.Text += " (Unranked)"; + + if (multiplier > 1.0) + MultiplierLabel.FadeColour(HighMultiplierColour, 200); + else if (multiplier < 1.0) + MultiplierLabel.FadeColour(LowMultiplierColour, 200); + else + MultiplierLabel.FadeColour(Color4.White, 200); + } + + protected override void PopOut() + { + base.PopOut(); + + footerContainer.MoveToX(footerContainer.DrawSize.X, DISAPPEAR_DURATION, Easing.InSine); + footerContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine); + + foreach (ModSection section in ModSectionsContainer.Children) + { + section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.MoveToX(100f, DISAPPEAR_DURATION, Easing.InSine); + section.ButtonsContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine); + } + } + + protected override void PopIn() + { + base.PopIn(); + + footerContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint); + footerContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint); + + foreach (ModSection section in ModSectionsContainer.Children) + { + section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint); + section.ButtonsContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint); + } + } + + public void DeselectAll() + { + foreach (ModSection section in ModSectionsContainer.Children) + section.DeselectAll(); + + refreshSelectedMods(); + } + + /// + /// Deselect one or more mods. + /// + /// The types of s which should be deselected. + /// Set to true to bypass animations and update selections immediately. + public void DeselectTypes(Type[] modTypes, bool immediate = false) + { + if (modTypes.Length == 0) return; + foreach (ModSection section in ModSectionsContainer.Children) + section.DeselectTypes(modTypes, immediate); + } + + + private SampleChannel sampleOn, sampleOff; + + private void modButtonPressed(Mod selectedMod) + { + if (selectedMod != null) + { + if (State == Visibility.Visible) sampleOn?.Play(); + DeselectTypes(selectedMod.IncompatibleMods, true); + } + else + { + if (State == Visibility.Visible) sampleOff?.Play(); + } + + refreshSelectedMods(); + } + + private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); + + public ModSelectOverlay() + { + FirstWaveColour = OsuColour.FromHex(@"19b0e2"); + SecondWaveColour = OsuColour.FromHex(@"2280a2"); + ThirdWaveColour = OsuColour.FromHex(@"005774"); + FourthWaveColour = OsuColour.FromHex(@"003a4e"); + + Height = 510; + Content.RelativeSizeAxes = Axes.X; + Content.AutoSizeAxes = Axes.Y; + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(36, 50, 68, 255) + }, + new Triangles + { + TriangleScale = 5, + RelativeSizeAxes = Axes.X, + Height = Height, //set the height from the start to ensure correct triangle density. + ColourLight = new Color4(53, 66, 82, 255), + ColourDark = new Color4(41, 54, 70, 255), + }, + }, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + // Header + new Container + { + RelativeSizeAxes = Axes.X, + Height = 82, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(10).Opacity(100), + }, + new FillFlowContainer + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Width = content_width, + Padding = new MarginPadding + { + Top = 10, + Bottom = 10, + }, + Children = new Drawable[] + { + new OsuSpriteText + { + Font = @"Exo2.0-Bold", + Text = @"Gameplay Mods", + TextSize = 22, + Shadow = true, + Margin = new MarginPadding + { + Bottom = 4, + }, + }, + new OsuSpriteText + { + Text = @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.", + TextSize = 18, + Shadow = true, + }, + new OsuSpriteText + { + Text = @"Others are just for fun.", + TextSize = 18, + Shadow = true, + }, + }, + }, + }, + }, + // Body + ModSectionsContainer = new FillFlowContainer + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, 10f), + Width = content_width, + Children = new ModSection[] + { + new DifficultyReductionSection + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Action = modButtonPressed, + }, + new DifficultyIncreaseSection + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Action = modButtonPressed, + }, + new SpecialSection + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Action = modButtonPressed, + }, + } + }, + // Footer + new Container + { + RelativeSizeAxes = Axes.X, + Height = 70, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(172, 20, 116, 255), + Alpha = 0.5f, + }, + footerContainer = new FillFlowContainer + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Width = content_width, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding + { + Vertical = 15 + }, + Children = new Drawable[] + { + DeselectAllButton = new TriangleButton + { + Width = 180, + Text = "Deselect All", + Action = DeselectAll, + Margin = new MarginPadding + { + Right = 20 + } + }, + new OsuSpriteText + { + Text = @"Score Multiplier: ", + TextSize = 30, + Shadow = true, + Margin = new MarginPadding + { + Top = 5 + } + }, + MultiplierLabel = new OsuSpriteText + { + Font = @"Exo2.0-Bold", + TextSize = 30, + Shadow = true, + Margin = new MarginPadding + { + Top = 5 + } + } + } + } + }, + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Mods/SpecialSection.cs b/osu.Game/Overlays/Mods/SpecialSection.cs index 75b2462ff5..b3540cf915 100644 --- a/osu.Game/Overlays/Mods/SpecialSection.cs +++ b/osu.Game/Overlays/Mods/SpecialSection.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Input; -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Overlays.Mods -{ - public class SpecialSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; - public override ModType ModType => ModType.Special; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - SelectedColour = colours.BlueLight; - } - - public SpecialSection() - { - Header = @"Special"; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Overlays.Mods +{ + public class SpecialSection : ModSection + { + protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; + public override ModType ModType => ModType.Special; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + SelectedColour = colours.BlueLight; + } + + public SpecialSection() + { + Header = @"Special"; + } + } +} diff --git a/osu.Game/Overlays/Music/CollectionsDropdown.cs b/osu.Game/Overlays/Music/CollectionsDropdown.cs index 648a00993d..e06688e8f0 100644 --- a/osu.Game/Overlays/Music/CollectionsDropdown.cs +++ b/osu.Game/Overlays/Music/CollectionsDropdown.cs @@ -1,74 +1,74 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Music -{ - public class CollectionsDropdown : OsuDropdown - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Gray6; - } - - protected override DropdownHeader CreateHeader() => new CollectionsHeader(); - - protected override DropdownMenu CreateMenu() => new CollectionsMenu(); - - private class CollectionsMenu : OsuDropdownMenu - { - public CollectionsMenu() - { - CornerRadius = 5; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.3f), - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray4; - } - } - - private class CollectionsHeader : OsuDropdownHeader - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray4; - } - - public CollectionsHeader() - { - CornerRadius = 5; - Height = 30; - Icon.Size = new Vector2(14); - Icon.Margin = new MarginPadding(0); - Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 }; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.3f), - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Music +{ + public class CollectionsDropdown : OsuDropdown + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Gray6; + } + + protected override DropdownHeader CreateHeader() => new CollectionsHeader(); + + protected override DropdownMenu CreateMenu() => new CollectionsMenu(); + + private class CollectionsMenu : OsuDropdownMenu + { + public CollectionsMenu() + { + CornerRadius = 5; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.3f), + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray4; + } + } + + private class CollectionsHeader : OsuDropdownHeader + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray4; + } + + public CollectionsHeader() + { + CornerRadius = 5; + Height = 30; + Icon.Size = new Vector2(14); + Icon.Margin = new MarginPadding(0); + Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 }; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.3f), + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + } + } + } +} diff --git a/osu.Game/Overlays/Music/FilterControl.cs b/osu.Game/Overlays/Music/FilterControl.cs index fa437fc2e8..2d492dcf1e 100644 --- a/osu.Game/Overlays/Music/FilterControl.cs +++ b/osu.Game/Overlays/Music/FilterControl.cs @@ -1,76 +1,76 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using OpenTK; -using OpenTK.Graphics; -using System; - -namespace osu.Game.Overlays.Music -{ - public class FilterControl : Container - { - public readonly FilterTextBox Search; - - public FilterControl() - { - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, 10f), - Children = new Drawable[] - { - Search = new FilterTextBox - { - RelativeSizeAxes = Axes.X, - Height = 40, - Exit = () => ExitRequested?.Invoke(), - }, - new CollectionsDropdown - { - RelativeSizeAxes = Axes.X, - Items = new[] { new KeyValuePair(@"All", PlaylistCollection.All) }, - } - }, - }, - }; - - Search.Current.ValueChanged += current_ValueChanged; - } - - private void current_ValueChanged(string newValue) => FilterChanged?.Invoke(newValue); - - public Action ExitRequested; - - public Action FilterChanged; - - public class FilterTextBox : SearchTextBox - { - private Color4 backgroundColour; - - protected override Color4 BackgroundUnfocused => backgroundColour; - protected override Color4 BackgroundFocused => backgroundColour; - protected override bool AllowCommit => true; - - public FilterTextBox() - { - Masking = true; - CornerRadius = 5; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - backgroundColour = colours.Gray2; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using OpenTK; +using OpenTK.Graphics; +using System; + +namespace osu.Game.Overlays.Music +{ + public class FilterControl : Container + { + public readonly FilterTextBox Search; + + public FilterControl() + { + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + Search = new FilterTextBox + { + RelativeSizeAxes = Axes.X, + Height = 40, + Exit = () => ExitRequested?.Invoke(), + }, + new CollectionsDropdown + { + RelativeSizeAxes = Axes.X, + Items = new[] { new KeyValuePair(@"All", PlaylistCollection.All) }, + } + }, + }, + }; + + Search.Current.ValueChanged += current_ValueChanged; + } + + private void current_ValueChanged(string newValue) => FilterChanged?.Invoke(newValue); + + public Action ExitRequested; + + public Action FilterChanged; + + public class FilterTextBox : SearchTextBox + { + private Color4 backgroundColour; + + protected override Color4 BackgroundUnfocused => backgroundColour; + protected override Color4 BackgroundFocused => backgroundColour; + protected override bool AllowCommit => true; + + public FilterTextBox() + { + Masking = true; + CornerRadius = 5; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + backgroundColour = colours.Gray2; + } + } + } +} diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 71fdcff6af..a630a86fce 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -1,182 +1,182 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Framework.Localisation; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Overlays.Music -{ - public class PlaylistItem : Container, IFilterable, IDraggable - { - private const float fade_duration = 100; - - private Color4 hoverColour; - private Color4 artistColour; - - private SpriteIcon handle; - private TextFlowContainer text; - private IEnumerable titleSprites; - private UnicodeBindableString titleBind; - private UnicodeBindableString artistBind; - - public readonly BeatmapSetInfo BeatmapSetInfo; - - public Action OnSelect; - - public bool IsDraggable { get; private set; } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - IsDraggable = handle.IsHovered; - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - IsDraggable = false; - return base.OnMouseUp(state, args); - } - - private bool selected; - public bool Selected - { - get { return selected; } - set - { - if (value == selected) return; - selected = value; - - FinishTransforms(true); - foreach (SpriteText s in titleSprites) - s.FadeColour(Selected ? hoverColour : Color4.White, fade_duration); - } - } - - public PlaylistItem(BeatmapSetInfo setInfo) - { - BeatmapSetInfo = setInfo; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Top = 3, Bottom = 3 }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, LocalisationEngine localisation) - { - hoverColour = colours.Yellow; - artistColour = colours.Gray9; - - var metadata = BeatmapSetInfo.Metadata; - FilterTerms = metadata.SearchableTerms; - - Children = new Drawable[] - { - handle = new PlaylistItemHandle - { - Colour = colours.Gray5 - }, - text = new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = 20 }, - ContentIndent = 10f, - }, - }; - - titleBind = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); - artistBind = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); - - artistBind.ValueChanged += newText => recreateText(); - artistBind.TriggerChange(); - } - - private void recreateText() - { - text.Clear(); - - //space after the title to put a space between the title and artist - titleSprites = text.AddText(titleBind.Value + @" ", sprite => - { - sprite.TextSize = 16; - sprite.Font = @"Exo2.0-Regular"; - }); - - text.AddText(artistBind.Value, sprite => - { - sprite.TextSize = 14; - sprite.Font = @"Exo2.0-Bold"; - sprite.Colour = artistColour; - sprite.Padding = new MarginPadding { Top = 1 }; - }); - } - - protected override bool OnHover(InputState state) - { - handle.FadeIn(fade_duration); - - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - handle.FadeOut(fade_duration); - } - - protected override bool OnClick(InputState state) - { - OnSelect?.Invoke(BeatmapSetInfo); - return true; - } - - public IEnumerable FilterTerms { get; private set; } - - private bool matching = true; - - public bool MatchingFilter - { - get { return matching; } - set - { - if (matching == value) return; - - matching = value; - - this.FadeTo(matching ? 1 : 0, 200); - } - } - - private class PlaylistItemHandle : SpriteIcon - { - public PlaylistItemHandle() - { - Anchor = Anchor.TopLeft; - Origin = Anchor.TopLeft; - Size = new Vector2(12); - Icon = FontAwesome.fa_bars; - Alpha = 0f; - Margin = new MarginPadding { Left = 5, Top = 2 }; - } - } - } - - public interface IDraggable : IDrawable - { - /// - /// Whether this can be dragged in its current state. - /// - bool IsDraggable { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Overlays.Music +{ + public class PlaylistItem : Container, IFilterable, IDraggable + { + private const float fade_duration = 100; + + private Color4 hoverColour; + private Color4 artistColour; + + private SpriteIcon handle; + private TextFlowContainer text; + private IEnumerable titleSprites; + private UnicodeBindableString titleBind; + private UnicodeBindableString artistBind; + + public readonly BeatmapSetInfo BeatmapSetInfo; + + public Action OnSelect; + + public bool IsDraggable { get; private set; } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + IsDraggable = handle.IsHovered; + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + IsDraggable = false; + return base.OnMouseUp(state, args); + } + + private bool selected; + public bool Selected + { + get { return selected; } + set + { + if (value == selected) return; + selected = value; + + FinishTransforms(true); + foreach (SpriteText s in titleSprites) + s.FadeColour(Selected ? hoverColour : Color4.White, fade_duration); + } + } + + public PlaylistItem(BeatmapSetInfo setInfo) + { + BeatmapSetInfo = setInfo; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Top = 3, Bottom = 3 }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, LocalisationEngine localisation) + { + hoverColour = colours.Yellow; + artistColour = colours.Gray9; + + var metadata = BeatmapSetInfo.Metadata; + FilterTerms = metadata.SearchableTerms; + + Children = new Drawable[] + { + handle = new PlaylistItemHandle + { + Colour = colours.Gray5 + }, + text = new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = 20 }, + ContentIndent = 10f, + }, + }; + + titleBind = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); + artistBind = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); + + artistBind.ValueChanged += newText => recreateText(); + artistBind.TriggerChange(); + } + + private void recreateText() + { + text.Clear(); + + //space after the title to put a space between the title and artist + titleSprites = text.AddText(titleBind.Value + @" ", sprite => + { + sprite.TextSize = 16; + sprite.Font = @"Exo2.0-Regular"; + }); + + text.AddText(artistBind.Value, sprite => + { + sprite.TextSize = 14; + sprite.Font = @"Exo2.0-Bold"; + sprite.Colour = artistColour; + sprite.Padding = new MarginPadding { Top = 1 }; + }); + } + + protected override bool OnHover(InputState state) + { + handle.FadeIn(fade_duration); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + handle.FadeOut(fade_duration); + } + + protected override bool OnClick(InputState state) + { + OnSelect?.Invoke(BeatmapSetInfo); + return true; + } + + public IEnumerable FilterTerms { get; private set; } + + private bool matching = true; + + public bool MatchingFilter + { + get { return matching; } + set + { + if (matching == value) return; + + matching = value; + + this.FadeTo(matching ? 1 : 0, 200); + } + } + + private class PlaylistItemHandle : SpriteIcon + { + public PlaylistItemHandle() + { + Anchor = Anchor.TopLeft; + Origin = Anchor.TopLeft; + Size = new Vector2(12); + Icon = FontAwesome.fa_bars; + Alpha = 0f; + Margin = new MarginPadding { Left = 5, Top = 2 }; + } + } + } + + public interface IDraggable : IDrawable + { + /// + /// Whether this can be dragged in its current state. + /// + bool IsDraggable { get; } + } +} diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 03ce7fd88f..d63babf3b6 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -1,255 +1,255 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Overlays.Music -{ - public class PlaylistList : CompositeDrawable - { - public Action OnSelect; - - private readonly ItemsScrollContainer items; - - public PlaylistList() - { - InternalChild = items = new ItemsScrollContainer - { - RelativeSizeAxes = Axes.Both, - OnSelect = set => OnSelect?.Invoke(set) - }; - } - - public new MarginPadding Padding - { - get { return base.Padding; } - set { base.Padding = value; } - } - - public IEnumerable BeatmapSets - { - get { return items.Sets; } - set { items.Sets = value; } - } - - public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet; - public BeatmapSetInfo NextSet => items.NextSet; - public BeatmapSetInfo PreviousSet => items.PreviousSet; - - public BeatmapSetInfo SelectedSet - { - get { return items.SelectedSet; } - set { items.SelectedSet = value; } - } - - public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet); - public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet); - - public void Filter(string searchTerm) => items.SearchTerm = searchTerm; - - private class ItemsScrollContainer : OsuScrollContainer - { - public Action OnSelect; - - private readonly SearchContainer search; - private readonly FillFlowContainer items; - - public ItemsScrollContainer() - { - Children = new Drawable[] - { - search = new SearchContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - items = new ItemSearchContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - } - } - }; - } - - public IEnumerable Sets - { - get { return items.Select(x => x.BeatmapSetInfo).ToList(); } - set - { - items.Clear(); - value.ForEach(AddBeatmapSet); - } - } - - public string SearchTerm - { - get { return search.SearchTerm; } - set { search.SearchTerm = value; } - } - - public void AddBeatmapSet(BeatmapSetInfo beatmapSet) - { - var newItem = new PlaylistItem(beatmapSet) { OnSelect = set => OnSelect?.Invoke(set) }; - - items.Add(newItem); - items.SetLayoutPosition(newItem, items.Count); - } - - public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) - { - var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID); - if (itemToRemove != null) - items.Remove(itemToRemove); - } - - public BeatmapSetInfo SelectedSet - { - get { return items.FirstOrDefault(i => i.Selected)?.BeatmapSetInfo; } - set - { - foreach (PlaylistItem s in items.Children) - s.Selected = s.BeatmapSetInfo.ID == value?.ID; - } - } - - public BeatmapSetInfo FirstVisibleSet => items.FirstOrDefault(i => i.MatchingFilter)?.BeatmapSetInfo; - public BeatmapSetInfo NextSet => (items.SkipWhile(i => !i.Selected).Skip(1).FirstOrDefault() ?? items.FirstOrDefault())?.BeatmapSetInfo; - public BeatmapSetInfo PreviousSet => (items.TakeWhile(i => !i.Selected).LastOrDefault() ?? items.LastOrDefault())?.BeatmapSetInfo; - - private Vector2 nativeDragPosition; - private PlaylistItem draggedItem; - - protected override bool OnDragStart(InputState state) - { - nativeDragPosition = state.Mouse.NativeState.Position; - draggedItem = items.FirstOrDefault(d => d.IsDraggable); - return draggedItem != null || base.OnDragStart(state); - } - - protected override bool OnDrag(InputState state) - { - nativeDragPosition = state.Mouse.NativeState.Position; - if (draggedItem == null) - return base.OnDrag(state); - return true; - } - - protected override bool OnDragEnd(InputState state) - { - nativeDragPosition = state.Mouse.NativeState.Position; - var handled = draggedItem != null || base.OnDragEnd(state); - draggedItem = null; - - return handled; - } - - protected override void Update() - { - base.Update(); - - if (draggedItem == null) - return; - - updateScrollPosition(); - updateDragPosition(); - } - - private void updateScrollPosition() - { - const float start_offset = 10; - const double max_power = 50; - const double exp_base = 1.05; - - var localPos = ToLocalSpace(nativeDragPosition); - - if (localPos.Y < start_offset) - { - if (Current <= 0) - return; - - var power = Math.Min(max_power, Math.Abs(start_offset - localPos.Y)); - ScrollBy(-(float)Math.Pow(exp_base, power)); - } - else if (localPos.Y > DrawHeight - start_offset) - { - if (IsScrolledToEnd()) - return; - - var power = Math.Min(max_power, Math.Abs(DrawHeight - start_offset - localPos.Y)); - ScrollBy((float)Math.Pow(exp_base, power)); - } - } - - private void updateDragPosition() - { - var itemsPos = items.ToLocalSpace(nativeDragPosition); - - int srcIndex = (int)items.GetLayoutPosition(draggedItem); - - // Find the last item with position < mouse position. Note we can't directly use - // the item positions as they are being transformed - float heightAccumulator = 0; - int dstIndex = 0; - for (; dstIndex < items.Count; dstIndex++) - { - // Using BoundingBox here takes care of scale, paddings, etc... - heightAccumulator += items[dstIndex].BoundingBox.Height; - if (heightAccumulator > itemsPos.Y) - break; - } - - dstIndex = MathHelper.Clamp(dstIndex, 0, items.Count - 1); - - if (srcIndex == dstIndex) - return; - - if (srcIndex < dstIndex) - { - for (int i = srcIndex + 1; i <= dstIndex; i++) - items.SetLayoutPosition(items[i], i - 1); - } - else - { - for (int i = dstIndex; i < srcIndex; i++) - items.SetLayoutPosition(items[i], i + 1); - } - - items.SetLayoutPosition(draggedItem, dstIndex); - } - - private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren - { - public IEnumerable FilterTerms => new string[] { }; - - public bool MatchingFilter - { - set - { - if (value) - InvalidateLayout(); - } - } - - public IEnumerable FilterableChildren => Children; - - public ItemSearchContainer() - { - LayoutDuration = 200; - LayoutEasing = Easing.OutQuint; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Overlays.Music +{ + public class PlaylistList : CompositeDrawable + { + public Action OnSelect; + + private readonly ItemsScrollContainer items; + + public PlaylistList() + { + InternalChild = items = new ItemsScrollContainer + { + RelativeSizeAxes = Axes.Both, + OnSelect = set => OnSelect?.Invoke(set) + }; + } + + public new MarginPadding Padding + { + get { return base.Padding; } + set { base.Padding = value; } + } + + public IEnumerable BeatmapSets + { + get { return items.Sets; } + set { items.Sets = value; } + } + + public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet; + public BeatmapSetInfo NextSet => items.NextSet; + public BeatmapSetInfo PreviousSet => items.PreviousSet; + + public BeatmapSetInfo SelectedSet + { + get { return items.SelectedSet; } + set { items.SelectedSet = value; } + } + + public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet); + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet); + + public void Filter(string searchTerm) => items.SearchTerm = searchTerm; + + private class ItemsScrollContainer : OsuScrollContainer + { + public Action OnSelect; + + private readonly SearchContainer search; + private readonly FillFlowContainer items; + + public ItemsScrollContainer() + { + Children = new Drawable[] + { + search = new SearchContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + items = new ItemSearchContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + } + } + }; + } + + public IEnumerable Sets + { + get { return items.Select(x => x.BeatmapSetInfo).ToList(); } + set + { + items.Clear(); + value.ForEach(AddBeatmapSet); + } + } + + public string SearchTerm + { + get { return search.SearchTerm; } + set { search.SearchTerm = value; } + } + + public void AddBeatmapSet(BeatmapSetInfo beatmapSet) + { + var newItem = new PlaylistItem(beatmapSet) { OnSelect = set => OnSelect?.Invoke(set) }; + + items.Add(newItem); + items.SetLayoutPosition(newItem, items.Count); + } + + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) + { + var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID); + if (itemToRemove != null) + items.Remove(itemToRemove); + } + + public BeatmapSetInfo SelectedSet + { + get { return items.FirstOrDefault(i => i.Selected)?.BeatmapSetInfo; } + set + { + foreach (PlaylistItem s in items.Children) + s.Selected = s.BeatmapSetInfo.ID == value?.ID; + } + } + + public BeatmapSetInfo FirstVisibleSet => items.FirstOrDefault(i => i.MatchingFilter)?.BeatmapSetInfo; + public BeatmapSetInfo NextSet => (items.SkipWhile(i => !i.Selected).Skip(1).FirstOrDefault() ?? items.FirstOrDefault())?.BeatmapSetInfo; + public BeatmapSetInfo PreviousSet => (items.TakeWhile(i => !i.Selected).LastOrDefault() ?? items.LastOrDefault())?.BeatmapSetInfo; + + private Vector2 nativeDragPosition; + private PlaylistItem draggedItem; + + protected override bool OnDragStart(InputState state) + { + nativeDragPosition = state.Mouse.NativeState.Position; + draggedItem = items.FirstOrDefault(d => d.IsDraggable); + return draggedItem != null || base.OnDragStart(state); + } + + protected override bool OnDrag(InputState state) + { + nativeDragPosition = state.Mouse.NativeState.Position; + if (draggedItem == null) + return base.OnDrag(state); + return true; + } + + protected override bool OnDragEnd(InputState state) + { + nativeDragPosition = state.Mouse.NativeState.Position; + var handled = draggedItem != null || base.OnDragEnd(state); + draggedItem = null; + + return handled; + } + + protected override void Update() + { + base.Update(); + + if (draggedItem == null) + return; + + updateScrollPosition(); + updateDragPosition(); + } + + private void updateScrollPosition() + { + const float start_offset = 10; + const double max_power = 50; + const double exp_base = 1.05; + + var localPos = ToLocalSpace(nativeDragPosition); + + if (localPos.Y < start_offset) + { + if (Current <= 0) + return; + + var power = Math.Min(max_power, Math.Abs(start_offset - localPos.Y)); + ScrollBy(-(float)Math.Pow(exp_base, power)); + } + else if (localPos.Y > DrawHeight - start_offset) + { + if (IsScrolledToEnd()) + return; + + var power = Math.Min(max_power, Math.Abs(DrawHeight - start_offset - localPos.Y)); + ScrollBy((float)Math.Pow(exp_base, power)); + } + } + + private void updateDragPosition() + { + var itemsPos = items.ToLocalSpace(nativeDragPosition); + + int srcIndex = (int)items.GetLayoutPosition(draggedItem); + + // Find the last item with position < mouse position. Note we can't directly use + // the item positions as they are being transformed + float heightAccumulator = 0; + int dstIndex = 0; + for (; dstIndex < items.Count; dstIndex++) + { + // Using BoundingBox here takes care of scale, paddings, etc... + heightAccumulator += items[dstIndex].BoundingBox.Height; + if (heightAccumulator > itemsPos.Y) + break; + } + + dstIndex = MathHelper.Clamp(dstIndex, 0, items.Count - 1); + + if (srcIndex == dstIndex) + return; + + if (srcIndex < dstIndex) + { + for (int i = srcIndex + 1; i <= dstIndex; i++) + items.SetLayoutPosition(items[i], i - 1); + } + else + { + for (int i = dstIndex; i < srcIndex; i++) + items.SetLayoutPosition(items[i], i + 1); + } + + items.SetLayoutPosition(draggedItem, dstIndex); + } + + private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren + { + public IEnumerable FilterTerms => new string[] { }; + + public bool MatchingFilter + { + set + { + if (value) + InvalidateLayout(); + } + } + + public IEnumerable FilterableChildren => Children; + + public ItemSearchContainer() + { + LayoutDuration = 200; + LayoutEasing = Easing.OutQuint; + } + } + } + } +} diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index c981e5f493..3496c044fb 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -1,177 +1,177 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Music -{ - public class PlaylistOverlay : OverlayContainer - { - private const float transition_duration = 600; - - private const float playlist_height = 510; - - private FilterControl filter; - private PlaylistList list; - - private BeatmapManager beatmaps; - - private readonly Bindable beatmapBacking = new Bindable(); - - public IEnumerable BeatmapSets => list.BeatmapSets; - - [BackgroundDependencyLoader] - private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours) - { - this.beatmaps = beatmaps; - - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - CornerRadius = 5, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(40), - Radius = 5, - }, - Children = new Drawable[] - { - new Box - { - Colour = colours.Gray3, - RelativeSizeAxes = Axes.Both, - }, - list = new PlaylistList - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 95, Bottom = 10, Right = 10 }, - OnSelect = itemSelected, - }, - filter = new FilterControl - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ExitRequested = () => State = Visibility.Hidden, - FilterChanged = search => list.Filter(search), - Padding = new MarginPadding(10), - }, - }, - }, - }; - - beatmaps.ItemAdded += handleBeatmapAdded; - beatmaps.ItemRemoved += handleBeatmapRemoved; - - list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); - - beatmapBacking.BindTo(game.Beatmap); - - filter.Search.OnCommit = (sender, newText) => - { - var beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault(); - if (beatmap != null) playSpecified(beatmap); - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - beatmapBacking.ValueChanged += b => list.SelectedSet = b?.BeatmapSetInfo; - beatmapBacking.TriggerChange(); - } - - private void handleBeatmapAdded(BeatmapSetInfo setInfo) => Schedule(() => list.AddBeatmapSet(setInfo)); - private void handleBeatmapRemoved(BeatmapSetInfo setInfo) => Schedule(() => list.RemoveBeatmapSet(setInfo)); - - protected override void PopIn() - { - filter.Search.HoldFocus = true; - Schedule(() => GetContainingInputManager().ChangeFocus(filter.Search)); - - this.ResizeTo(new Vector2(1, playlist_height), transition_duration, Easing.OutQuint); - this.FadeIn(transition_duration, Easing.OutQuint); - } - - protected override void PopOut() - { - filter.Search.HoldFocus = false; - - this.ResizeTo(new Vector2(1, 0), transition_duration, Easing.OutQuint); - this.FadeOut(transition_duration); - } - - private void itemSelected(BeatmapSetInfo set) - { - if (set.ID == (beatmapBacking.Value?.BeatmapSetInfo?.ID ?? -1)) - { - beatmapBacking.Value?.Track?.Seek(0); - return; - } - - playSpecified(set.Beatmaps.First()); - } - - public void PlayPrevious() - { - var playable = list.PreviousSet; - - if (playable != null) - { - playSpecified(playable.Beatmaps.First()); - list.SelectedSet = playable; - } - } - - public void PlayNext() - { - var playable = list.NextSet; - - if (playable != null) - { - playSpecified(playable.Beatmaps.First()); - list.SelectedSet = playable; - } - } - - private void playSpecified(BeatmapInfo info) - { - beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - - var track = beatmapBacking.Value.Track; - - track.Restart(); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (beatmaps != null) - { - beatmaps.ItemAdded -= handleBeatmapAdded; - beatmaps.ItemRemoved -= handleBeatmapRemoved; - } - } - } - - //todo: placeholder - public enum PlaylistCollection - { - All - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Music +{ + public class PlaylistOverlay : OverlayContainer + { + private const float transition_duration = 600; + + private const float playlist_height = 510; + + private FilterControl filter; + private PlaylistList list; + + private BeatmapManager beatmaps; + + private readonly Bindable beatmapBacking = new Bindable(); + + public IEnumerable BeatmapSets => list.BeatmapSets; + + [BackgroundDependencyLoader] + private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours) + { + this.beatmaps = beatmaps; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + CornerRadius = 5, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(40), + Radius = 5, + }, + Children = new Drawable[] + { + new Box + { + Colour = colours.Gray3, + RelativeSizeAxes = Axes.Both, + }, + list = new PlaylistList + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 95, Bottom = 10, Right = 10 }, + OnSelect = itemSelected, + }, + filter = new FilterControl + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ExitRequested = () => State = Visibility.Hidden, + FilterChanged = search => list.Filter(search), + Padding = new MarginPadding(10), + }, + }, + }, + }; + + beatmaps.ItemAdded += handleBeatmapAdded; + beatmaps.ItemRemoved += handleBeatmapRemoved; + + list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); + + beatmapBacking.BindTo(game.Beatmap); + + filter.Search.OnCommit = (sender, newText) => + { + var beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault(); + if (beatmap != null) playSpecified(beatmap); + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + beatmapBacking.ValueChanged += b => list.SelectedSet = b?.BeatmapSetInfo; + beatmapBacking.TriggerChange(); + } + + private void handleBeatmapAdded(BeatmapSetInfo setInfo) => Schedule(() => list.AddBeatmapSet(setInfo)); + private void handleBeatmapRemoved(BeatmapSetInfo setInfo) => Schedule(() => list.RemoveBeatmapSet(setInfo)); + + protected override void PopIn() + { + filter.Search.HoldFocus = true; + Schedule(() => GetContainingInputManager().ChangeFocus(filter.Search)); + + this.ResizeTo(new Vector2(1, playlist_height), transition_duration, Easing.OutQuint); + this.FadeIn(transition_duration, Easing.OutQuint); + } + + protected override void PopOut() + { + filter.Search.HoldFocus = false; + + this.ResizeTo(new Vector2(1, 0), transition_duration, Easing.OutQuint); + this.FadeOut(transition_duration); + } + + private void itemSelected(BeatmapSetInfo set) + { + if (set.ID == (beatmapBacking.Value?.BeatmapSetInfo?.ID ?? -1)) + { + beatmapBacking.Value?.Track?.Seek(0); + return; + } + + playSpecified(set.Beatmaps.First()); + } + + public void PlayPrevious() + { + var playable = list.PreviousSet; + + if (playable != null) + { + playSpecified(playable.Beatmaps.First()); + list.SelectedSet = playable; + } + } + + public void PlayNext() + { + var playable = list.NextSet; + + if (playable != null) + { + playSpecified(playable.Beatmaps.First()); + list.SelectedSet = playable; + } + } + + private void playSpecified(BeatmapInfo info) + { + beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); + + var track = beatmapBacking.Value.Track; + + track.Restart(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmaps != null) + { + beatmaps.ItemAdded -= handleBeatmapAdded; + beatmaps.ItemRemoved -= handleBeatmapRemoved; + } + } + } + + //todo: placeholder + public enum PlaylistCollection + { + All + } +} diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 74f6e4435d..b4021f2808 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -1,447 +1,447 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using System.Threading.Tasks; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input; -using osu.Framework.Localisation; -using osu.Framework.Threading; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Music; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays -{ - public class MusicController : OsuFocusedOverlayContainer - { - private const float player_height = 130; - - private const float transition_length = 800; - - private const float progress_height = 10; - - private const float bottom_black_area_height = 55; - - private Drawable background; - private ProgressBar progressBar; - - private IconButton prevButton; - private IconButton playButton; - private IconButton nextButton; - private IconButton playlistButton; - - private SpriteText title, artist; - - private PlaylistOverlay playlist; - - private LocalisationEngine localisation; - - private readonly Bindable beatmapBacking = new Bindable(); - - private Container dragContainer; - private Container playerContainer; - - public MusicController() - { - Width = 400; - Margin = new MarginPadding(10); - - // required to let MusicController handle beatmap cycling. - AlwaysPresent = true; - } - - private Vector2 dragStart; - - protected override bool OnDragStart(InputState state) - { - base.OnDragStart(state); - dragStart = state.Mouse.Position; - return true; - } - - protected override bool OnDrag(InputState state) - { - if (base.OnDrag(state)) return true; - - Vector2 change = state.Mouse.Position - dragStart; - - // Diminish the drag distance as we go further to simulate "rubber band" feeling. - change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length; - - dragContainer.MoveTo(change); - return true; - } - - protected override bool OnDragEnd(InputState state) - { - dragContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic); - return base.OnDragEnd(state); - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase game, OsuColour colours, LocalisationEngine localisation) - { - this.localisation = localisation; - - Children = new Drawable[] - { - dragContainer = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - playlist = new PlaylistOverlay - { - RelativeSizeAxes = Axes.X, - Y = player_height + 10, - }, - playerContainer = new Container - { - RelativeSizeAxes = Axes.X, - Height = player_height, - Masking = true, - CornerRadius = 5, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(40), - Radius = 5, - }, - Children = new[] - { - background = new Background(), - title = new OsuSpriteText - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.TopCentre, - Position = new Vector2(0, 40), - TextSize = 25, - Colour = Color4.White, - Text = @"Nothing to play", - Font = @"Exo2.0-MediumItalic" - }, - artist = new OsuSpriteText - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Position = new Vector2(0, 45), - TextSize = 15, - Colour = Color4.White, - Text = @"Nothing to play", - Font = @"Exo2.0-BoldItalic" - }, - new Container - { - Padding = new MarginPadding { Bottom = progress_height }, - Height = bottom_black_area_height, - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Children = new[] - { - prevButton = new IconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = prev, - Icon = FontAwesome.fa_step_backward, - }, - playButton = new IconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1.4f), - IconScale = new Vector2(1.4f), - Action = play, - Icon = FontAwesome.fa_play_circle_o, - }, - nextButton = new IconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = next, - Icon = FontAwesome.fa_step_forward, - }, - } - }, - playlistButton = new IconButton - { - Origin = Anchor.Centre, - Anchor = Anchor.CentreRight, - Position = new Vector2(-bottom_black_area_height / 2, 0), - Icon = FontAwesome.fa_bars, - Action = () => playlist.ToggleVisibility(), - }, - } - }, - progressBar = new ProgressBar - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Height = progress_height, - FillColour = colours.Yellow, - OnSeek = progress => current?.Track.Seek(progress) - } - }, - }, - } - } - }; - - beatmapBacking.BindTo(game.Beatmap); - - playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint); - } - - protected override void LoadComplete() - { - beatmapBacking.ValueChanged += beatmapChanged; - beatmapBacking.DisabledChanged += beatmapDisabledChanged; - beatmapBacking.TriggerChange(); - - base.LoadComplete(); - } - - private void beatmapDisabledChanged(bool disabled) - { - if (disabled) - playlist.Hide(); - - prevButton.Enabled.Value = !disabled; - nextButton.Enabled.Value = !disabled; - playlistButton.Enabled.Value = !disabled; - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - Height = dragContainer.Height; - } - - protected override void Update() - { - base.Update(); - - if (current?.TrackLoaded ?? false) - { - var track = current.Track; - - progressBar.EndTime = track.Length; - progressBar.CurrentTime = track.CurrentTime; - - playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; - - if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled && playlist.BeatmapSets.Any()) - next(); - } - else - playButton.Icon = FontAwesome.fa_play_circle_o; - } - - private void play() - { - var track = current?.Track; - - if (track == null) - { - if (!beatmapBacking.Disabled) - playlist.PlayNext(); - return; - } - - if (track.IsRunning) - track.Stop(); - else - track.Start(); - } - - private void prev() - { - queuedDirection = TransformDirection.Prev; - playlist.PlayPrevious(); - } - - private void next() - { - queuedDirection = TransformDirection.Next; - playlist.PlayNext(); - } - - private WorkingBeatmap current; - private TransformDirection? queuedDirection; - - private void beatmapChanged(WorkingBeatmap beatmap) - { - TransformDirection direction = TransformDirection.None; - - if (current != null) - { - bool audioEquals = beatmap?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false; - - if (audioEquals) - direction = TransformDirection.None; - else if (queuedDirection.HasValue) - { - direction = queuedDirection.Value; - queuedDirection = null; - } - else - { - //figure out the best direction based on order in playlist. - var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); - var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count(); - - direction = last > next ? TransformDirection.Prev : TransformDirection.Next; - } - } - - current = beatmap; - - progressBar.CurrentTime = 0; - - updateDisplay(current, direction); - - queuedDirection = null; - } - - private ScheduledDelegate pendingBeatmapSwitch; - - private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction) - { - //we might be off-screen when this update comes in. - //rather than Scheduling, manually handle this to avoid possible memory contention. - pendingBeatmapSwitch?.Cancel(); - - pendingBeatmapSwitch = Schedule(delegate - { - // todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync() - Task.Run(() => - { - if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists - { - title.Current = null; - title.Text = @"Nothing to play"; - - artist.Current = null; - artist.Text = @"Nothing to play"; - } - else - { - BeatmapMetadata metadata = beatmap.Metadata; - title.Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); - artist.Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); - } - }); - - LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground => - { - switch (direction) - { - case TransformDirection.Next: - newBackground.Position = new Vector2(400, 0); - newBackground.MoveToX(0, 500, Easing.OutCubic); - background.MoveToX(-400, 500, Easing.OutCubic); - break; - case TransformDirection.Prev: - newBackground.Position = new Vector2(-400, 0); - newBackground.MoveToX(0, 500, Easing.OutCubic); - background.MoveToX(400, 500, Easing.OutCubic); - break; - } - - background.Expire(); - background = newBackground; - - playerContainer.Add(newBackground); - }); - }); - } - - protected override void PopIn() - { - base.PopIn(); - - this.FadeIn(transition_length, Easing.OutQuint); - dragContainer.ScaleTo(1, transition_length, Easing.OutElastic); - } - - protected override void PopOut() - { - base.PopOut(); - - this.FadeOut(transition_length, Easing.OutQuint); - dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint); - } - - private enum TransformDirection - { - None, - Next, - Prev - } - - private class Background : BufferedContainer - { - private readonly Sprite sprite; - private readonly WorkingBeatmap beatmap; - - public Background(WorkingBeatmap beatmap = null) - { - this.beatmap = beatmap; - CacheDrawnFrameBuffer = true; - Depth = float.MaxValue; - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - sprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(150), - FillMode = FillMode.Fill, - }, - new Box - { - RelativeSizeAxes = Axes.X, - Height = bottom_black_area_height, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Colour = Color4.Black.Opacity(0.5f) - } - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4"); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input; +using osu.Framework.Localisation; +using osu.Framework.Threading; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Music; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays +{ + public class MusicController : OsuFocusedOverlayContainer + { + private const float player_height = 130; + + private const float transition_length = 800; + + private const float progress_height = 10; + + private const float bottom_black_area_height = 55; + + private Drawable background; + private ProgressBar progressBar; + + private IconButton prevButton; + private IconButton playButton; + private IconButton nextButton; + private IconButton playlistButton; + + private SpriteText title, artist; + + private PlaylistOverlay playlist; + + private LocalisationEngine localisation; + + private readonly Bindable beatmapBacking = new Bindable(); + + private Container dragContainer; + private Container playerContainer; + + public MusicController() + { + Width = 400; + Margin = new MarginPadding(10); + + // required to let MusicController handle beatmap cycling. + AlwaysPresent = true; + } + + private Vector2 dragStart; + + protected override bool OnDragStart(InputState state) + { + base.OnDragStart(state); + dragStart = state.Mouse.Position; + return true; + } + + protected override bool OnDrag(InputState state) + { + if (base.OnDrag(state)) return true; + + Vector2 change = state.Mouse.Position - dragStart; + + // Diminish the drag distance as we go further to simulate "rubber band" feeling. + change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length; + + dragContainer.MoveTo(change); + return true; + } + + protected override bool OnDragEnd(InputState state) + { + dragContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + return base.OnDragEnd(state); + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game, OsuColour colours, LocalisationEngine localisation) + { + this.localisation = localisation; + + Children = new Drawable[] + { + dragContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + playlist = new PlaylistOverlay + { + RelativeSizeAxes = Axes.X, + Y = player_height + 10, + }, + playerContainer = new Container + { + RelativeSizeAxes = Axes.X, + Height = player_height, + Masking = true, + CornerRadius = 5, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(40), + Radius = 5, + }, + Children = new[] + { + background = new Background(), + title = new OsuSpriteText + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.TopCentre, + Position = new Vector2(0, 40), + TextSize = 25, + Colour = Color4.White, + Text = @"Nothing to play", + Font = @"Exo2.0-MediumItalic" + }, + artist = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Position = new Vector2(0, 45), + TextSize = 15, + Colour = Color4.White, + Text = @"Nothing to play", + Font = @"Exo2.0-BoldItalic" + }, + new Container + { + Padding = new MarginPadding { Bottom = progress_height }, + Height = bottom_black_area_height, + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Children = new[] + { + prevButton = new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = prev, + Icon = FontAwesome.fa_step_backward, + }, + playButton = new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.4f), + IconScale = new Vector2(1.4f), + Action = play, + Icon = FontAwesome.fa_play_circle_o, + }, + nextButton = new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = next, + Icon = FontAwesome.fa_step_forward, + }, + } + }, + playlistButton = new IconButton + { + Origin = Anchor.Centre, + Anchor = Anchor.CentreRight, + Position = new Vector2(-bottom_black_area_height / 2, 0), + Icon = FontAwesome.fa_bars, + Action = () => playlist.ToggleVisibility(), + }, + } + }, + progressBar = new ProgressBar + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Height = progress_height, + FillColour = colours.Yellow, + OnSeek = progress => current?.Track.Seek(progress) + } + }, + }, + } + } + }; + + beatmapBacking.BindTo(game.Beatmap); + + playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint); + } + + protected override void LoadComplete() + { + beatmapBacking.ValueChanged += beatmapChanged; + beatmapBacking.DisabledChanged += beatmapDisabledChanged; + beatmapBacking.TriggerChange(); + + base.LoadComplete(); + } + + private void beatmapDisabledChanged(bool disabled) + { + if (disabled) + playlist.Hide(); + + prevButton.Enabled.Value = !disabled; + nextButton.Enabled.Value = !disabled; + playlistButton.Enabled.Value = !disabled; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + Height = dragContainer.Height; + } + + protected override void Update() + { + base.Update(); + + if (current?.TrackLoaded ?? false) + { + var track = current.Track; + + progressBar.EndTime = track.Length; + progressBar.CurrentTime = track.CurrentTime; + + playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; + + if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled && playlist.BeatmapSets.Any()) + next(); + } + else + playButton.Icon = FontAwesome.fa_play_circle_o; + } + + private void play() + { + var track = current?.Track; + + if (track == null) + { + if (!beatmapBacking.Disabled) + playlist.PlayNext(); + return; + } + + if (track.IsRunning) + track.Stop(); + else + track.Start(); + } + + private void prev() + { + queuedDirection = TransformDirection.Prev; + playlist.PlayPrevious(); + } + + private void next() + { + queuedDirection = TransformDirection.Next; + playlist.PlayNext(); + } + + private WorkingBeatmap current; + private TransformDirection? queuedDirection; + + private void beatmapChanged(WorkingBeatmap beatmap) + { + TransformDirection direction = TransformDirection.None; + + if (current != null) + { + bool audioEquals = beatmap?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false; + + if (audioEquals) + direction = TransformDirection.None; + else if (queuedDirection.HasValue) + { + direction = queuedDirection.Value; + queuedDirection = null; + } + else + { + //figure out the best direction based on order in playlist. + var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); + var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count(); + + direction = last > next ? TransformDirection.Prev : TransformDirection.Next; + } + } + + current = beatmap; + + progressBar.CurrentTime = 0; + + updateDisplay(current, direction); + + queuedDirection = null; + } + + private ScheduledDelegate pendingBeatmapSwitch; + + private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction) + { + //we might be off-screen when this update comes in. + //rather than Scheduling, manually handle this to avoid possible memory contention. + pendingBeatmapSwitch?.Cancel(); + + pendingBeatmapSwitch = Schedule(delegate + { + // todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync() + Task.Run(() => + { + if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists + { + title.Current = null; + title.Text = @"Nothing to play"; + + artist.Current = null; + artist.Text = @"Nothing to play"; + } + else + { + BeatmapMetadata metadata = beatmap.Metadata; + title.Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); + artist.Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); + } + }); + + LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground => + { + switch (direction) + { + case TransformDirection.Next: + newBackground.Position = new Vector2(400, 0); + newBackground.MoveToX(0, 500, Easing.OutCubic); + background.MoveToX(-400, 500, Easing.OutCubic); + break; + case TransformDirection.Prev: + newBackground.Position = new Vector2(-400, 0); + newBackground.MoveToX(0, 500, Easing.OutCubic); + background.MoveToX(400, 500, Easing.OutCubic); + break; + } + + background.Expire(); + background = newBackground; + + playerContainer.Add(newBackground); + }); + }); + } + + protected override void PopIn() + { + base.PopIn(); + + this.FadeIn(transition_length, Easing.OutQuint); + dragContainer.ScaleTo(1, transition_length, Easing.OutElastic); + } + + protected override void PopOut() + { + base.PopOut(); + + this.FadeOut(transition_length, Easing.OutQuint); + dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint); + } + + private enum TransformDirection + { + None, + Next, + Prev + } + + private class Background : BufferedContainer + { + private readonly Sprite sprite; + private readonly WorkingBeatmap beatmap; + + public Background(WorkingBeatmap beatmap = null) + { + this.beatmap = beatmap; + CacheDrawnFrameBuffer = true; + Depth = float.MaxValue; + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + sprite = new Sprite + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(150), + FillMode = FillMode.Fill, + }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = bottom_black_area_height, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Colour = Color4.Black.Opacity(0.5f) + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4"); + } + } + } +} diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index f5b281efc1..09b6022ac5 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -1,183 +1,183 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Overlays.Notifications; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; -using System; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Threading; - -namespace osu.Game.Overlays -{ - public class NotificationOverlay : OsuFocusedOverlayContainer - { - private const float width = 320; - - public const float TRANSITION_LENGTH = 600; - - /// - /// Whether posted notifications should be processed. - /// - public readonly BindableBool Enabled = new BindableBool(true); - - private FlowContainer sections; - - /// - /// Provide a source for the toolbar height. - /// - public Func GetToolbarHeight; - - public NotificationOverlay() - { - ScheduledDelegate notificationsEnabler = null; - Enabled.ValueChanged += v => - { - if (!IsLoaded) - { - processingPosts = v; - return; - } - - notificationsEnabler?.Cancel(); - - if (v) - // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. - notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000); - else - processingPosts = false; - }; - } - - [BackgroundDependencyLoader] - private void load() - { - Width = width; - RelativeSizeAxes = Axes.Y; - - AlwaysPresent = true; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.6f - }, - new OsuScrollContainer - { - Masking = true, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - sections = new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Children = new[] - { - new NotificationSection - { - Title = @"Notifications", - ClearText = @"Clear All", - AcceptTypes = new[] { typeof(SimpleNotification) } - }, - new NotificationSection - { - Title = @"Running Tasks", - ClearText = @"Cancel All", - AcceptTypes = new[] { typeof(ProgressNotification) } - } - } - } - } - } - }; - } - - private int totalCount => sections.Select(c => c.DisplayedCount).Sum(); - private int unreadCount => sections.Select(c => c.UnreadCount).Sum(); - - public readonly BindableInt UnreadCount = new BindableInt(); - - private int runningDepth; - - private void notificationClosed() => updateCounts(); - - private readonly Scheduler postScheduler = new Scheduler(); - - private bool processingPosts = true; - - public void Post(Notification notification) => postScheduler.Add(() => - { - ++runningDepth; - - notification.Closed += notificationClosed; - - var hasCompletionTarget = notification as IHasCompletionTarget; - if (hasCompletionTarget != null) - hasCompletionTarget.CompletionTarget = Post; - - var ourType = notification.GetType(); - - var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType))); - section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth); - - State = Visibility.Visible; - - updateCounts(); - }); - - protected override void Update() - { - base.Update(); - if (processingPosts) - postScheduler.Update(); - } - - protected override void PopIn() - { - base.PopIn(); - - this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); - this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); - } - - protected override void PopOut() - { - base.PopOut(); - - markAllRead(); - - this.MoveToX(width, TRANSITION_LENGTH, Easing.OutQuint); - this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); - } - - private void updateCounts() - { - UnreadCount.Value = unreadCount; - } - - private void markAllRead() - { - sections.Children.ForEach(s => s.MarkAllRead()); - - updateCounts(); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Notifications; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Threading; + +namespace osu.Game.Overlays +{ + public class NotificationOverlay : OsuFocusedOverlayContainer + { + private const float width = 320; + + public const float TRANSITION_LENGTH = 600; + + /// + /// Whether posted notifications should be processed. + /// + public readonly BindableBool Enabled = new BindableBool(true); + + private FlowContainer sections; + + /// + /// Provide a source for the toolbar height. + /// + public Func GetToolbarHeight; + + public NotificationOverlay() + { + ScheduledDelegate notificationsEnabler = null; + Enabled.ValueChanged += v => + { + if (!IsLoaded) + { + processingPosts = v; + return; + } + + notificationsEnabler?.Cancel(); + + if (v) + // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. + notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000); + else + processingPosts = false; + }; + } + + [BackgroundDependencyLoader] + private void load() + { + Width = width; + RelativeSizeAxes = Axes.Y; + + AlwaysPresent = true; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f + }, + new OsuScrollContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + sections = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Children = new[] + { + new NotificationSection + { + Title = @"Notifications", + ClearText = @"Clear All", + AcceptTypes = new[] { typeof(SimpleNotification) } + }, + new NotificationSection + { + Title = @"Running Tasks", + ClearText = @"Cancel All", + AcceptTypes = new[] { typeof(ProgressNotification) } + } + } + } + } + } + }; + } + + private int totalCount => sections.Select(c => c.DisplayedCount).Sum(); + private int unreadCount => sections.Select(c => c.UnreadCount).Sum(); + + public readonly BindableInt UnreadCount = new BindableInt(); + + private int runningDepth; + + private void notificationClosed() => updateCounts(); + + private readonly Scheduler postScheduler = new Scheduler(); + + private bool processingPosts = true; + + public void Post(Notification notification) => postScheduler.Add(() => + { + ++runningDepth; + + notification.Closed += notificationClosed; + + var hasCompletionTarget = notification as IHasCompletionTarget; + if (hasCompletionTarget != null) + hasCompletionTarget.CompletionTarget = Post; + + var ourType = notification.GetType(); + + var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType))); + section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth); + + State = Visibility.Visible; + + updateCounts(); + }); + + protected override void Update() + { + base.Update(); + if (processingPosts) + postScheduler.Update(); + } + + protected override void PopIn() + { + base.PopIn(); + + this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); + this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + + markAllRead(); + + this.MoveToX(width, TRANSITION_LENGTH, Easing.OutQuint); + this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); + } + + private void updateCounts() + { + UnreadCount.Value = unreadCount; + } + + private void markAllRead() + { + sections.Children.ForEach(s => s.MarkAllRead()); + + updateCounts(); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; + } + } +} diff --git a/osu.Game/Overlays/Notifications/IHasCompletionTarget.cs b/osu.Game/Overlays/Notifications/IHasCompletionTarget.cs index ea2928607f..6b91f50aa0 100644 --- a/osu.Game/Overlays/Notifications/IHasCompletionTarget.cs +++ b/osu.Game/Overlays/Notifications/IHasCompletionTarget.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Overlays.Notifications -{ - public interface IHasCompletionTarget - { - Action CompletionTarget { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Overlays.Notifications +{ + public interface IHasCompletionTarget + { + Action CompletionTarget { get; set; } + } +} diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 66db57c18f..d2b5ae1829 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -1,263 +1,263 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Graphics; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Notifications -{ - public abstract class Notification : Container - { - /// - /// User requested close. - /// - public event Action Closed; - - /// - /// Run on user activating the notification. Return true to close. - /// - public Func Activated; - - /// - /// Should we show at the top of our section on display? - /// - public virtual bool DisplayOnTop => true; - - protected NotificationLight Light; - private readonly CloseButton closeButton; - protected Container IconContent; - private readonly Container content; - - protected override Container Content => content; - - protected Container NotificationContent; - - public virtual bool Read { get; set; } - - protected Notification() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - AddRangeInternal(new Drawable[] - { - Light = new NotificationLight - { - Margin = new MarginPadding { Right = 5 }, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, - }, - NotificationContent = new Container - { - CornerRadius = 8, - Masking = true, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - AutoSizeDuration = 400, - AutoSizeEasing = Easing.OutQuint, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - }, - new Container - { - RelativeSizeAxes = Axes.X, - Padding = new MarginPadding(5), - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - IconContent = new Container - { - Size = new Vector2(40), - Masking = true, - CornerRadius = 5, - }, - content = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Left = 45, - Right = 30 - }, - } - } - }, - closeButton = new CloseButton - { - Alpha = 0, - Action = Close, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding - { - Right = 5 - }, - } - } - } - }); - } - - protected override bool OnHover(InputState state) - { - closeButton.FadeIn(75); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - closeButton.FadeOut(75); - base.OnHoverLost(state); - } - - protected override bool OnClick(InputState state) - { - if (Activated?.Invoke() ?? true) - Close(); - - return true; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - this.FadeInFromZero(200); - NotificationContent.MoveToX(DrawSize.X); - NotificationContent.MoveToX(0, 500, Easing.OutQuint); - } - - public bool WasClosed; - - public virtual void Close() - { - if (WasClosed) return; - WasClosed = true; - - Closed?.Invoke(); - this.FadeOut(100); - Expire(); - } - - private class CloseButton : OsuClickableContainer - { - private Color4 hoverColour; - - public CloseButton() - { - Colour = OsuColour.Gray(0.2f); - AutoSizeAxes = Axes.Both; - - Children = new[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.fa_times_circle, - Size = new Vector2(20), - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoverColour = colours.Yellow; - } - - protected override bool OnHover(InputState state) - { - this.FadeColour(hoverColour, 200); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - this.FadeColour(OsuColour.Gray(0.2f), 200); - base.OnHoverLost(state); - } - } - - public class NotificationLight : Container - { - private bool pulsate; - private Container pulsateLayer; - - public bool Pulsate - { - get { return pulsate; } - set - { - if (pulsate == value) return; - - pulsate = value; - - pulsateLayer.ClearTransforms(); - pulsateLayer.Alpha = 1; - - if (pulsate) - { - const float length = 1000; - pulsateLayer.Loop(length / 2, - p => p.FadeTo(0.4f, length, Easing.In).Then().FadeTo(1, length, Easing.Out) - ); - } - } - } - - public new SRGBColour Colour - { - set - { - base.Colour = value; - pulsateLayer.EdgeEffect = new EdgeEffectParameters - { - Colour = ((Color4)value).Opacity(0.5f), //todo: avoid cast - Type = EdgeEffectType.Glow, - Radius = 12, - Roundness = 12, - }; - } - } - - [BackgroundDependencyLoader] - private void load() - { - Size = new Vector2(6, 15); - - Children = new[] - { - pulsateLayer = new CircularContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Masking = true, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - }, - } - } - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Notifications +{ + public abstract class Notification : Container + { + /// + /// User requested close. + /// + public event Action Closed; + + /// + /// Run on user activating the notification. Return true to close. + /// + public Func Activated; + + /// + /// Should we show at the top of our section on display? + /// + public virtual bool DisplayOnTop => true; + + protected NotificationLight Light; + private readonly CloseButton closeButton; + protected Container IconContent; + private readonly Container content; + + protected override Container Content => content; + + protected Container NotificationContent; + + public virtual bool Read { get; set; } + + protected Notification() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + AddRangeInternal(new Drawable[] + { + Light = new NotificationLight + { + Margin = new MarginPadding { Right = 5 }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreRight, + }, + NotificationContent = new Container + { + CornerRadius = 8, + Masking = true, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + AutoSizeDuration = 400, + AutoSizeEasing = Easing.OutQuint, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + new Container + { + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding(5), + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + IconContent = new Container + { + Size = new Vector2(40), + Masking = true, + CornerRadius = 5, + }, + content = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Left = 45, + Right = 30 + }, + } + } + }, + closeButton = new CloseButton + { + Alpha = 0, + Action = Close, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding + { + Right = 5 + }, + } + } + } + }); + } + + protected override bool OnHover(InputState state) + { + closeButton.FadeIn(75); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + closeButton.FadeOut(75); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + if (Activated?.Invoke() ?? true) + Close(); + + return true; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.FadeInFromZero(200); + NotificationContent.MoveToX(DrawSize.X); + NotificationContent.MoveToX(0, 500, Easing.OutQuint); + } + + public bool WasClosed; + + public virtual void Close() + { + if (WasClosed) return; + WasClosed = true; + + Closed?.Invoke(); + this.FadeOut(100); + Expire(); + } + + private class CloseButton : OsuClickableContainer + { + private Color4 hoverColour; + + public CloseButton() + { + Colour = OsuColour.Gray(0.2f); + AutoSizeAxes = Axes.Both; + + Children = new[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_times_circle, + Size = new Vector2(20), + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverColour = colours.Yellow; + } + + protected override bool OnHover(InputState state) + { + this.FadeColour(hoverColour, 200); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + this.FadeColour(OsuColour.Gray(0.2f), 200); + base.OnHoverLost(state); + } + } + + public class NotificationLight : Container + { + private bool pulsate; + private Container pulsateLayer; + + public bool Pulsate + { + get { return pulsate; } + set + { + if (pulsate == value) return; + + pulsate = value; + + pulsateLayer.ClearTransforms(); + pulsateLayer.Alpha = 1; + + if (pulsate) + { + const float length = 1000; + pulsateLayer.Loop(length / 2, + p => p.FadeTo(0.4f, length, Easing.In).Then().FadeTo(1, length, Easing.Out) + ); + } + } + } + + public new SRGBColour Colour + { + set + { + base.Colour = value; + pulsateLayer.EdgeEffect = new EdgeEffectParameters + { + Colour = ((Color4)value).Opacity(0.5f), //todo: avoid cast + Type = EdgeEffectType.Glow, + Radius = 12, + Roundness = 12, + }; + } + } + + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(6, 15); + + Children = new[] + { + pulsateLayer = new CircularContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Masking = true, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + }, + } + } + }; + } + } + } +} diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 533f5326e3..c166624d2b 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -1,174 +1,174 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Notifications -{ - public class NotificationSection : AlwaysUpdateFillFlowContainer - { - private OsuSpriteText titleText; - private OsuSpriteText countText; - - private ClearAllButton clearButton; - - private FlowContainer notifications; - - public int DisplayedCount => notifications.Count(n => !n.WasClosed); - public int UnreadCount => notifications.Count(n => !n.WasClosed && !n.Read); - - public void Add(Notification notification, float position) - { - notifications.Add(notification); - notifications.SetLayoutPosition(notification, position); - } - - public IEnumerable AcceptTypes; - - private string clearText; - - public string ClearText - { - get { return clearText; } - set - { - clearText = value; - if (clearButton != null) clearButton.Text = clearText; - } - } - - private string title; - - public string Title - { - get { return title; } - set - { - title = value; - if (titleText != null) titleText.Text = title.ToUpper(); - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - - Padding = new MarginPadding - { - Top = 10, - Bottom = 5, - Right = 20, - Left = 20, - }; - - AddRangeInternal(new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - clearButton = new ClearAllButton - { - Text = clearText, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Action = clearAll - }, - new FillFlowContainer - { - Margin = new MarginPadding - { - Bottom = 5 - }, - Spacing = new Vector2(5, 0), - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - titleText = new OsuSpriteText - { - Text = title.ToUpper(), - Font = @"Exo2.0-Black", - }, - countText = new OsuSpriteText - { - Text = "3", - Colour = colours.Yellow, - Font = @"Exo2.0-Black", - }, - } - }, - }, - }, - notifications = new AlwaysUpdateFillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - LayoutDuration = 150, - LayoutEasing = Easing.OutQuart, - Spacing = new Vector2(3), - } - }); - } - - private void clearAll() - { - notifications.Children.ForEach(c => c.Close()); - } - - protected override void Update() - { - base.Update(); - - countText.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString(); - } - - private class ClearAllButton : OsuClickableContainer - { - private readonly OsuSpriteText text; - - public ClearAllButton() - { - AutoSizeAxes = Axes.Both; - - Children = new[] - { - text = new OsuSpriteText() - }; - } - - public string Text - { - get { return text.Text; } - set { text.Text = value.ToUpper(); } - } - } - - public void MarkAllRead() - { - notifications?.Children.ForEach(n => n.Read = true); - } - } - - public class AlwaysUpdateFillFlowContainer : FillFlowContainer - where T : Drawable - { - // this is required to ensure correct layout and scheduling on children. - // the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297). - protected override bool RequiresChildrenUpdate => true; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Notifications +{ + public class NotificationSection : AlwaysUpdateFillFlowContainer + { + private OsuSpriteText titleText; + private OsuSpriteText countText; + + private ClearAllButton clearButton; + + private FlowContainer notifications; + + public int DisplayedCount => notifications.Count(n => !n.WasClosed); + public int UnreadCount => notifications.Count(n => !n.WasClosed && !n.Read); + + public void Add(Notification notification, float position) + { + notifications.Add(notification); + notifications.SetLayoutPosition(notification, position); + } + + public IEnumerable AcceptTypes; + + private string clearText; + + public string ClearText + { + get { return clearText; } + set + { + clearText = value; + if (clearButton != null) clearButton.Text = clearText; + } + } + + private string title; + + public string Title + { + get { return title; } + set + { + title = value; + if (titleText != null) titleText.Text = title.ToUpper(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + + Padding = new MarginPadding + { + Top = 10, + Bottom = 5, + Right = 20, + Left = 20, + }; + + AddRangeInternal(new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + clearButton = new ClearAllButton + { + Text = clearText, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Action = clearAll + }, + new FillFlowContainer + { + Margin = new MarginPadding + { + Bottom = 5 + }, + Spacing = new Vector2(5, 0), + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + titleText = new OsuSpriteText + { + Text = title.ToUpper(), + Font = @"Exo2.0-Black", + }, + countText = new OsuSpriteText + { + Text = "3", + Colour = colours.Yellow, + Font = @"Exo2.0-Black", + }, + } + }, + }, + }, + notifications = new AlwaysUpdateFillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + LayoutDuration = 150, + LayoutEasing = Easing.OutQuart, + Spacing = new Vector2(3), + } + }); + } + + private void clearAll() + { + notifications.Children.ForEach(c => c.Close()); + } + + protected override void Update() + { + base.Update(); + + countText.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString(); + } + + private class ClearAllButton : OsuClickableContainer + { + private readonly OsuSpriteText text; + + public ClearAllButton() + { + AutoSizeAxes = Axes.Both; + + Children = new[] + { + text = new OsuSpriteText() + }; + } + + public string Text + { + get { return text.Text; } + set { text.Text = value.ToUpper(); } + } + } + + public void MarkAllRead() + { + notifications?.Children.ForEach(n => n.Read = true); + } + } + + public class AlwaysUpdateFillFlowContainer : FillFlowContainer + where T : Drawable + { + // this is required to ensure correct layout and scheduling on children. + // the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297). + protected override bool RequiresChildrenUpdate => true; + } +} diff --git a/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs b/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs index df2be95b40..0711e49608 100644 --- a/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs @@ -1,23 +1,23 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; -using osu.Framework.Graphics.Colour; - -namespace osu.Game.Overlays.Notifications -{ - public class ProgressCompletionNotification : SimpleNotification - { - public ProgressCompletionNotification() - { - Icon = FontAwesome.fa_check; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - IconBackgound.Colour = ColourInfo.GradientVertical(colours.GreenDark, colours.GreenLight); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Framework.Graphics.Colour; + +namespace osu.Game.Overlays.Notifications +{ + public class ProgressCompletionNotification : SimpleNotification + { + public ProgressCompletionNotification() + { + Icon = FontAwesome.fa_check; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IconBackgound.Colour = ColourInfo.GradientVertical(colours.GreenDark, colours.GreenLight); + } + } +} diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index b36ac6eebc..7a07fb970c 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -1,237 +1,237 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Notifications -{ - public class ProgressNotification : Notification, IHasCompletionTarget - { - public string Text - { - set - { - Schedule(() => textDrawable.Text = value); - } - } - - public string CompletionText { get; set; } = "Task has completed!"; - - public float Progress - { - get { return progressBar.Progress; } - set { Schedule(() => progressBar.Progress = value); } - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - //we may have received changes before we were displayed. - State = state; - } - - public virtual ProgressNotificationState State - { - get { return state; } - set - { - Schedule(() => - { - bool stateChanged = state != value; - state = value; - - if (IsLoaded) - { - switch (state) - { - case ProgressNotificationState.Queued: - Light.Colour = colourQueued; - Light.Pulsate = false; - progressBar.Active = false; - break; - case ProgressNotificationState.Active: - Light.Colour = colourActive; - Light.Pulsate = true; - progressBar.Active = true; - break; - case ProgressNotificationState.Cancelled: - Light.Colour = colourCancelled; - Light.Pulsate = false; - progressBar.Active = false; - break; - } - } - - if (stateChanged) - { - switch (state) - { - case ProgressNotificationState.Completed: - NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint); - this.FadeOut(200).Finally(d => Completed()); - break; - } - } - }); - } - } - - private ProgressNotificationState state; - - protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification - { - Activated = CompletionClickAction, - Text = CompletionText - }; - - protected virtual void Completed() - { - CompletionTarget?.Invoke(CreateCompletionNotification()); - base.Close(); - } - - public override bool DisplayOnTop => false; - - private readonly ProgressBar progressBar; - private Color4 colourQueued; - private Color4 colourActive; - private Color4 colourCancelled; - - private readonly TextFlowContainer textDrawable; - - public ProgressNotification() - { - IconContent.Add(new Box - { - RelativeSizeAxes = Axes.Both, - }); - - Content.Add(textDrawable = new OsuTextFlowContainer(t => - { - t.TextSize = 16; - }) - { - Colour = OsuColour.Gray(128), - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }); - - NotificationContent.Add(progressBar = new ProgressBar - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - }); - - State = ProgressNotificationState.Queued; - - // don't close on click by default. - Activated = () => false; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - colourQueued = colours.YellowDark; - colourActive = colours.Blue; - colourCancelled = colours.Red; - } - - public override void Close() - { - switch (State) - { - case ProgressNotificationState.Cancelled: - base.Close(); - break; - case ProgressNotificationState.Active: - case ProgressNotificationState.Queued: - if (CancelRequested?.Invoke() != false) - State = ProgressNotificationState.Cancelled; - break; - } - } - - public Func CancelRequested { get; set; } - - /// - /// The function to post completion notifications back to. - /// - public Action CompletionTarget { get; set; } - - /// - /// An action to complete when the completion notification is clicked. - /// - public Func CompletionClickAction; - - private class ProgressBar : Container - { - private readonly Box box; - - private Color4 colourActive; - private Color4 colourInactive; - - private float progress; - public float Progress - { - get { return progress; } - set - { - if (progress == value) return; - - progress = value; - box.ResizeTo(new Vector2(progress, 1), 100, Easing.OutQuad); - } - } - - private bool active; - - public bool Active - { - get { return active; } - set - { - active = value; - this.FadeColour(active ? colourActive : colourInactive, 100); - } - } - - public ProgressBar() - { - Children = new[] - { - box = new Box - { - RelativeSizeAxes = Axes.Both, - Width = 0, - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - colourActive = colours.Blue; - Colour = colourInactive = OsuColour.Gray(0.5f); - Height = 5; - } - } - } - - public enum ProgressNotificationState - { - Queued, - Active, - Completed, - Cancelled - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Notifications +{ + public class ProgressNotification : Notification, IHasCompletionTarget + { + public string Text + { + set + { + Schedule(() => textDrawable.Text = value); + } + } + + public string CompletionText { get; set; } = "Task has completed!"; + + public float Progress + { + get { return progressBar.Progress; } + set { Schedule(() => progressBar.Progress = value); } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + //we may have received changes before we were displayed. + State = state; + } + + public virtual ProgressNotificationState State + { + get { return state; } + set + { + Schedule(() => + { + bool stateChanged = state != value; + state = value; + + if (IsLoaded) + { + switch (state) + { + case ProgressNotificationState.Queued: + Light.Colour = colourQueued; + Light.Pulsate = false; + progressBar.Active = false; + break; + case ProgressNotificationState.Active: + Light.Colour = colourActive; + Light.Pulsate = true; + progressBar.Active = true; + break; + case ProgressNotificationState.Cancelled: + Light.Colour = colourCancelled; + Light.Pulsate = false; + progressBar.Active = false; + break; + } + } + + if (stateChanged) + { + switch (state) + { + case ProgressNotificationState.Completed: + NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint); + this.FadeOut(200).Finally(d => Completed()); + break; + } + } + }); + } + } + + private ProgressNotificationState state; + + protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification + { + Activated = CompletionClickAction, + Text = CompletionText + }; + + protected virtual void Completed() + { + CompletionTarget?.Invoke(CreateCompletionNotification()); + base.Close(); + } + + public override bool DisplayOnTop => false; + + private readonly ProgressBar progressBar; + private Color4 colourQueued; + private Color4 colourActive; + private Color4 colourCancelled; + + private readonly TextFlowContainer textDrawable; + + public ProgressNotification() + { + IconContent.Add(new Box + { + RelativeSizeAxes = Axes.Both, + }); + + Content.Add(textDrawable = new OsuTextFlowContainer(t => + { + t.TextSize = 16; + }) + { + Colour = OsuColour.Gray(128), + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }); + + NotificationContent.Add(progressBar = new ProgressBar + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + }); + + State = ProgressNotificationState.Queued; + + // don't close on click by default. + Activated = () => false; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + colourQueued = colours.YellowDark; + colourActive = colours.Blue; + colourCancelled = colours.Red; + } + + public override void Close() + { + switch (State) + { + case ProgressNotificationState.Cancelled: + base.Close(); + break; + case ProgressNotificationState.Active: + case ProgressNotificationState.Queued: + if (CancelRequested?.Invoke() != false) + State = ProgressNotificationState.Cancelled; + break; + } + } + + public Func CancelRequested { get; set; } + + /// + /// The function to post completion notifications back to. + /// + public Action CompletionTarget { get; set; } + + /// + /// An action to complete when the completion notification is clicked. + /// + public Func CompletionClickAction; + + private class ProgressBar : Container + { + private readonly Box box; + + private Color4 colourActive; + private Color4 colourInactive; + + private float progress; + public float Progress + { + get { return progress; } + set + { + if (progress == value) return; + + progress = value; + box.ResizeTo(new Vector2(progress, 1), 100, Easing.OutQuad); + } + } + + private bool active; + + public bool Active + { + get { return active; } + set + { + active = value; + this.FadeColour(active ? colourActive : colourInactive, 100); + } + } + + public ProgressBar() + { + Children = new[] + { + box = new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + colourActive = colours.Blue; + Colour = colourInactive = OsuColour.Gray(0.5f); + Height = 5; + } + } + } + + public enum ProgressNotificationState + { + Queued, + Active, + Completed, + Cancelled + } +} diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs index c3d5a52476..a78bc8da81 100644 --- a/osu.Game/Overlays/Notifications/SimpleNotification.cs +++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs @@ -1,93 +1,93 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Overlays.Notifications -{ - public class SimpleNotification : Notification - { - private string text = string.Empty; - public string Text - { - get { return text; } - set - { - text = value; - textDrawable.Text = text; - } - } - - private FontAwesome icon = FontAwesome.fa_info_circle; - public FontAwesome Icon - { - get { return icon; } - set - { - icon = value; - iconDrawable.Icon = icon; - } - } - - private readonly TextFlowContainer textDrawable; - private readonly SpriteIcon iconDrawable; - - protected Box IconBackgound; - - public SimpleNotification() - { - IconContent.AddRange(new Drawable[] - { - IconBackgound = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.6f)) - }, - iconDrawable = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = icon, - Size = new Vector2(20), - } - }); - - Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 16) - { - Colour = OsuColour.Gray(128), - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Text = text - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Light.Colour = colours.Green; - } - - public override bool Read - { - get - { - return base.Read; - } - - set - { - if (value == base.Read) return; - - base.Read = value; - Light.FadeTo(value ? 0 : 1, 100); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Overlays.Notifications +{ + public class SimpleNotification : Notification + { + private string text = string.Empty; + public string Text + { + get { return text; } + set + { + text = value; + textDrawable.Text = text; + } + } + + private FontAwesome icon = FontAwesome.fa_info_circle; + public FontAwesome Icon + { + get { return icon; } + set + { + icon = value; + iconDrawable.Icon = icon; + } + } + + private readonly TextFlowContainer textDrawable; + private readonly SpriteIcon iconDrawable; + + protected Box IconBackgound; + + public SimpleNotification() + { + IconContent.AddRange(new Drawable[] + { + IconBackgound = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.6f)) + }, + iconDrawable = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = icon, + Size = new Vector2(20), + } + }); + + Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 16) + { + Colour = OsuColour.Gray(128), + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Text = text + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Light.Colour = colours.Green; + } + + public override bool Read + { + get + { + return base.Read; + } + + set + { + if (value == base.Read) return; + + base.Read = value; + Light.FadeTo(value ? 0 : 1, 100); + } + } + } +} diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index bbb2c476f4..3dd088891d 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -1,291 +1,291 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Configuration.Tracking; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Overlays -{ - public class OnScreenDisplay : Container - { - private readonly Container box; - - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - - private readonly SpriteText textLine1; - private readonly SpriteText textLine2; - private readonly SpriteText textLine3; - - private const float height = 110; - private const float height_contracted = height * 0.9f; - - private readonly FillFlowContainer optionLights; - - public OnScreenDisplay() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - box = new Container - { - Origin = Anchor.Centre, - RelativePositionAxes = Axes.Both, - Position = new Vector2(0.5f, 0.75f), - Masking = true, - AutoSizeAxes = Axes.X, - Height = height_contracted, - Alpha = 0, - CornerRadius = 20, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.7f, - }, - new Container // purely to add a minimum width - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 240, - RelativeSizeAxes = Axes.Y, - }, - textLine1 = new OsuSpriteText - { - Padding = new MarginPadding(10), - Font = @"Exo2.0-Black", - Spacing = new Vector2(1, 0), - TextSize = 14, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - textLine2 = new OsuSpriteText - { - TextSize = 24, - Font = @"Exo2.0-Light", - Padding = new MarginPadding { Left = 10, Right = 10 }, - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - }, - new FillFlowContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - optionLights = new FillFlowContainer - { - Padding = new MarginPadding { Top = 20, Bottom = 5 }, - Spacing = new Vector2(5, 0), - Direction = FillDirection.Horizontal, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both - }, - textLine3 = new OsuSpriteText - { - Padding = new MarginPadding { Bottom = 15 }, - Font = @"Exo2.0-Bold", - TextSize = 12, - Alpha = 0.3f, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - } - } - } - }, - }; - } - - [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig) - { - BeginTracking(this, frameworkConfig); - } - - private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>(); - - /// - /// Registers a to have its settings tracked by this . - /// - /// The object that is registering the to be tracked. - /// The to be tracked. - /// If is null. - /// If is already being tracked from the same . - public void BeginTracking(object source, ITrackableConfigManager configManager) - { - if (configManager == null) throw new ArgumentNullException(nameof(configManager)); - - if (trackedConfigManagers.ContainsKey((source, configManager))) - throw new InvalidOperationException($"{nameof(configManager)} is already registered."); - - var trackedSettings = configManager.CreateTrackedSettings(); - if (trackedSettings == null) - return; - - configManager.LoadInto(trackedSettings); - trackedSettings.SettingChanged += display; - - trackedConfigManagers.Add((source, configManager), trackedSettings); - } - - /// - /// Unregisters a from having its settings tracked by this . - /// - /// The object that registered the to be tracked. - /// The that is being tracked. - /// If is null. - /// If is not being tracked from the same . - public void StopTracking(object source, ITrackableConfigManager configManager) - { - if (configManager == null) throw new ArgumentNullException(nameof(configManager)); - - if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing)) - throw new InvalidOperationException($"{nameof(configManager)} is not registered."); - - existing.Unload(); - existing.SettingChanged -= display; - - trackedConfigManagers.Remove((source, configManager)); - } - - private void display(SettingDescription description) - { - Schedule(() => - { - textLine1.Text = description.Name.ToUpper(); - textLine2.Text = description.Value; - textLine3.Text = description.Shortcut.ToUpper(); - - box.Animate( - b => b.FadeIn(500, Easing.OutQuint), - b => b.ResizeHeightTo(height, 500, Easing.OutQuint) - ).Then( - b => b.FadeOutFromOne(1500, Easing.InQuint), - b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint) - ); - - int optionCount = 0; - int selectedOption = -1; - - if (description.RawValue is bool) - { - optionCount = 1; - if ((bool)description.RawValue) selectedOption = 0; - } - else if (description.RawValue is Enum) - { - var values = Enum.GetValues(description.RawValue.GetType()); - optionCount = values.Length; - selectedOption = Convert.ToInt32(description.RawValue); - } - - textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre; - textLine2.Y = optionCount > 0 ? 0 : 5; - - if (optionLights.Children.Count != optionCount) - { - optionLights.Clear(); - for (int i = 0; i < optionCount; i++) - optionLights.Add(new OptionLight()); - } - - for (int i = 0; i < optionCount; i++) - optionLights.Children[i].Glowing = i == selectedOption; - }); - } - - private class OptionLight : Container - { - private Color4 glowingColour, idleColour; - - private const float transition_speed = 300; - - private const float glow_strength = 0.4f; - - private readonly Box fill; - - public OptionLight() - { - Children = new[] - { - fill = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 1, - }, - }; - } - - private bool glowing; - - public bool Glowing - { - set - { - glowing = value; - if (!IsLoaded) return; - - updateGlow(); - } - } - - private void updateGlow() - { - if (glowing) - { - fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint); - FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint); - } - else - { - FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint); - fill.FadeColour(idleColour, transition_speed, Easing.OutQuint); - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - fill.Colour = idleColour = Color4.White.Opacity(0.4f); - glowingColour = Color4.White; - - Size = new Vector2(25, 5); - - Masking = true; - CornerRadius = 3; - - EdgeEffect = new EdgeEffectParameters - { - Colour = colours.BlueDark.Opacity(glow_strength), - Type = EdgeEffectType.Glow, - Radius = 8, - }; - } - - protected override void LoadComplete() - { - updateGlow(); - FinishTransforms(true); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Configuration.Tracking; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays +{ + public class OnScreenDisplay : Container + { + private readonly Container box; + + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; + + private readonly SpriteText textLine1; + private readonly SpriteText textLine2; + private readonly SpriteText textLine3; + + private const float height = 110; + private const float height_contracted = height * 0.9f; + + private readonly FillFlowContainer optionLights; + + public OnScreenDisplay() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + box = new Container + { + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + Position = new Vector2(0.5f, 0.75f), + Masking = true, + AutoSizeAxes = Axes.X, + Height = height_contracted, + Alpha = 0, + CornerRadius = 20, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.7f, + }, + new Container // purely to add a minimum width + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 240, + RelativeSizeAxes = Axes.Y, + }, + textLine1 = new OsuSpriteText + { + Padding = new MarginPadding(10), + Font = @"Exo2.0-Black", + Spacing = new Vector2(1, 0), + TextSize = 14, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + textLine2 = new OsuSpriteText + { + TextSize = 24, + Font = @"Exo2.0-Light", + Padding = new MarginPadding { Left = 10, Right = 10 }, + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + }, + new FillFlowContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + optionLights = new FillFlowContainer + { + Padding = new MarginPadding { Top = 20, Bottom = 5 }, + Spacing = new Vector2(5, 0), + Direction = FillDirection.Horizontal, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both + }, + textLine3 = new OsuSpriteText + { + Padding = new MarginPadding { Bottom = 15 }, + Font = @"Exo2.0-Bold", + TextSize = 12, + Alpha = 0.3f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + } + } + } + }, + }; + } + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager frameworkConfig) + { + BeginTracking(this, frameworkConfig); + } + + private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>(); + + /// + /// Registers a to have its settings tracked by this . + /// + /// The object that is registering the to be tracked. + /// The to be tracked. + /// If is null. + /// If is already being tracked from the same . + public void BeginTracking(object source, ITrackableConfigManager configManager) + { + if (configManager == null) throw new ArgumentNullException(nameof(configManager)); + + if (trackedConfigManagers.ContainsKey((source, configManager))) + throw new InvalidOperationException($"{nameof(configManager)} is already registered."); + + var trackedSettings = configManager.CreateTrackedSettings(); + if (trackedSettings == null) + return; + + configManager.LoadInto(trackedSettings); + trackedSettings.SettingChanged += display; + + trackedConfigManagers.Add((source, configManager), trackedSettings); + } + + /// + /// Unregisters a from having its settings tracked by this . + /// + /// The object that registered the to be tracked. + /// The that is being tracked. + /// If is null. + /// If is not being tracked from the same . + public void StopTracking(object source, ITrackableConfigManager configManager) + { + if (configManager == null) throw new ArgumentNullException(nameof(configManager)); + + if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing)) + throw new InvalidOperationException($"{nameof(configManager)} is not registered."); + + existing.Unload(); + existing.SettingChanged -= display; + + trackedConfigManagers.Remove((source, configManager)); + } + + private void display(SettingDescription description) + { + Schedule(() => + { + textLine1.Text = description.Name.ToUpper(); + textLine2.Text = description.Value; + textLine3.Text = description.Shortcut.ToUpper(); + + box.Animate( + b => b.FadeIn(500, Easing.OutQuint), + b => b.ResizeHeightTo(height, 500, Easing.OutQuint) + ).Then( + b => b.FadeOutFromOne(1500, Easing.InQuint), + b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint) + ); + + int optionCount = 0; + int selectedOption = -1; + + if (description.RawValue is bool) + { + optionCount = 1; + if ((bool)description.RawValue) selectedOption = 0; + } + else if (description.RawValue is Enum) + { + var values = Enum.GetValues(description.RawValue.GetType()); + optionCount = values.Length; + selectedOption = Convert.ToInt32(description.RawValue); + } + + textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre; + textLine2.Y = optionCount > 0 ? 0 : 5; + + if (optionLights.Children.Count != optionCount) + { + optionLights.Clear(); + for (int i = 0; i < optionCount; i++) + optionLights.Add(new OptionLight()); + } + + for (int i = 0; i < optionCount; i++) + optionLights.Children[i].Glowing = i == selectedOption; + }); + } + + private class OptionLight : Container + { + private Color4 glowingColour, idleColour; + + private const float transition_speed = 300; + + private const float glow_strength = 0.4f; + + private readonly Box fill; + + public OptionLight() + { + Children = new[] + { + fill = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 1, + }, + }; + } + + private bool glowing; + + public bool Glowing + { + set + { + glowing = value; + if (!IsLoaded) return; + + updateGlow(); + } + } + + private void updateGlow() + { + if (glowing) + { + fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint); + FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint); + } + else + { + FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint); + fill.FadeColour(idleColour, transition_speed, Easing.OutQuint); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + fill.Colour = idleColour = Color4.White.Opacity(0.4f); + glowingColour = Color4.White; + + Size = new Vector2(25, 5); + + Masking = true; + CornerRadius = 3; + + EdgeEffect = new EdgeEffectParameters + { + Colour = colours.BlueDark.Opacity(glow_strength), + Type = EdgeEffectType.Glow, + Radius = 8, + }; + } + + protected override void LoadComplete() + { + updateGlow(); + FinishTransforms(true); + } + } + } +} diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index f4b363cd91..4a88431cc4 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -1,499 +1,499 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Diagnostics; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Users; - -namespace osu.Game.Overlays.Profile -{ - public class ProfileHeader : Container - { - private readonly OsuTextFlowContainer infoTextLeft; - private readonly LinkFlowContainer infoTextRight; - private readonly FillFlowContainer scoreText, scoreNumberText; - private readonly RankGraph rankGraph; - - public readonly SupporterIcon SupporterTag; - private readonly Container coverContainer; - private readonly Sprite levelBadge; - private readonly SpriteText levelText; - private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA; - private readonly Box colourBar; - private readonly DrawableFlag countryFlag; - - private const float cover_height = 350; - private const float info_height = 150; - private const float info_width = 220; - private const float avatar_size = 110; - private const float level_position = 30; - private const float level_height = 60; - - public ProfileHeader(User user) - { - RelativeSizeAxes = Axes.X; - Height = cover_height + info_height; - - Children = new Drawable[] - { - coverContainer = new Container - { - RelativeSizeAxes = Axes.X, - Height = cover_height, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f)) - }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - X = UserProfileOverlay.CONTENT_X_MARGIN, - Y = -20, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new UpdateableAvatar - { - User = user, - Size = new Vector2(avatar_size), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Masking = true, - CornerRadius = 5, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.25f), - Radius = 4, - }, - }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - X = avatar_size + 10, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - SupporterTag = new SupporterIcon - { - Alpha = 0, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = -75, - Size = new Vector2(25, 25) - }, - new ProfileLink(user) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = -48, - }, - countryFlag = new DrawableFlag(user.Country) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Width = 30, - Height = 20 - } - } - } - } - }, - colourBar = new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - X = UserProfileOverlay.CONTENT_X_MARGIN, - Height = 5, - Width = info_width, - Alpha = 0 - } - } - }, - infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14) - { - X = UserProfileOverlay.CONTENT_X_MARGIN, - Y = cover_height + 20, - Width = info_width, - AutoSizeAxes = Axes.Y, - ParagraphSpacing = 0.8f, - LineSpacing = 0.2f - }, - infoTextRight = new LinkFlowContainer(t => - { - t.TextSize = 14; - t.Font = @"Exo2.0-RegularItalic"; - }) - { - X = UserProfileOverlay.CONTENT_X_MARGIN + info_width + 20, - Y = cover_height + 20, - Width = info_width, - AutoSizeAxes = Axes.Y, - ParagraphSpacing = 0.8f, - LineSpacing = 0.2f - }, - new Container - { - X = -UserProfileOverlay.CONTENT_X_MARGIN, - RelativeSizeAxes = Axes.Y, - Width = 280, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Y = level_position, - Height = level_height, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Black.Opacity(0.5f), - RelativeSizeAxes = Axes.Both - }, - levelBadge = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Height = 50, - Width = 50, - Alpha = 0 - }, - levelText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Y = 11, - TextSize = 20 - } - } - }, - new Container - { - RelativeSizeAxes = Axes.X, - Y = cover_height, - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - Height = cover_height - level_height - level_position - 5, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Black.Opacity(0.5f), - RelativeSizeAxes = Axes.Both - }, - scoreText = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Horizontal = 20, Vertical = 18 }, - Spacing = new Vector2(0, 2) - }, - scoreNumberText = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Horizontal = 20, Vertical = 18 }, - Spacing = new Vector2(0, 2) - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -64, - Spacing = new Vector2(20, 0), - Children = new[] - { - gradeSSPlus = new GradeBadge("SSPlus") { Alpha = 0 }, - gradeSS = new GradeBadge("SS") { Alpha = 0 }, - } - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -18, - Spacing = new Vector2(20, 0), - Children = new[] - { - gradeSPlus = new GradeBadge("SPlus") { Alpha = 0 }, - gradeS = new GradeBadge("S") { Alpha = 0 }, - gradeA = new GradeBadge("A") { Alpha = 0 }, - } - } - } - }, - new Container - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Height = info_height - 15, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Black.Opacity(0.25f), - RelativeSizeAxes = Axes.Both - }, - rankGraph = new RankGraph - { - RelativeSizeAxes = Axes.Both - } - } - } - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - levelBadge.Texture = textures.Get(@"Profile/levelbadge"); - } - - private User user; - - public User User - { - get { return user; } - set - { - user = value; - loadUser(); - } - } - - private void loadUser() - { - LoadComponentAsync(new UserCoverBackground(user) - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fill, - OnLoadComplete = d => d.FadeInFromZero(200), - Depth = float.MaxValue, - }, coverContainer.Add); - - if (user.IsSupporter) - SupporterTag.Show(); - - if (!string.IsNullOrEmpty(user.Colour)) - { - colourBar.Colour = OsuColour.FromHex(user.Colour); - colourBar.Show(); - } - - void boldItalic(SpriteText t) => t.Font = @"Exo2.0-BoldItalic"; - void lightText(SpriteText t) => t.Alpha = 0.8f; - - OsuSpriteText createScoreText(string text) => new OsuSpriteText - { - TextSize = 14, - Text = text - }; - - OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText - { - TextSize = 14, - Font = @"Exo2.0-Bold", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Text = text - }; - - if (user.Age != null) - { - infoTextLeft.AddText($"{user.Age} years old ", boldItalic); - } - - if (user.Country != null) - { - infoTextLeft.AddText("from ", lightText); - infoTextLeft.AddText(user.Country.FullName, boldItalic); - countryFlag.Country = user.Country; - } - - infoTextLeft.NewParagraph(); - - if (user.JoinDate.ToUniversalTime().Year < 2008) - { - infoTextLeft.AddText("Here since the beginning", boldItalic); - } - else - { - infoTextLeft.AddText("Joined ", lightText); - infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic); - } - - infoTextLeft.NewLine(); - infoTextLeft.AddText("Last seen ", lightText); - infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic); - infoTextLeft.NewParagraph(); - - if (user.PlayStyle?.Length > 0) - { - infoTextLeft.AddText("Plays with ", lightText); - infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); - } - - string websiteWithoutProtcol = user.Website; - if (!string.IsNullOrEmpty(websiteWithoutProtcol)) - { - int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal); - if (protocolIndex >= 0) - websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2); - } - - tryAddInfoRightLine(FontAwesome.fa_map_marker, user.Location); - tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Interests); - tryAddInfoRightLine(FontAwesome.fa_suitcase, user.Occupation); - infoTextRight.NewParagraph(); - if (!string.IsNullOrEmpty(user.Twitter)) - tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); - tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website); - tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); - - if (user.Statistics != null) - { - levelBadge.Show(); - levelText.Text = user.Statistics.Level.Current.ToString(); - - scoreText.Add(createScoreText("Ranked Score")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.RankedScore.ToString(@"#,0"))); - scoreText.Add(createScoreText("Accuracy")); - scoreNumberText.Add(createScoreNumberText($"{user.Statistics.Accuracy:0.##}%")); - scoreText.Add(createScoreText("Play Count")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.PlayCount.ToString(@"#,0"))); - scoreText.Add(createScoreText("Total Score")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalScore.ToString(@"#,0"))); - scoreText.Add(createScoreText("Total Hits")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalHits.ToString(@"#,0"))); - scoreText.Add(createScoreText("Max Combo")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.MaxCombo.ToString(@"#,0"))); - scoreText.Add(createScoreText("Replays Watched by Others")); - scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplaysWatched.ToString(@"#,0"))); - - gradeSSPlus.DisplayCount = user.Statistics.GradesCount.SSPlus; - gradeSSPlus.Show(); - gradeSS.DisplayCount = user.Statistics.GradesCount.SS; - gradeSS.Show(); - gradeSPlus.DisplayCount = user.Statistics.GradesCount.SPlus; - gradeSPlus.Show(); - gradeS.DisplayCount = user.Statistics.GradesCount.S; - gradeS.Show(); - gradeA.DisplayCount = user.Statistics.GradesCount.A; - gradeA.Show(); - - rankGraph.User.Value = user; - } - } - - private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null) - { - if (string.IsNullOrEmpty(str)) return; - - infoTextRight.AddIcon(icon); - if (url != null) - { - infoTextRight.AddLink(" " + str, url); - } - else - { - infoTextRight.AddText(" " + str); - } - - infoTextRight.NewLine(); - } - - private class ProfileLink : OsuHoverContainer, IHasTooltip - { - public string TooltipText => "View Profile in Browser"; - - public override bool HandleMouseInput => true; - - public ProfileLink(User user) - { - Action = () => Process.Start($@"https://osu.ppy.sh/users/{user.Id}"); - - AutoSizeAxes = Axes.Both; - - Child = new OsuSpriteText - { - Text = user.Username, - Font = @"Exo2.0-RegularItalic", - TextSize = 30, - }; - } - } - - - private class GradeBadge : Container - { - private const float width = 50; - private readonly string grade; - private readonly Sprite badge; - private readonly SpriteText numberText; - - public int DisplayCount - { - set { numberText.Text = value.ToString(@"#,0"); } - } - - public GradeBadge(string grade) - { - this.grade = grade; - Width = width; - Height = 41; - Add(badge = new Sprite - { - Width = width, - Height = 26 - }); - Add(numberText = new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - TextSize = 14, - Font = @"Exo2.0-Bold" - }); - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - badge.Texture = textures.Get($"Grades/{grade}"); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Diagnostics; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile +{ + public class ProfileHeader : Container + { + private readonly OsuTextFlowContainer infoTextLeft; + private readonly LinkFlowContainer infoTextRight; + private readonly FillFlowContainer scoreText, scoreNumberText; + private readonly RankGraph rankGraph; + + public readonly SupporterIcon SupporterTag; + private readonly Container coverContainer; + private readonly Sprite levelBadge; + private readonly SpriteText levelText; + private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA; + private readonly Box colourBar; + private readonly DrawableFlag countryFlag; + + private const float cover_height = 350; + private const float info_height = 150; + private const float info_width = 220; + private const float avatar_size = 110; + private const float level_position = 30; + private const float level_height = 60; + + public ProfileHeader(User user) + { + RelativeSizeAxes = Axes.X; + Height = cover_height + info_height; + + Children = new Drawable[] + { + coverContainer = new Container + { + RelativeSizeAxes = Axes.X, + Height = cover_height, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f)) + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + X = UserProfileOverlay.CONTENT_X_MARGIN, + Y = -20, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new UpdateableAvatar + { + User = user, + Size = new Vector2(avatar_size), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Masking = true, + CornerRadius = 5, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Radius = 4, + }, + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + X = avatar_size + 10, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + SupporterTag = new SupporterIcon + { + Alpha = 0, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = -75, + Size = new Vector2(25, 25) + }, + new ProfileLink(user) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = -48, + }, + countryFlag = new DrawableFlag(user.Country) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Width = 30, + Height = 20 + } + } + } + } + }, + colourBar = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + X = UserProfileOverlay.CONTENT_X_MARGIN, + Height = 5, + Width = info_width, + Alpha = 0 + } + } + }, + infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14) + { + X = UserProfileOverlay.CONTENT_X_MARGIN, + Y = cover_height + 20, + Width = info_width, + AutoSizeAxes = Axes.Y, + ParagraphSpacing = 0.8f, + LineSpacing = 0.2f + }, + infoTextRight = new LinkFlowContainer(t => + { + t.TextSize = 14; + t.Font = @"Exo2.0-RegularItalic"; + }) + { + X = UserProfileOverlay.CONTENT_X_MARGIN + info_width + 20, + Y = cover_height + 20, + Width = info_width, + AutoSizeAxes = Axes.Y, + ParagraphSpacing = 0.8f, + LineSpacing = 0.2f + }, + new Container + { + X = -UserProfileOverlay.CONTENT_X_MARGIN, + RelativeSizeAxes = Axes.Y, + Width = 280, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Y = level_position, + Height = level_height, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black.Opacity(0.5f), + RelativeSizeAxes = Axes.Both + }, + levelBadge = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Height = 50, + Width = 50, + Alpha = 0 + }, + levelText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Y = 11, + TextSize = 20 + } + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + Y = cover_height, + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + Height = cover_height - level_height - level_position - 5, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black.Opacity(0.5f), + RelativeSizeAxes = Axes.Both + }, + scoreText = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Horizontal = 20, Vertical = 18 }, + Spacing = new Vector2(0, 2) + }, + scoreNumberText = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Horizontal = 20, Vertical = 18 }, + Spacing = new Vector2(0, 2) + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -64, + Spacing = new Vector2(20, 0), + Children = new[] + { + gradeSSPlus = new GradeBadge("SSPlus") { Alpha = 0 }, + gradeSS = new GradeBadge("SS") { Alpha = 0 }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -18, + Spacing = new Vector2(20, 0), + Children = new[] + { + gradeSPlus = new GradeBadge("SPlus") { Alpha = 0 }, + gradeS = new GradeBadge("S") { Alpha = 0 }, + gradeA = new GradeBadge("A") { Alpha = 0 }, + } + } + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Height = info_height - 15, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black.Opacity(0.25f), + RelativeSizeAxes = Axes.Both + }, + rankGraph = new RankGraph + { + RelativeSizeAxes = Axes.Both + } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + levelBadge.Texture = textures.Get(@"Profile/levelbadge"); + } + + private User user; + + public User User + { + get { return user; } + set + { + user = value; + loadUser(); + } + } + + private void loadUser() + { + LoadComponentAsync(new UserCoverBackground(user) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill, + OnLoadComplete = d => d.FadeInFromZero(200), + Depth = float.MaxValue, + }, coverContainer.Add); + + if (user.IsSupporter) + SupporterTag.Show(); + + if (!string.IsNullOrEmpty(user.Colour)) + { + colourBar.Colour = OsuColour.FromHex(user.Colour); + colourBar.Show(); + } + + void boldItalic(SpriteText t) => t.Font = @"Exo2.0-BoldItalic"; + void lightText(SpriteText t) => t.Alpha = 0.8f; + + OsuSpriteText createScoreText(string text) => new OsuSpriteText + { + TextSize = 14, + Text = text + }; + + OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-Bold", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = text + }; + + if (user.Age != null) + { + infoTextLeft.AddText($"{user.Age} years old ", boldItalic); + } + + if (user.Country != null) + { + infoTextLeft.AddText("from ", lightText); + infoTextLeft.AddText(user.Country.FullName, boldItalic); + countryFlag.Country = user.Country; + } + + infoTextLeft.NewParagraph(); + + if (user.JoinDate.ToUniversalTime().Year < 2008) + { + infoTextLeft.AddText("Here since the beginning", boldItalic); + } + else + { + infoTextLeft.AddText("Joined ", lightText); + infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic); + } + + infoTextLeft.NewLine(); + infoTextLeft.AddText("Last seen ", lightText); + infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic); + infoTextLeft.NewParagraph(); + + if (user.PlayStyle?.Length > 0) + { + infoTextLeft.AddText("Plays with ", lightText); + infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); + } + + string websiteWithoutProtcol = user.Website; + if (!string.IsNullOrEmpty(websiteWithoutProtcol)) + { + int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal); + if (protocolIndex >= 0) + websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2); + } + + tryAddInfoRightLine(FontAwesome.fa_map_marker, user.Location); + tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Interests); + tryAddInfoRightLine(FontAwesome.fa_suitcase, user.Occupation); + infoTextRight.NewParagraph(); + if (!string.IsNullOrEmpty(user.Twitter)) + tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); + tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website); + tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); + + if (user.Statistics != null) + { + levelBadge.Show(); + levelText.Text = user.Statistics.Level.Current.ToString(); + + scoreText.Add(createScoreText("Ranked Score")); + scoreNumberText.Add(createScoreNumberText(user.Statistics.RankedScore.ToString(@"#,0"))); + scoreText.Add(createScoreText("Accuracy")); + scoreNumberText.Add(createScoreNumberText($"{user.Statistics.Accuracy:0.##}%")); + scoreText.Add(createScoreText("Play Count")); + scoreNumberText.Add(createScoreNumberText(user.Statistics.PlayCount.ToString(@"#,0"))); + scoreText.Add(createScoreText("Total Score")); + scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalScore.ToString(@"#,0"))); + scoreText.Add(createScoreText("Total Hits")); + scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalHits.ToString(@"#,0"))); + scoreText.Add(createScoreText("Max Combo")); + scoreNumberText.Add(createScoreNumberText(user.Statistics.MaxCombo.ToString(@"#,0"))); + scoreText.Add(createScoreText("Replays Watched by Others")); + scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplaysWatched.ToString(@"#,0"))); + + gradeSSPlus.DisplayCount = user.Statistics.GradesCount.SSPlus; + gradeSSPlus.Show(); + gradeSS.DisplayCount = user.Statistics.GradesCount.SS; + gradeSS.Show(); + gradeSPlus.DisplayCount = user.Statistics.GradesCount.SPlus; + gradeSPlus.Show(); + gradeS.DisplayCount = user.Statistics.GradesCount.S; + gradeS.Show(); + gradeA.DisplayCount = user.Statistics.GradesCount.A; + gradeA.Show(); + + rankGraph.User.Value = user; + } + } + + private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null) + { + if (string.IsNullOrEmpty(str)) return; + + infoTextRight.AddIcon(icon); + if (url != null) + { + infoTextRight.AddLink(" " + str, url); + } + else + { + infoTextRight.AddText(" " + str); + } + + infoTextRight.NewLine(); + } + + private class ProfileLink : OsuHoverContainer, IHasTooltip + { + public string TooltipText => "View Profile in Browser"; + + public override bool HandleMouseInput => true; + + public ProfileLink(User user) + { + Action = () => Process.Start($@"https://osu.ppy.sh/users/{user.Id}"); + + AutoSizeAxes = Axes.Both; + + Child = new OsuSpriteText + { + Text = user.Username, + Font = @"Exo2.0-RegularItalic", + TextSize = 30, + }; + } + } + + + private class GradeBadge : Container + { + private const float width = 50; + private readonly string grade; + private readonly Sprite badge; + private readonly SpriteText numberText; + + public int DisplayCount + { + set { numberText.Text = value.ToString(@"#,0"); } + } + + public GradeBadge(string grade) + { + this.grade = grade; + Width = width; + Height = 41; + Add(badge = new Sprite + { + Width = width, + Height = 26 + }); + Add(numberText = new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + TextSize = 14, + Font = @"Exo2.0-Bold" + }); + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + badge.Texture = textures.Get($"Grades/{grade}"); + } + } + } +} diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs index 1c23fa575e..c408f69cd7 100644 --- a/osu.Game/Overlays/Profile/ProfileSection.cs +++ b/osu.Game/Overlays/Profile/ProfileSection.cs @@ -1,79 +1,79 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Users; -using OpenTK.Graphics; -using osu.Framework.Configuration; - -namespace osu.Game.Overlays.Profile -{ - public abstract class ProfileSection : FillFlowContainer - { - public abstract string Title { get; } - - public abstract string Identifier { get; } - - private readonly FillFlowContainer content; - - protected override Container Content => content; - - public readonly Bindable User = new Bindable(); - - protected ProfileSection() - { - Direction = FillDirection.Vertical; - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - InternalChildren = new Drawable[] - { - new OsuSpriteText - { - Text = Title, - TextSize = 20, - Font = @"Exo2.0-RegularItalic", - Margin = new MarginPadding - { - Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, - Vertical = 10 - } - }, - content = new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Padding = new MarginPadding - { - Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, - Bottom = 20 - } - }, - new Box - { - RelativeSizeAxes = Axes.X, - Height = 1, - Colour = OsuColour.Gray(34), - EdgeSmoothness = new Vector2(1) - } - }; - - // placeholder - Add(new OsuSpriteText - { - Text = @"coming soon!", - TextSize = 16, - Font = @"Exo2.0-Medium", - Colour = Color4.Gray, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Top = 100, Bottom = 100 } - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using OpenTK.Graphics; +using osu.Framework.Configuration; + +namespace osu.Game.Overlays.Profile +{ + public abstract class ProfileSection : FillFlowContainer + { + public abstract string Title { get; } + + public abstract string Identifier { get; } + + private readonly FillFlowContainer content; + + protected override Container Content => content; + + public readonly Bindable User = new Bindable(); + + protected ProfileSection() + { + Direction = FillDirection.Vertical; + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + InternalChildren = new Drawable[] + { + new OsuSpriteText + { + Text = Title, + TextSize = 20, + Font = @"Exo2.0-RegularItalic", + Margin = new MarginPadding + { + Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, + Vertical = 10 + } + }, + content = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding + { + Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, + Bottom = 20 + } + }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + Colour = OsuColour.Gray(34), + EdgeSmoothness = new Vector2(1) + } + }; + + // placeholder + Add(new OsuSpriteText + { + Text = @"coming soon!", + TextSize = 16, + Font = @"Exo2.0-Medium", + Colour = Color4.Gray, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Top = 100, Bottom = 100 } + }); + } + } +} diff --git a/osu.Game/Overlays/Profile/RankGraph.cs b/osu.Game/Overlays/Profile/RankGraph.cs index 369bdee65f..72dd4352f6 100644 --- a/osu.Game/Overlays/Profile/RankGraph.cs +++ b/osu.Game/Overlays/Profile/RankGraph.cs @@ -1,219 +1,219 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Users; -using System.Collections.Generic; -using osu.Framework.Configuration; - -namespace osu.Game.Overlays.Profile -{ - public class RankGraph : Container - { - private const float primary_textsize = 25; - private const float secondary_textsize = 13; - private const float padding = 10; - private const float fade_duration = 150; - private const int ranked_days = 88; - - private readonly SpriteText rankText, performanceText, relativeText; - private readonly RankChartLineGraph graph; - private readonly OsuSpriteText placeholder; - - private KeyValuePair[] ranks; - public Bindable User = new Bindable(); - - public RankGraph() - { - Padding = new MarginPadding { Vertical = padding }; - Children = new Drawable[] - { - placeholder = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "No recent plays", - TextSize = 14, - Font = @"Exo2.0-RegularItalic", - }, - rankText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = @"Exo2.0-RegularItalic", - TextSize = primary_textsize - }, - relativeText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = @"Exo2.0-RegularItalic", - Y = 25, - TextSize = secondary_textsize - }, - performanceText = new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Font = @"Exo2.0-RegularItalic", - TextSize = secondary_textsize - }, - graph = new RankChartLineGraph - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 75, - Y = -secondary_textsize, - Alpha = 0, - } - }; - - graph.OnBallMove += showHistoryRankTexts; - - User.ValueChanged += userChanged; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - graph.Colour = colours.Yellow; - } - - private void userChanged(User user) - { - placeholder.FadeIn(fade_duration, Easing.Out); - - if (user?.Statistics?.Ranks.Global == null) - { - rankText.Text = string.Empty; - performanceText.Text = string.Empty; - relativeText.Text = string.Empty; - graph.FadeOut(fade_duration, Easing.Out); - ranks = null; - return; - } - - int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global.Value }; - ranks = userRanks.Select((x, index) => new KeyValuePair(index, x)).Where(x => x.Value != 0).ToArray(); - - if (ranks.Length > 1) - { - placeholder.FadeOut(fade_duration, Easing.Out); - - graph.DefaultValueCount = ranks.Length; - graph.Values = ranks.Select(x => -(float)Math.Log(x.Value)); - graph.SetStaticBallPosition(); - } - - graph.FadeTo(ranks.Length > 1 ? 1 : 0, fade_duration, Easing.Out); - - updateRankTexts(); - } - - private void updateRankTexts() - { - var user = User.Value; - - performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty; - rankText.Text = user.Statistics.Ranks.Global > 0 ? $"#{user.Statistics.Ranks.Global:#,0}" : "no rank"; - relativeText.Text = user.Country != null && user.Statistics.Ranks.Country > 0 ? $"{user.Country.FullName} #{user.Statistics.Ranks.Country:#,0}" : "no rank"; - } - - private void showHistoryRankTexts(int dayIndex) - { - rankText.Text = $"#{ranks[dayIndex].Value:#,0}"; - relativeText.Text = dayIndex + 1 == ranks.Length ? "Now" : $"{ranked_days - ranks[dayIndex].Key} days ago"; - } - - protected override bool OnHover(InputState state) - { - if (ranks?.Length > 1) - { - graph.UpdateBallPosition(state.Mouse.Position.X); - graph.ShowBall(); - } - return base.OnHover(state); - } - - protected override bool OnMouseMove(InputState state) - { - if (ranks?.Length > 1) - graph.UpdateBallPosition(state.Mouse.Position.X); - - return base.OnMouseMove(state); - } - - protected override void OnHoverLost(InputState state) - { - if (ranks?.Length > 1) - { - graph.HideBall(); - updateRankTexts(); - } - - base.OnHoverLost(state); - } - - private class RankChartLineGraph : LineGraph - { - private readonly CircularContainer staticBall; - private readonly CircularContainer movingBall; - - public Action OnBallMove; - - public RankChartLineGraph() - { - Add(staticBall = new CircularContainer - { - Origin = Anchor.Centre, - Size = new Vector2(8), - Masking = true, - RelativePositionAxes = Axes.Both, - Child = new Box { RelativeSizeAxes = Axes.Both } - }); - Add(movingBall = new CircularContainer - { - Origin = Anchor.Centre, - Size = new Vector2(8), - Alpha = 0, - Masking = true, - RelativePositionAxes = Axes.Both, - Child = new Box { RelativeSizeAxes = Axes.Both } - }); - } - - public void SetStaticBallPosition() => staticBall.Position = new Vector2(1, GetYPosition(Values.Last())); - - public void UpdateBallPosition(float mouseXPosition) - { - int index = calculateIndex(mouseXPosition); - movingBall.Position = calculateBallPosition(index); - OnBallMove.Invoke(index); - } - - public void ShowBall() => movingBall.FadeIn(fade_duration); - - public void HideBall() => movingBall.FadeOut(fade_duration); - - private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1)); - - private Vector2 calculateBallPosition(int index) - { - float y = GetYPosition(Values.ElementAt(index)); - return new Vector2(index / (float)(DefaultValueCount - 1), y); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Users; +using System.Collections.Generic; +using osu.Framework.Configuration; + +namespace osu.Game.Overlays.Profile +{ + public class RankGraph : Container + { + private const float primary_textsize = 25; + private const float secondary_textsize = 13; + private const float padding = 10; + private const float fade_duration = 150; + private const int ranked_days = 88; + + private readonly SpriteText rankText, performanceText, relativeText; + private readonly RankChartLineGraph graph; + private readonly OsuSpriteText placeholder; + + private KeyValuePair[] ranks; + public Bindable User = new Bindable(); + + public RankGraph() + { + Padding = new MarginPadding { Vertical = padding }; + Children = new Drawable[] + { + placeholder = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "No recent plays", + TextSize = 14, + Font = @"Exo2.0-RegularItalic", + }, + rankText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = @"Exo2.0-RegularItalic", + TextSize = primary_textsize + }, + relativeText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = @"Exo2.0-RegularItalic", + Y = 25, + TextSize = secondary_textsize + }, + performanceText = new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Font = @"Exo2.0-RegularItalic", + TextSize = secondary_textsize + }, + graph = new RankChartLineGraph + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 75, + Y = -secondary_textsize, + Alpha = 0, + } + }; + + graph.OnBallMove += showHistoryRankTexts; + + User.ValueChanged += userChanged; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + graph.Colour = colours.Yellow; + } + + private void userChanged(User user) + { + placeholder.FadeIn(fade_duration, Easing.Out); + + if (user?.Statistics?.Ranks.Global == null) + { + rankText.Text = string.Empty; + performanceText.Text = string.Empty; + relativeText.Text = string.Empty; + graph.FadeOut(fade_duration, Easing.Out); + ranks = null; + return; + } + + int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global.Value }; + ranks = userRanks.Select((x, index) => new KeyValuePair(index, x)).Where(x => x.Value != 0).ToArray(); + + if (ranks.Length > 1) + { + placeholder.FadeOut(fade_duration, Easing.Out); + + graph.DefaultValueCount = ranks.Length; + graph.Values = ranks.Select(x => -(float)Math.Log(x.Value)); + graph.SetStaticBallPosition(); + } + + graph.FadeTo(ranks.Length > 1 ? 1 : 0, fade_duration, Easing.Out); + + updateRankTexts(); + } + + private void updateRankTexts() + { + var user = User.Value; + + performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty; + rankText.Text = user.Statistics.Ranks.Global > 0 ? $"#{user.Statistics.Ranks.Global:#,0}" : "no rank"; + relativeText.Text = user.Country != null && user.Statistics.Ranks.Country > 0 ? $"{user.Country.FullName} #{user.Statistics.Ranks.Country:#,0}" : "no rank"; + } + + private void showHistoryRankTexts(int dayIndex) + { + rankText.Text = $"#{ranks[dayIndex].Value:#,0}"; + relativeText.Text = dayIndex + 1 == ranks.Length ? "Now" : $"{ranked_days - ranks[dayIndex].Key} days ago"; + } + + protected override bool OnHover(InputState state) + { + if (ranks?.Length > 1) + { + graph.UpdateBallPosition(state.Mouse.Position.X); + graph.ShowBall(); + } + return base.OnHover(state); + } + + protected override bool OnMouseMove(InputState state) + { + if (ranks?.Length > 1) + graph.UpdateBallPosition(state.Mouse.Position.X); + + return base.OnMouseMove(state); + } + + protected override void OnHoverLost(InputState state) + { + if (ranks?.Length > 1) + { + graph.HideBall(); + updateRankTexts(); + } + + base.OnHoverLost(state); + } + + private class RankChartLineGraph : LineGraph + { + private readonly CircularContainer staticBall; + private readonly CircularContainer movingBall; + + public Action OnBallMove; + + public RankChartLineGraph() + { + Add(staticBall = new CircularContainer + { + Origin = Anchor.Centre, + Size = new Vector2(8), + Masking = true, + RelativePositionAxes = Axes.Both, + Child = new Box { RelativeSizeAxes = Axes.Both } + }); + Add(movingBall = new CircularContainer + { + Origin = Anchor.Centre, + Size = new Vector2(8), + Alpha = 0, + Masking = true, + RelativePositionAxes = Axes.Both, + Child = new Box { RelativeSizeAxes = Axes.Both } + }); + } + + public void SetStaticBallPosition() => staticBall.Position = new Vector2(1, GetYPosition(Values.Last())); + + public void UpdateBallPosition(float mouseXPosition) + { + int index = calculateIndex(mouseXPosition); + movingBall.Position = calculateBallPosition(index); + OnBallMove.Invoke(index); + } + + public void ShowBall() => movingBall.FadeIn(fade_duration); + + public void HideBall() => movingBall.FadeOut(fade_duration); + + private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1)); + + private Vector2 calculateBallPosition(int index) + { + float y = GetYPosition(Values.ElementAt(index)); + return new Vector2(index / (float)(DefaultValueCount - 1), y); + } + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/AboutSection.cs b/osu.Game/Overlays/Profile/Sections/AboutSection.cs index 5c56355d65..ba3ab80a0c 100644 --- a/osu.Game/Overlays/Profile/Sections/AboutSection.cs +++ b/osu.Game/Overlays/Profile/Sections/AboutSection.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Overlays.Profile.Sections -{ - public class AboutSection : ProfileSection - { - public override string Title => "me!"; - - public override string Identifier => "me"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Overlays.Profile.Sections +{ + public class AboutSection : ProfileSection + { + public override string Title => "me!"; + + public override string Identifier => "me"; + } +} diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index 8a40f0a489..da08c08179 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -1,63 +1,63 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Localisation; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -namespace osu.Game.Overlays.Profile.Sections -{ - /// - /// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row (see ). - /// - public class BeatmapMetadataContainer : OsuHoverContainer, IHasTooltip - { - private readonly BeatmapInfo beatmap; - - public BeatmapMetadataContainer(BeatmapInfo beatmap) - { - this.beatmap = beatmap; - AutoSizeAxes = Axes.Both; - TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}"; - } - - public string TooltipText { get; } - - [BackgroundDependencyLoader(true)] - private void load(LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) - { - Action = () => - { - if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); - }; - - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Current = locale.GetUnicodePreference( - $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", - $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] " - ), - TextSize = 15, - Font = "Exo2.0-SemiBoldItalic", - }, - new OsuSpriteText - { - Current = locale.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist), - TextSize = 12, - Padding = new MarginPadding { Top = 3 }, - Font = "Exo2.0-RegularItalic", - }, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +namespace osu.Game.Overlays.Profile.Sections +{ + /// + /// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row (see ). + /// + public class BeatmapMetadataContainer : OsuHoverContainer, IHasTooltip + { + private readonly BeatmapInfo beatmap; + + public BeatmapMetadataContainer(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + AutoSizeAxes = Axes.Both; + TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}"; + } + + public string TooltipText { get; } + + [BackgroundDependencyLoader(true)] + private void load(LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) + { + Action = () => + { + if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); + }; + + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Current = locale.GetUnicodePreference( + $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", + $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] " + ), + TextSize = 15, + Font = "Exo2.0-SemiBoldItalic", + }, + new OsuSpriteText + { + Current = locale.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist), + TextSize = 12, + Padding = new MarginPadding { Top = 3 }, + Font = "Exo2.0-RegularItalic", + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index da070d1cc2..842a13f8da 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -1,72 +1,72 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Direct; -using osu.Game.Users; -using System.Linq; - -namespace osu.Game.Overlays.Profile.Sections.Beatmaps -{ - public class PaginatedBeatmapContainer : PaginatedContainer - { - private const float panel_padding = 10f; - - private readonly BeatmapSetType type; - - private DirectPanel currentlyPlaying; - - public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") - : base(user, header, missing) - { - this.type = type; - - ItemsPerPage = 6; - - ItemsContainer.Spacing = new Vector2(panel_padding); - } - - protected override void ShowMore() - { - base.ShowMore(); - - var req = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); - - req.Success += sets => - { - ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); - ShowMoreLoading.Hide(); - - if (!sets.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - foreach (var s in sets) - { - if (!s.OnlineBeatmapSetID.HasValue) - continue; - - var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets)); - ItemsContainer.Add(panel); - - panel.PreviewPlaying.ValueChanged += isPlaying => - { - if (!isPlaying) return; - - if (currentlyPlaying != null && currentlyPlaying != panel) - currentlyPlaying.PreviewPlaying.Value = false; - - currentlyPlaying = panel; - }; - } - }; - - Api.Queue(req); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Direct; +using osu.Game.Users; +using System.Linq; + +namespace osu.Game.Overlays.Profile.Sections.Beatmaps +{ + public class PaginatedBeatmapContainer : PaginatedContainer + { + private const float panel_padding = 10f; + + private readonly BeatmapSetType type; + + private DirectPanel currentlyPlaying; + + public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") + : base(user, header, missing) + { + this.type = type; + + ItemsPerPage = 6; + + ItemsContainer.Spacing = new Vector2(panel_padding); + } + + protected override void ShowMore() + { + base.ShowMore(); + + var req = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + + req.Success += sets => + { + ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); + + if (!sets.Any() && VisiblePages == 1) + { + MissingText.Show(); + return; + } + + foreach (var s in sets) + { + if (!s.OnlineBeatmapSetID.HasValue) + continue; + + var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets)); + ItemsContainer.Add(panel); + + panel.PreviewPlaying.ValueChanged += isPlaying => + { + if (!isPlaying) return; + + if (currentlyPlaying != null && currentlyPlaying != panel) + currentlyPlaying.PreviewPlaying.Value = false; + + currentlyPlaying = panel; + }; + } + }; + + Api.Queue(req); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 760054716f..367d096c16 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Profile.Sections.Beatmaps; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class BeatmapsSection : ProfileSection - { - public override string Title => "Beatmaps"; - - public override string Identifier => "beatmaps"; - - public BeatmapsSection() - { - Children = new[] - { - new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections.Beatmaps; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class BeatmapsSection : ProfileSection + { + public override string Title => "Beatmaps"; + + public override string Identifier => "beatmaps"; + + public BeatmapsSection() + { + Children = new[] + { + new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"), + new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"), + new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"), + new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"), + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs index 4956015b3f..3c841cbf14 100644 --- a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs +++ b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs @@ -1,124 +1,124 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Profile.Sections -{ - public abstract class DrawableProfileRow : Container - { - private const int fade_duration = 200; - - private Box underscoreLine; - private readonly Box coloredBackground; - private readonly Container background; - - /// - /// A visual element displayed to the left of content. - /// - protected abstract Drawable CreateLeftVisual(); - - protected FillFlowContainer LeftFlowContainer { get; private set; } - protected FillFlowContainer RightFlowContainer { get; private set; } - - protected override Container Content { get; } - - protected DrawableProfileRow() - { - RelativeSizeAxes = Axes.X; - Height = 60; - InternalChildren = new Drawable[] - { - background = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 3, - Alpha = 0, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 1f, - Colour = Color4.Black.Opacity(0.2f), - }, - Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both } - }, - Content = new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Width = 0.97f, - }, - }; - } - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colour) - { - AddRange(new Drawable[] - { - underscoreLine = new Box - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 1, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new[] - { - CreateLeftVisual(), - LeftFlowContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 10 }, - Direction = FillDirection.Vertical, - }, - } - }, - RightFlowContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Direction = FillDirection.Vertical, - }, - }); - - coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; - } - - protected override bool OnClick(InputState state) => true; - - protected override bool OnHover(InputState state) - { - background.FadeIn(fade_duration, Easing.OutQuint); - underscoreLine.FadeOut(fade_duration, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(InputState state) - { - background.FadeOut(fade_duration, Easing.OutQuint); - underscoreLine.FadeIn(fade_duration, Easing.OutQuint); - base.OnHoverLost(state); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Profile.Sections +{ + public abstract class DrawableProfileRow : Container + { + private const int fade_duration = 200; + + private Box underscoreLine; + private readonly Box coloredBackground; + private readonly Container background; + + /// + /// A visual element displayed to the left of content. + /// + protected abstract Drawable CreateLeftVisual(); + + protected FillFlowContainer LeftFlowContainer { get; private set; } + protected FillFlowContainer RightFlowContainer { get; private set; } + + protected override Container Content { get; } + + protected DrawableProfileRow() + { + RelativeSizeAxes = Axes.X; + Height = 60; + InternalChildren = new Drawable[] + { + background = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 3, + Alpha = 0, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 1f, + Colour = Color4.Black.Opacity(0.2f), + }, + Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both } + }, + Content = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 0.97f, + }, + }; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colour) + { + AddRange(new Drawable[] + { + underscoreLine = new Box + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 1, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + CreateLeftVisual(), + LeftFlowContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10 }, + Direction = FillDirection.Vertical, + }, + } + }, + RightFlowContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Vertical, + }, + }); + + coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; + } + + protected override bool OnClick(InputState state) => true; + + protected override bool OnHover(InputState state) + { + background.FadeIn(fade_duration, Easing.OutQuint); + underscoreLine.FadeOut(fade_duration, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + background.FadeOut(fade_duration, Easing.OutQuint); + underscoreLine.FadeIn(fade_duration, Easing.OutQuint); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs index 2f8f8aa0ae..be8e09105b 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs @@ -1,105 +1,105 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using OpenTK; - -namespace osu.Game.Overlays.Profile.Sections.Historical -{ - public class DrawableMostPlayedRow : DrawableProfileRow - { - private readonly BeatmapInfo beatmap; - private readonly int playCount; - private OsuHoverContainer mapperContainer; - - public DrawableMostPlayedRow(BeatmapInfo beatmap, int playCount) - { - this.beatmap = beatmap; - this.playCount = playCount; - } - - protected override Drawable CreateLeftVisual() => new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - OnLoadComplete = d => d.FadeInFromZero(500, Easing.OutQuint) - }) - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - RelativeSizeAxes = Axes.None, - Size = new Vector2(80, 50), - }; - - [BackgroundDependencyLoader(true)] - private void load(UserProfileOverlay profileOverlay) - { - LeftFlowContainer.Add(new BeatmapMetadataContainer(beatmap)); - LeftFlowContainer.Add(new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = @"mapped by ", - TextSize = 12, - }, - mapperContainer = new OsuHoverContainer - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = beatmap.Metadata.AuthorString, - TextSize = 12, - Font = @"Exo2.0-MediumItalic" - } - } - }, - } - }); - - RightFlowContainer.Add(new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new[] - { - new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Text = playCount.ToString(), - TextSize = 18, - Font = @"Exo2.0-SemiBoldItalic" - }, - new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Text = @"times played ", - TextSize = 12, - Font = @"Exo2.0-RegularItalic" - }, - } - }); - - if (profileOverlay != null) - mapperContainer.Action = () => profileOverlay.ShowUser(beatmap.BeatmapSet.Metadata.Author); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using OpenTK; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class DrawableMostPlayedRow : DrawableProfileRow + { + private readonly BeatmapInfo beatmap; + private readonly int playCount; + private OsuHoverContainer mapperContainer; + + public DrawableMostPlayedRow(BeatmapInfo beatmap, int playCount) + { + this.beatmap = beatmap; + this.playCount = playCount; + } + + protected override Drawable CreateLeftVisual() => new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + OnLoadComplete = d => d.FadeInFromZero(500, Easing.OutQuint) + }) + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + RelativeSizeAxes = Axes.None, + Size = new Vector2(80, 50), + }; + + [BackgroundDependencyLoader(true)] + private void load(UserProfileOverlay profileOverlay) + { + LeftFlowContainer.Add(new BeatmapMetadataContainer(beatmap)); + LeftFlowContainer.Add(new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"mapped by ", + TextSize = 12, + }, + mapperContainer = new OsuHoverContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = beatmap.Metadata.AuthorString, + TextSize = 12, + Font = @"Exo2.0-MediumItalic" + } + } + }, + } + }); + + RightFlowContainer.Add(new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = playCount.ToString(), + TextSize = 18, + Font = @"Exo2.0-SemiBoldItalic" + }, + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = @"times played ", + TextSize = 12, + Font = @"Exo2.0-RegularItalic" + }, + } + }); + + if (profileOverlay != null) + mapperContainer.Action = () => profileOverlay.ShowUser(beatmap.BeatmapSet.Metadata.Author); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 176b4bc30a..42784682be 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -1,51 +1,51 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Online.API.Requests; -using osu.Game.Users; - -namespace osu.Game.Overlays.Profile.Sections.Historical -{ - public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer - { - public PaginatedMostPlayedBeatmapContainer(Bindable user) - :base(user, "Most Played Beatmaps", "No records. :(") - { - ItemsPerPage = 5; - - ItemsContainer.Direction = FillDirection.Vertical; - } - - protected override void ShowMore() - { - base.ShowMore(); - - var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); - - req.Success += beatmaps => - { - ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); - ShowMoreLoading.Hide(); - - if (!beatmaps.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - MissingText.Hide(); - - foreach (var beatmap in beatmaps) - { - ItemsContainer.Add(new DrawableMostPlayedRow(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); - } - }; - - Api.Queue(req); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API.Requests; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer + { + public PaginatedMostPlayedBeatmapContainer(Bindable user) + :base(user, "Most Played Beatmaps", "No records. :(") + { + ItemsPerPage = 5; + + ItemsContainer.Direction = FillDirection.Vertical; + } + + protected override void ShowMore() + { + base.ShowMore(); + + var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + + req.Success += beatmaps => + { + ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); + + if (!beatmaps.Any() && VisiblePages == 1) + { + MissingText.Show(); + return; + } + + MissingText.Hide(); + + foreach (var beatmap in beatmaps) + { + ItemsContainer.Add(new DrawableMostPlayedRow(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); + } + }; + + Api.Queue(req); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index 82b520cf10..924b27a86d 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Profile.Sections.Historical; -using osu.Game.Overlays.Profile.Sections.Ranks; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class HistoricalSection : ProfileSection - { - public override string Title => "Historical"; - - public override string Identifier => "historical"; - - public HistoricalSection() - { - Children = new Drawable[] - { - new PaginatedMostPlayedBeatmapContainer(User), - new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections.Historical; +using osu.Game.Overlays.Profile.Sections.Ranks; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class HistoricalSection : ProfileSection + { + public override string Title => "Historical"; + + public override string Identifier => "historical"; + + public HistoricalSection() + { + Children = new Drawable[] + { + new PaginatedMostPlayedBeatmapContainer(User), + new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("), + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 896ffc33f6..15b446efa5 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -1,135 +1,135 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Users; - -namespace osu.Game.Overlays.Profile.Sections.Kudosu -{ - public class KudosuInfo : Container - { - private readonly Bindable user = new Bindable(); - - public KudosuInfo(Bindable user) - { - this.user.BindTo(user); - - CountSection total; - CountSection avaliable; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Masking = true; - CornerRadius = 3; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 3f, - Colour = Color4.Black.Opacity(0.2f), - }; - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f) - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - total = new CountSection( - "Total Kudosu Earned", - "Based on how much of a contribution the user has made to beatmap moderation. See this link for more information." - ), - avaliable = new CountSection( - "Kudosu Avaliable", - "Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet." - ), - } - } - }; - - this.user.ValueChanged += newUser => - { - total.Count = newUser?.Kudosu.Total ?? 0; - avaliable.Count = newUser?.Kudosu.Available ?? 0; - }; - } - - protected override bool OnClick(InputState state) => true; - - private class CountSection : Container - { - private readonly OsuSpriteText valueText; - - public new int Count - { - set { valueText.Text = value.ToString(); } - } - - public CountSection(string header, string description) - { - RelativeSizeAxes = Axes.X; - Width = 0.5f; - AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Horizontal = 10, Top = 10, Bottom = 20 }; - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5, 0), - Children = new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Text = header + ":", - TextSize = 20, - Font = @"Exo2.0-RegularItalic", - }, - valueText = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Text = "0", - TextSize = 40, - UseFullGlyphHeight = false, - Font = @"Exo2.0-RegularItalic" - } - } - }, - new OsuTextFlowContainer(t => { t.TextSize = 19; }) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Text = description - } - } - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Sections.Kudosu +{ + public class KudosuInfo : Container + { + private readonly Bindable user = new Bindable(); + + public KudosuInfo(Bindable user) + { + this.user.BindTo(user); + + CountSection total; + CountSection avaliable; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Masking = true; + CornerRadius = 3; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 3f, + Colour = Color4.Black.Opacity(0.2f), + }; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + total = new CountSection( + "Total Kudosu Earned", + "Based on how much of a contribution the user has made to beatmap moderation. See this link for more information." + ), + avaliable = new CountSection( + "Kudosu Avaliable", + "Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet." + ), + } + } + }; + + this.user.ValueChanged += newUser => + { + total.Count = newUser?.Kudosu.Total ?? 0; + avaliable.Count = newUser?.Kudosu.Available ?? 0; + }; + } + + protected override bool OnClick(InputState state) => true; + + private class CountSection : Container + { + private readonly OsuSpriteText valueText; + + public new int Count + { + set { valueText.Text = value.ToString(); } + } + + public CountSection(string header, string description) + { + RelativeSizeAxes = Axes.X; + Width = 0.5f; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Horizontal = 10, Top = 10, Bottom = 20 }; + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5, 0), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = header + ":", + TextSize = 20, + Font = @"Exo2.0-RegularItalic", + }, + valueText = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = "0", + TextSize = 40, + UseFullGlyphHeight = false, + Font = @"Exo2.0-RegularItalic" + } + } + }, + new OsuTextFlowContainer(t => { t.TextSize = 19; }) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Text = description + } + } + }; + } + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs index 74291c587e..1490a4bda7 100644 --- a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs +++ b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Overlays.Profile.Sections.Kudosu; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class KudosuSection : ProfileSection - { - public override string Title => "Kudosu!"; - - public override string Identifier => "kudosu"; - - public KudosuSection() - { - Children = new[] - { - new KudosuInfo(User), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Overlays.Profile.Sections.Kudosu; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class KudosuSection : ProfileSection + { + public override string Title => "Kudosu!"; + + public override string Identifier => "kudosu"; + + public KudosuSection() + { + Children = new[] + { + new KudosuInfo(User), + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs index fe5543f264..45be08b292 100644 --- a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Overlays.Profile.Sections -{ - public class MedalsSection : ProfileSection - { - public override string Title => "Medals"; - - public override string Identifier => "medals"; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Overlays.Profile.Sections +{ + public class MedalsSection : ProfileSection + { + public override string Title => "Medals"; + + public override string Identifier => "medals"; + } +} diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 3864002367..1e0406c125 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -1,110 +1,110 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Rulesets; -using osu.Game.Users; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class PaginatedContainer : FillFlowContainer - { - protected readonly FillFlowContainer ItemsContainer; - protected readonly OsuHoverContainer ShowMoreButton; - protected readonly LoadingAnimation ShowMoreLoading; - protected readonly OsuSpriteText MissingText; - - protected int VisiblePages; - protected int ItemsPerPage; - - protected readonly Bindable User = new Bindable(); - - protected APIAccess Api; - protected RulesetStore Rulesets; - - public PaginatedContainer(Bindable user, string header, string missing) - { - User.BindTo(user); - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - - Children = new Drawable[] - { - new OsuSpriteText - { - TextSize = 15, - Text = header, - Font = "Exo2.0-RegularItalic", - Margin = new MarginPadding { Top = 10, Bottom = 10 }, - }, - ItemsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Bottom = 10 } - }, - ShowMoreButton = new OsuHoverContainer - { - Alpha = 0, - Action = ShowMore, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Child = new OsuSpriteText - { - TextSize = 14, - Text = "show more", - } - }, - ShowMoreLoading = new LoadingAnimation - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Size = new Vector2(14), - }, - MissingText = new OsuSpriteText - { - TextSize = 14, - Text = missing, - Alpha = 0, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(APIAccess api, RulesetStore rulesets) - { - Api = api; - Rulesets = rulesets; - - User.ValueChanged += onUserChanged; - User.TriggerChange(); - } - - private void onUserChanged(User newUser) - { - VisiblePages = 0; - ItemsContainer.Clear(); - ShowMoreButton.Hide(); - - if (newUser != null) - ShowMore(); - } - - protected virtual void ShowMore() - { - ShowMoreLoading.Show(); - ShowMoreButton.Hide(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class PaginatedContainer : FillFlowContainer + { + protected readonly FillFlowContainer ItemsContainer; + protected readonly OsuHoverContainer ShowMoreButton; + protected readonly LoadingAnimation ShowMoreLoading; + protected readonly OsuSpriteText MissingText; + + protected int VisiblePages; + protected int ItemsPerPage; + + protected readonly Bindable User = new Bindable(); + + protected APIAccess Api; + protected RulesetStore Rulesets; + + public PaginatedContainer(Bindable user, string header, string missing) + { + User.BindTo(user); + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + + Children = new Drawable[] + { + new OsuSpriteText + { + TextSize = 15, + Text = header, + Font = "Exo2.0-RegularItalic", + Margin = new MarginPadding { Top = 10, Bottom = 10 }, + }, + ItemsContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Bottom = 10 } + }, + ShowMoreButton = new OsuHoverContainer + { + Alpha = 0, + Action = ShowMore, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Child = new OsuSpriteText + { + TextSize = 14, + Text = "show more", + } + }, + ShowMoreLoading = new LoadingAnimation + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(14), + }, + MissingText = new OsuSpriteText + { + TextSize = 14, + Text = missing, + Alpha = 0, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(APIAccess api, RulesetStore rulesets) + { + Api = api; + Rulesets = rulesets; + + User.ValueChanged += onUserChanged; + User.TriggerChange(); + } + + private void onUserChanged(User newUser) + { + VisiblePages = 0; + ItemsContainer.Clear(); + ShowMoreButton.Hide(); + + if (newUser != null) + ShowMore(); + } + + protected virtual void ShowMore() + { + ShowMoreLoading.Show(); + ShowMoreButton.Hide(); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs index 3b814b0542..ddbe10c05c 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs @@ -1,49 +1,49 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Overlays.Profile.Sections.Ranks -{ - public class DrawablePerformanceScore : DrawableProfileScore - { - private readonly double? weight; - - public DrawablePerformanceScore(Score score, double? weight = null) - : base(score) - { - this.weight = weight; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - double pp = Score.PP ?? 0; - RightFlowContainer.Add(new OsuSpriteText - { - Text = $"{pp:0}pp", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - TextSize = 18, - Font = "Exo2.0-BoldItalic", - }); - - if (weight.HasValue) - { - RightFlowContainer.Add(new OsuSpriteText - { - Text = $"weighted: {pp * weight:0}pp ({weight:P0})", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Colour = colour.GrayA, - TextSize = 11, - Font = "Exo2.0-RegularItalic", - }); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Overlays.Profile.Sections.Ranks +{ + public class DrawablePerformanceScore : DrawableProfileScore + { + private readonly double? weight; + + public DrawablePerformanceScore(Score score, double? weight = null) + : base(score) + { + this.weight = weight; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + double pp = Score.PP ?? 0; + RightFlowContainer.Add(new OsuSpriteText + { + Text = $"{pp:0}pp", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 18, + Font = "Exo2.0-BoldItalic", + }); + + if (weight.HasValue) + { + RightFlowContainer.Add(new OsuSpriteText + { + Text = $"weighted: {pp * weight:0}pp ({weight:P0})", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Colour = colour.GrayA, + TextSize = 11, + Font = "Exo2.0-RegularItalic", + }); + } + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 509356ae04..67e86265ed 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -1,70 +1,70 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Select.Leaderboards; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Overlays.Profile.Sections.Ranks -{ - public abstract class DrawableProfileScore : DrawableProfileRow - { - private readonly ScoreModsContainer modsContainer; - protected readonly Score Score; - - protected DrawableProfileScore(Score score) - { - Score = score; - - RelativeSizeAxes = Axes.X; - Height = 60; - Children = new Drawable[] - { - modsContainer = new ScoreModsContainer - { - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Width = 60, - Margin = new MarginPadding { Right = 160 } - } - }; - } - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colour) - { - var text = new OsuSpriteText - { - Text = $"accuracy: {Score.Accuracy:P2}", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Colour = colour.GrayA, - TextSize = 11, - Font = "Exo2.0-RegularItalic" - }; - - RightFlowContainer.Add(text); - RightFlowContainer.SetLayoutPosition(text, 1); - - LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap)); - LeftFlowContainer.Add(new DrawableDate(Score.Date)); - - foreach (Mod mod in Score.Mods) - modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) }); - } - - protected override Drawable CreateLeftVisual() => new DrawableRank(Score.Rank) - { - RelativeSizeAxes = Axes.Y, - Width = 60, - FillMode = FillMode.Fit, - }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Overlays.Profile.Sections.Ranks +{ + public abstract class DrawableProfileScore : DrawableProfileRow + { + private readonly ScoreModsContainer modsContainer; + protected readonly Score Score; + + protected DrawableProfileScore(Score score) + { + Score = score; + + RelativeSizeAxes = Axes.X; + Height = 60; + Children = new Drawable[] + { + modsContainer = new ScoreModsContainer + { + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 60, + Margin = new MarginPadding { Right = 160 } + } + }; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colour) + { + var text = new OsuSpriteText + { + Text = $"accuracy: {Score.Accuracy:P2}", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Colour = colour.GrayA, + TextSize = 11, + Font = "Exo2.0-RegularItalic" + }; + + RightFlowContainer.Add(text); + RightFlowContainer.SetLayoutPosition(text, 1); + + LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap)); + LeftFlowContainer.Add(new DrawableDate(Score.Date)); + + foreach (Mod mod in Score.Mods) + modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) }); + } + + protected override Drawable CreateLeftVisual() => new DrawableRank(Score.Rank) + { + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs index 61154b2280..3a39ef5d61 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Overlays.Profile.Sections.Ranks -{ - public class DrawableTotalScore : DrawableProfileScore - { - public DrawableTotalScore(Score score) - : base(score) - { - } - - [BackgroundDependencyLoader] - private void load() - { - RightFlowContainer.Add(new OsuSpriteText - { - Text = Score.TotalScore.ToString("#,###"), - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - TextSize = 18, - Font = "Exo2.0-BoldItalic", - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Overlays.Profile.Sections.Ranks +{ + public class DrawableTotalScore : DrawableProfileScore + { + public DrawableTotalScore(Score score) + : base(score) + { + } + + [BackgroundDependencyLoader] + private void load() + { + RightFlowContainer.Add(new OsuSpriteText + { + Text = Score.TotalScore.ToString("#,###"), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 18, + Font = "Exo2.0-BoldItalic", + }); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 8e20953457..87df84e2e5 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -1,73 +1,73 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Online.API.Requests; -using osu.Game.Users; -using System; -using System.Linq; - -namespace osu.Game.Overlays.Profile.Sections.Ranks -{ - public class PaginatedScoreContainer : PaginatedContainer - { - private readonly bool includeWeight; - private readonly ScoreType type; - - public PaginatedScoreContainer(ScoreType type, Bindable user, string header, string missing, bool includeWeight = false) - : base(user, header, missing) - { - this.type = type; - this.includeWeight = includeWeight; - - ItemsPerPage = 5; - - ItemsContainer.Direction = FillDirection.Vertical; - } - - protected override void ShowMore() - { - base.ShowMore(); - - var req = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); - - req.Success += scores => - { - foreach (var s in scores) - s.ApplyRuleset(Rulesets.GetRuleset(s.OnlineRulesetID)); - - ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0); - ShowMoreLoading.Hide(); - - if (!scores.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - MissingText.Hide(); - - foreach (OnlineScore score in scores) - { - DrawableProfileScore drawableScore; - - switch (type) - { - default: - drawableScore = new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); - break; - case ScoreType.Recent: - drawableScore = new DrawableTotalScore(score); - break; - } - - ItemsContainer.Add(drawableScore); - } - }; - - Api.Queue(req); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API.Requests; +using osu.Game.Users; +using System; +using System.Linq; + +namespace osu.Game.Overlays.Profile.Sections.Ranks +{ + public class PaginatedScoreContainer : PaginatedContainer + { + private readonly bool includeWeight; + private readonly ScoreType type; + + public PaginatedScoreContainer(ScoreType type, Bindable user, string header, string missing, bool includeWeight = false) + : base(user, header, missing) + { + this.type = type; + this.includeWeight = includeWeight; + + ItemsPerPage = 5; + + ItemsContainer.Direction = FillDirection.Vertical; + } + + protected override void ShowMore() + { + base.ShowMore(); + + var req = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + + req.Success += scores => + { + foreach (var s in scores) + s.ApplyRuleset(Rulesets.GetRuleset(s.OnlineRulesetID)); + + ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); + + if (!scores.Any() && VisiblePages == 1) + { + MissingText.Show(); + return; + } + + MissingText.Hide(); + + foreach (OnlineScore score in scores) + { + DrawableProfileScore drawableScore; + + switch (type) + { + default: + drawableScore = new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); + break; + case ScoreType.Recent: + drawableScore = new DrawableTotalScore(score); + break; + } + + ItemsContainer.Add(drawableScore); + } + }; + + Api.Queue(req); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs index 59b16c0987..90f476fa73 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.UI; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Overlays.Profile.Sections.Ranks -{ - public class ScoreModsContainer : FlowContainer - { - protected override IEnumerable ComputeLayoutPositions() - { - int count = FlowingChildren.Count(); - for (int i = 0; i < count; i++) - yield return new Vector2(DrawWidth * i * (count == 1 ? 0 : 1f / (count - 1)), 0); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.UI; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Overlays.Profile.Sections.Ranks +{ + public class ScoreModsContainer : FlowContainer + { + protected override IEnumerable ComputeLayoutPositions() + { + int count = FlowingChildren.Count(); + for (int i = 0; i < count; i++) + yield return new Vector2(DrawWidth * i * (count == 1 ? 0 : 1f / (count - 1)), 0); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs index 64687284cb..965f668a68 100644 --- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Overlays.Profile.Sections.Ranks; -using osu.Game.Online.API.Requests; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class RanksSection : ProfileSection - { - public override string Title => "Ranks"; - - public override string Identifier => "top_ranks"; - - public RanksSection() - { - Children = new[] - { - new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :(", true), - new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks", "No awesome performance records yet. :("), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Overlays.Profile.Sections.Ranks; +using osu.Game.Online.API.Requests; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class RanksSection : ProfileSection + { + public override string Title => "Ranks"; + + public override string Identifier => "top_ranks"; + + public RanksSection() + { + Children = new[] + { + new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :(", true), + new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks", "No awesome performance records yet. :("), + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index e8be8d1e44..721cc30b61 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -1,162 +1,162 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Online.Chat; -using osu.Game.Screens.Select.Leaderboards; - -namespace osu.Game.Overlays.Profile.Sections.Recent -{ - public class DrawableRecentActivity : DrawableProfileRow - { - private APIAccess api; - - private readonly RecentActivity activity; - - private LinkFlowContainer content; - - public DrawableRecentActivity(RecentActivity activity) - { - this.activity = activity; - } - - [BackgroundDependencyLoader] - private void load(APIAccess api) - { - this.api = api; - - LeftFlowContainer.Padding = new MarginPadding { Left = 10, Right = 160 }; - - LeftFlowContainer.Add(content = new LinkFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }); - - RightFlowContainer.Add(new DrawableDate(activity.CreatedAt) - { - TextSize = 13, - Colour = OsuColour.Gray(0xAA), - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }); - - var formatted = createMessage(); - - content.AddLinks(formatted.Text, formatted.Links); - } - - protected override Drawable CreateLeftVisual() - { - switch (activity.Type) - { - case RecentActivityType.Rank: - return new DrawableRank(activity.ScoreRank) - { - RelativeSizeAxes = Axes.Y, - Width = 60, - FillMode = FillMode.Fit, - }; - - case RecentActivityType.Achievement: - return new MedalIcon(activity.Achievement.Slug) - { - RelativeSizeAxes = Axes.Y, - Width = 60, - FillMode = FillMode.Fit, - }; - - default: - return new Container - { - RelativeSizeAxes = Axes.Y, - Width = 60, - FillMode = FillMode.Fit, - }; - } - } - - private string toAbsoluteUrl(string url) => $"{api.Endpoint}{url}"; - - private MessageFormatter.MessageFormatterResult createMessage() - { - string userLinkTemplate() => $"[{toAbsoluteUrl(activity.User?.Url)} {activity.User?.Username}]"; - string beatmapLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmap?.Url)} {activity.Beatmap?.Title}]"; - string beatmapsetLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmapset?.Url)} {activity.Beatmapset?.Title}]"; - - string message; - - switch (activity.Type) - { - case RecentActivityType.Achievement: - message = $"{userLinkTemplate()} unlocked the {activity.Achievement.Name} medal!"; - break; - - case RecentActivityType.BeatmapPlaycount: - message = $"{beatmapLinkTemplate()} has been played {activity.Count} times!"; - break; - - case RecentActivityType.BeatmapsetApprove: - message = $"{beatmapsetLinkTemplate()} has been {activity.Approval.ToString().ToLowerInvariant()}!"; - break; - - case RecentActivityType.BeatmapsetDelete: - message = $"{beatmapsetLinkTemplate()} has been deleted."; - break; - - case RecentActivityType.BeatmapsetRevive: - message = $"{beatmapsetLinkTemplate()} has been revived from eternal slumber by {userLinkTemplate()}."; - break; - - case RecentActivityType.BeatmapsetUpdate: - message = $"{userLinkTemplate()} has updated the beatmap {beatmapsetLinkTemplate()}!"; - break; - - case RecentActivityType.BeatmapsetUpload: - message = $"{userLinkTemplate()} has submitted a new beatmap {beatmapsetLinkTemplate()}!"; - break; - - case RecentActivityType.Medal: - // apparently this shouldn't exist look at achievement instead (https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/profile-page/recent-activity.coffee#L111) - message = string.Empty; - break; - - case RecentActivityType.Rank: - message = $"{userLinkTemplate()} achieved rank #{activity.Rank} on {beatmapLinkTemplate()} ({activity.Mode}!)"; - break; - - case RecentActivityType.RankLost: - message = $"{userLinkTemplate()} has lost first place on {beatmapLinkTemplate()} ({activity.Mode}!)"; - break; - - case RecentActivityType.UserSupportAgain: - message = $"{userLinkTemplate()} has once again chosen to support osu! - thanks for your generosity!"; - break; - - case RecentActivityType.UserSupportFirst: - message = $"{userLinkTemplate()} has become an osu! supporter - thanks for your generosity!"; - break; - - case RecentActivityType.UserSupportGift: - message = $"{userLinkTemplate()} has received the gift of osu! supporter!"; - break; - - case RecentActivityType.UsernameChange: - message = $"{activity.User?.PreviousUsername} has changed their username to {userLinkTemplate()}!"; - break; - - default: - message = string.Empty; - break; - } - - return MessageFormatter.FormatText(message); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Chat; +using osu.Game.Screens.Select.Leaderboards; + +namespace osu.Game.Overlays.Profile.Sections.Recent +{ + public class DrawableRecentActivity : DrawableProfileRow + { + private APIAccess api; + + private readonly RecentActivity activity; + + private LinkFlowContainer content; + + public DrawableRecentActivity(RecentActivity activity) + { + this.activity = activity; + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + + LeftFlowContainer.Padding = new MarginPadding { Left = 10, Right = 160 }; + + LeftFlowContainer.Add(content = new LinkFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }); + + RightFlowContainer.Add(new DrawableDate(activity.CreatedAt) + { + TextSize = 13, + Colour = OsuColour.Gray(0xAA), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }); + + var formatted = createMessage(); + + content.AddLinks(formatted.Text, formatted.Links); + } + + protected override Drawable CreateLeftVisual() + { + switch (activity.Type) + { + case RecentActivityType.Rank: + return new DrawableRank(activity.ScoreRank) + { + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; + + case RecentActivityType.Achievement: + return new MedalIcon(activity.Achievement.Slug) + { + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; + + default: + return new Container + { + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; + } + } + + private string toAbsoluteUrl(string url) => $"{api.Endpoint}{url}"; + + private MessageFormatter.MessageFormatterResult createMessage() + { + string userLinkTemplate() => $"[{toAbsoluteUrl(activity.User?.Url)} {activity.User?.Username}]"; + string beatmapLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmap?.Url)} {activity.Beatmap?.Title}]"; + string beatmapsetLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmapset?.Url)} {activity.Beatmapset?.Title}]"; + + string message; + + switch (activity.Type) + { + case RecentActivityType.Achievement: + message = $"{userLinkTemplate()} unlocked the {activity.Achievement.Name} medal!"; + break; + + case RecentActivityType.BeatmapPlaycount: + message = $"{beatmapLinkTemplate()} has been played {activity.Count} times!"; + break; + + case RecentActivityType.BeatmapsetApprove: + message = $"{beatmapsetLinkTemplate()} has been {activity.Approval.ToString().ToLowerInvariant()}!"; + break; + + case RecentActivityType.BeatmapsetDelete: + message = $"{beatmapsetLinkTemplate()} has been deleted."; + break; + + case RecentActivityType.BeatmapsetRevive: + message = $"{beatmapsetLinkTemplate()} has been revived from eternal slumber by {userLinkTemplate()}."; + break; + + case RecentActivityType.BeatmapsetUpdate: + message = $"{userLinkTemplate()} has updated the beatmap {beatmapsetLinkTemplate()}!"; + break; + + case RecentActivityType.BeatmapsetUpload: + message = $"{userLinkTemplate()} has submitted a new beatmap {beatmapsetLinkTemplate()}!"; + break; + + case RecentActivityType.Medal: + // apparently this shouldn't exist look at achievement instead (https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/profile-page/recent-activity.coffee#L111) + message = string.Empty; + break; + + case RecentActivityType.Rank: + message = $"{userLinkTemplate()} achieved rank #{activity.Rank} on {beatmapLinkTemplate()} ({activity.Mode}!)"; + break; + + case RecentActivityType.RankLost: + message = $"{userLinkTemplate()} has lost first place on {beatmapLinkTemplate()} ({activity.Mode}!)"; + break; + + case RecentActivityType.UserSupportAgain: + message = $"{userLinkTemplate()} has once again chosen to support osu! - thanks for your generosity!"; + break; + + case RecentActivityType.UserSupportFirst: + message = $"{userLinkTemplate()} has become an osu! supporter - thanks for your generosity!"; + break; + + case RecentActivityType.UserSupportGift: + message = $"{userLinkTemplate()} has received the gift of osu! supporter!"; + break; + + case RecentActivityType.UsernameChange: + message = $"{activity.User?.PreviousUsername} has changed their username to {userLinkTemplate()}!"; + break; + + default: + message = string.Empty; + break; + } + + return MessageFormatter.FormatText(message); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs index 6ffbe7193f..0d354c728f 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs @@ -1,38 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; - -namespace osu.Game.Overlays.Profile.Sections.Recent -{ - public class MedalIcon : Container - { - private readonly string slug; - private readonly Sprite sprite; - - private string url => $@"https://s.ppy.sh/images/medals-client/{slug}@2x.png"; - - public MedalIcon(string slug) - { - this.slug = slug; - - Child = sprite = new Sprite - { - Height = 40, - Width = 40, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - sprite.Texture = textures.Get(url); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Overlays.Profile.Sections.Recent +{ + public class MedalIcon : Container + { + private readonly string slug; + private readonly Sprite sprite; + + private string url => $@"https://s.ppy.sh/images/medals-client/{slug}@2x.png"; + + public MedalIcon(string slug) + { + this.slug = slug; + + Child = sprite = new Sprite + { + Height = 40, + Width = 40, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + sprite.Texture = textures.Get(url); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index d479627cde..8c1108b115 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -1,48 +1,48 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Game.Online.API.Requests; -using osu.Game.Users; -using System.Linq; - -namespace osu.Game.Overlays.Profile.Sections.Recent -{ - public class PaginatedRecentActivityContainer : PaginatedContainer - { - public PaginatedRecentActivityContainer(Bindable user, string header, string missing) - : base(user, header, missing) - { - ItemsPerPage = 5; - } - - protected override void ShowMore() - { - base.ShowMore(); - - var req = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); - - req.Success += activities => - { - ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); - ShowMoreLoading.Hide(); - - if (!activities.Any() && VisiblePages == 1) - { - MissingText.Show(); - return; - } - - MissingText.Hide(); - - foreach (RecentActivity activity in activities) - { - ItemsContainer.Add(new DrawableRecentActivity(activity)); - } - }; - - Api.Queue(req); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Users; +using System.Linq; + +namespace osu.Game.Overlays.Profile.Sections.Recent +{ + public class PaginatedRecentActivityContainer : PaginatedContainer + { + public PaginatedRecentActivityContainer(Bindable user, string header, string missing) + : base(user, header, missing) + { + ItemsPerPage = 5; + } + + protected override void ShowMore() + { + base.ShowMore(); + + var req = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + + req.Success += activities => + { + ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); + + if (!activities.Any() && VisiblePages == 1) + { + MissingText.Show(); + return; + } + + MissingText.Hide(); + + foreach (RecentActivity activity in activities) + { + ItemsContainer.Add(new DrawableRecentActivity(activity)); + } + }; + + Api.Queue(req); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/RecentSection.cs b/osu.Game/Overlays/Profile/Sections/RecentSection.cs index 84a941aa1a..fdccd00bac 100644 --- a/osu.Game/Overlays/Profile/Sections/RecentSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RecentSection.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Overlays.Profile.Sections.Recent; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class RecentSection : ProfileSection - { - public override string Title => "Recent"; - - public override string Identifier => "recent_activity"; - - public RecentSection() - { - Children = new[] - { - new PaginatedRecentActivityContainer(User, null, @"This user hasn't done anything notable recently!"), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Overlays.Profile.Sections.Recent; + +namespace osu.Game.Overlays.Profile.Sections +{ + public class RecentSection : ProfileSection + { + public override string Title => "Recent"; + + public override string Identifier => "recent_activity"; + + public RecentSection() + { + Children = new[] + { + new PaginatedRecentActivityContainer(User, null, @"This user hasn't done anything notable recently!"), + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/SupporterIcon.cs b/osu.Game/Overlays/Profile/SupporterIcon.cs index 8f89dfe2ea..e8d52bf50e 100644 --- a/osu.Game/Overlays/Profile/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/SupporterIcon.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; - -namespace osu.Game.Overlays.Profile -{ - public class SupporterIcon : CircularContainer, IHasTooltip - { - private readonly Box background; - - public string TooltipText => "osu!supporter"; - - public SupporterIcon() - { - Masking = true; - Children = new Drawable[] - { - new Box { RelativeSizeAxes = Axes.Both }, - new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.8f), - Masking = true, - Children = new Drawable[] - { - background = new Box { RelativeSizeAxes = Axes.Both }, - new Triangles - { - TriangleScale = 0.2f, - ColourLight = OsuColour.FromHex(@"ff7db7"), - ColourDark = OsuColour.FromHex(@"de5b95"), - RelativeSizeAxes = Axes.Both, - Velocity = 0.3f, - }, - } - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.fa_heart, - Scale = new Vector2(0.45f), - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.Pink; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; + +namespace osu.Game.Overlays.Profile +{ + public class SupporterIcon : CircularContainer, IHasTooltip + { + private readonly Box background; + + public string TooltipText => "osu!supporter"; + + public SupporterIcon() + { + Masking = true; + Children = new Drawable[] + { + new Box { RelativeSizeAxes = Axes.Both }, + new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.8f), + Masking = true, + Children = new Drawable[] + { + background = new Box { RelativeSizeAxes = Axes.Both }, + new Triangles + { + TriangleScale = 0.2f, + ColourLight = OsuColour.FromHex(@"ff7db7"), + ColourDark = OsuColour.FromHex(@"de5b95"), + RelativeSizeAxes = Axes.Both, + Velocity = 0.3f, + }, + } + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_heart, + Scale = new Vector2(0.45f), + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.Pink; + } + } +} diff --git a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs index 84d28e95ed..26a87230de 100644 --- a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs +++ b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs @@ -1,102 +1,102 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.SearchableList -{ - public class DisplayStyleControl : Container - { - public readonly SlimEnumDropdown Dropdown; - public readonly Bindable DisplayStyle = new Bindable(); - - public DisplayStyleControl() - { - AutoSizeAxes = Axes.Both; - - Children = new[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Spacing = new Vector2(10f, 0f), - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5f, 0f), - Direction = FillDirection.Horizontal, - Children = new[] - { - new DisplayStyleToggleButton(FontAwesome.fa_th_large, PanelDisplayStyle.Grid, DisplayStyle), - new DisplayStyleToggleButton(FontAwesome.fa_list_ul, PanelDisplayStyle.List, DisplayStyle), - }, - }, - Dropdown = new SlimEnumDropdown - { - RelativeSizeAxes = Axes.None, - Width = 160f, - }, - }, - }, - }; - - DisplayStyle.Value = PanelDisplayStyle.Grid; - } - - private class DisplayStyleToggleButton : OsuClickableContainer - { - private readonly SpriteIcon icon; - private readonly PanelDisplayStyle style; - private readonly Bindable bindable; - - public DisplayStyleToggleButton(FontAwesome icon, PanelDisplayStyle style, Bindable bindable) - { - this.bindable = bindable; - this.style = style; - Size = new Vector2(25f); - - Children = new Drawable[] - { - this.icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = icon, - Size = new Vector2(18), - Alpha = 0.5f, - }, - }; - - bindable.ValueChanged += Bindable_ValueChanged; - Bindable_ValueChanged(bindable.Value); - Action = () => bindable.Value = this.style; - } - - private void Bindable_ValueChanged(PanelDisplayStyle style) - { - icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100); - } - - protected override void Dispose(bool isDisposing) - { - bindable.ValueChanged -= Bindable_ValueChanged; - } - } - } - - public enum PanelDisplayStyle - { - Grid, - List, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.SearchableList +{ + public class DisplayStyleControl : Container + { + public readonly SlimEnumDropdown Dropdown; + public readonly Bindable DisplayStyle = new Bindable(); + + public DisplayStyleControl() + { + AutoSizeAxes = Axes.Both; + + Children = new[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Spacing = new Vector2(10f, 0f), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5f, 0f), + Direction = FillDirection.Horizontal, + Children = new[] + { + new DisplayStyleToggleButton(FontAwesome.fa_th_large, PanelDisplayStyle.Grid, DisplayStyle), + new DisplayStyleToggleButton(FontAwesome.fa_list_ul, PanelDisplayStyle.List, DisplayStyle), + }, + }, + Dropdown = new SlimEnumDropdown + { + RelativeSizeAxes = Axes.None, + Width = 160f, + }, + }, + }, + }; + + DisplayStyle.Value = PanelDisplayStyle.Grid; + } + + private class DisplayStyleToggleButton : OsuClickableContainer + { + private readonly SpriteIcon icon; + private readonly PanelDisplayStyle style; + private readonly Bindable bindable; + + public DisplayStyleToggleButton(FontAwesome icon, PanelDisplayStyle style, Bindable bindable) + { + this.bindable = bindable; + this.style = style; + Size = new Vector2(25f); + + Children = new Drawable[] + { + this.icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = icon, + Size = new Vector2(18), + Alpha = 0.5f, + }, + }; + + bindable.ValueChanged += Bindable_ValueChanged; + Bindable_ValueChanged(bindable.Value); + Action = () => bindable.Value = this.style; + } + + private void Bindable_ValueChanged(PanelDisplayStyle style) + { + icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100); + } + + protected override void Dispose(bool isDisposing) + { + bindable.ValueChanged -= Bindable_ValueChanged; + } + } + } + + public enum PanelDisplayStyle + { + Grid, + List, + } +} diff --git a/osu.Game/Overlays/SearchableList/HeaderTabControl.cs b/osu.Game/Overlays/SearchableList/HeaderTabControl.cs index 2dd9d7de29..62685eb323 100644 --- a/osu.Game/Overlays/SearchableList/HeaderTabControl.cs +++ b/osu.Game/Overlays/SearchableList/HeaderTabControl.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.SearchableList -{ - public class HeaderTabControl : OsuTabControl - { - protected override TabItem CreateTabItem(T value) => new HeaderTabItem(value); - - public HeaderTabControl() - { - Height = 26; - AccentColour = Color4.White; - } - - private class HeaderTabItem : OsuTabItem - { - public HeaderTabItem(T value) : base(value) - { - Text.TextSize = 16; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.SearchableList +{ + public class HeaderTabControl : OsuTabControl + { + protected override TabItem CreateTabItem(T value) => new HeaderTabItem(value); + + public HeaderTabControl() + { + Height = 26; + AccentColour = Color4.White; + } + + private class HeaderTabItem : OsuTabItem + { + public HeaderTabItem(T value) : base(value) + { + Text.TextSize = 16; + } + } + } +} diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs index ffc6a370ec..82a60a09e1 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs @@ -1,134 +1,134 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Overlays.SearchableList -{ - public abstract class SearchableListFilterControl : Container - { - private const float padding = 10; - - private readonly Container filterContainer; - private readonly Box tabStrip; - - public readonly SearchTextBox Search; - public readonly PageTabControl Tabs; - public readonly DisplayStyleControl DisplayStyleControl; - - protected abstract Color4 BackgroundColour { get; } - protected abstract T DefaultTab { get; } - protected virtual Drawable CreateSupplementaryControls() => null; - - protected SearchableListFilterControl() - { - if (!typeof(T).IsEnum) - throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument"); - - RelativeSizeAxes = Axes.X; - - var controls = CreateSupplementaryControls(); - Container controlsContainer; - Children = new Drawable[] - { - filterContainer = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = BackgroundColour, - Alpha = 0.9f, - }, - tabStrip = new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 1, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Top = padding, Horizontal = SearchableListOverlay.WIDTH_PADDING }, - Children = new Drawable[] - { - Search = new FilterSearchTextBox - { - RelativeSizeAxes = Axes.X, - }, - controlsContainer = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = controls != null ? padding : 0 }, - }, - Tabs = new PageTabControl - { - RelativeSizeAxes = Axes.X, - }, - new Box //keep the tab strip part of autosize, but don't put it in the flow container - { - RelativeSizeAxes = Axes.X, - Height = 1, - Colour = Color4.White.Opacity(0), - }, - }, - }, - }, - }, - DisplayStyleControl = new DisplayStyleControl - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }, - }; - - if (controls != null) controlsContainer.Children = new[] { controls }; - - Tabs.Current.Value = DefaultTab; - Tabs.Current.TriggerChange(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - tabStrip.Colour = colours.Yellow; - } - - protected override void Update() - { - base.Update(); - - Height = filterContainer.Height; - DisplayStyleControl.Margin = new MarginPadding { Top = filterContainer.Height - 35, Right = SearchableListOverlay.WIDTH_PADDING }; - } - - private class FilterSearchTextBox : SearchTextBox - { - protected override Color4 BackgroundUnfocused => backgroundColour; - protected override Color4 BackgroundFocused => backgroundColour; - protected override bool AllowCommit => true; - - private Color4 backgroundColour; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - backgroundColour = colours.Gray2.Opacity(0.9f); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Overlays.SearchableList +{ + public abstract class SearchableListFilterControl : Container + { + private const float padding = 10; + + private readonly Container filterContainer; + private readonly Box tabStrip; + + public readonly SearchTextBox Search; + public readonly PageTabControl Tabs; + public readonly DisplayStyleControl DisplayStyleControl; + + protected abstract Color4 BackgroundColour { get; } + protected abstract T DefaultTab { get; } + protected virtual Drawable CreateSupplementaryControls() => null; + + protected SearchableListFilterControl() + { + if (!typeof(T).IsEnum) + throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument"); + + RelativeSizeAxes = Axes.X; + + var controls = CreateSupplementaryControls(); + Container controlsContainer; + Children = new Drawable[] + { + filterContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = BackgroundColour, + Alpha = 0.9f, + }, + tabStrip = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 1, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Top = padding, Horizontal = SearchableListOverlay.WIDTH_PADDING }, + Children = new Drawable[] + { + Search = new FilterSearchTextBox + { + RelativeSizeAxes = Axes.X, + }, + controlsContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = controls != null ? padding : 0 }, + }, + Tabs = new PageTabControl + { + RelativeSizeAxes = Axes.X, + }, + new Box //keep the tab strip part of autosize, but don't put it in the flow container + { + RelativeSizeAxes = Axes.X, + Height = 1, + Colour = Color4.White.Opacity(0), + }, + }, + }, + }, + }, + DisplayStyleControl = new DisplayStyleControl + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + }; + + if (controls != null) controlsContainer.Children = new[] { controls }; + + Tabs.Current.Value = DefaultTab; + Tabs.Current.TriggerChange(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + tabStrip.Colour = colours.Yellow; + } + + protected override void Update() + { + base.Update(); + + Height = filterContainer.Height; + DisplayStyleControl.Margin = new MarginPadding { Top = filterContainer.Height - 35, Right = SearchableListOverlay.WIDTH_PADDING }; + } + + private class FilterSearchTextBox : SearchTextBox + { + protected override Color4 BackgroundUnfocused => backgroundColour; + protected override Color4 BackgroundFocused => backgroundColour; + protected override bool AllowCommit => true; + + private Color4 backgroundColour; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + backgroundColour = colours.Gray2.Opacity(0.9f); + } + } + } +} diff --git a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs index e053f2f773..9c4fe1c398 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs @@ -1,83 +1,83 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Overlays.SearchableList -{ - public abstract class SearchableListHeader : Container - { - public readonly HeaderTabControl Tabs; - - protected abstract Color4 BackgroundColour { get; } - protected abstract T DefaultTab { get; } - protected abstract Drawable CreateHeaderText(); - protected abstract FontAwesome Icon { get; } - - protected SearchableListHeader() - { - if (!typeof(T).IsEnum) - throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument"); - - RelativeSizeAxes = Axes.X; - Height = 90; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = BackgroundColour, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = SearchableListOverlay.WIDTH_PADDING, Right = SearchableListOverlay.WIDTH_PADDING }, - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.BottomLeft, - Position = new Vector2(-35f, 5f), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10f, 0f), - Children = new[] - { - new SpriteIcon - { - Size = new Vector2(25), - Icon = Icon, - }, - CreateHeaderText(), - }, - }, - Tabs = new HeaderTabControl - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - }, - }, - }, - }; - - Tabs.Current.Value = DefaultTab; - Tabs.Current.TriggerChange(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Tabs.StripColour = colours.Green; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Overlays.SearchableList +{ + public abstract class SearchableListHeader : Container + { + public readonly HeaderTabControl Tabs; + + protected abstract Color4 BackgroundColour { get; } + protected abstract T DefaultTab { get; } + protected abstract Drawable CreateHeaderText(); + protected abstract FontAwesome Icon { get; } + + protected SearchableListHeader() + { + if (!typeof(T).IsEnum) + throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument"); + + RelativeSizeAxes = Axes.X; + Height = 90; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = BackgroundColour, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = SearchableListOverlay.WIDTH_PADDING, Right = SearchableListOverlay.WIDTH_PADDING }, + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomLeft, + Position = new Vector2(-35f, 5f), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0f), + Children = new[] + { + new SpriteIcon + { + Size = new Vector2(25), + Icon = Icon, + }, + CreateHeaderText(), + }, + }, + Tabs = new HeaderTabControl + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + }, + }, + }, + }; + + Tabs.Current.Value = DefaultTab; + Tabs.Current.TriggerChange(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Tabs.StripColour = colours.Green; + } + } +} diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs index 3998af05b2..47cdb4a765 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs @@ -1,123 +1,123 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.SearchableList -{ - public abstract class SearchableListOverlay : WaveOverlayContainer - { - public static readonly float WIDTH_PADDING = 80; - } - - public abstract class SearchableListOverlay : SearchableListOverlay - { - private readonly Container scrollContainer; - - protected readonly SearchableListHeader Header; - protected readonly SearchableListFilterControl Filter; - protected readonly FillFlowContainer ScrollFlow; - - protected abstract Color4 BackgroundColour { get; } - protected abstract Color4 TrianglesColourLight { get; } - protected abstract Color4 TrianglesColourDark { get; } - protected abstract SearchableListHeader CreateHeader(); - protected abstract SearchableListFilterControl CreateFilterControl(); - - protected SearchableListOverlay() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = BackgroundColour, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] - { - new Triangles - { - RelativeSizeAxes = Axes.Both, - TriangleScale = 5, - ColourLight = TrianglesColourLight, - ColourDark = TrianglesColourDark, - }, - }, - }, - scrollContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - ScrollbarVisible = false, - Children = new[] - { - ScrollFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 }, - Direction = FillDirection.Vertical, - }, - }, - }, - }, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - Header = CreateHeader(), - Filter = CreateFilterControl(), - }, - }, - }; - - Filter.Search.Exit = Hide; - } - - protected override void Update() - { - base.Update(); - - scrollContainer.Padding = new MarginPadding { Top = Header.Height + Filter.Height }; - } - - protected override void OnFocus(InputState state) - { - GetContainingInputManager().ChangeFocus(Filter.Search); - } - - protected override void PopIn() - { - base.PopIn(); - - Filter.Search.HoldFocus = true; - } - - protected override void PopOut() - { - base.PopOut(); - - Filter.Search.HoldFocus = false; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.SearchableList +{ + public abstract class SearchableListOverlay : WaveOverlayContainer + { + public static readonly float WIDTH_PADDING = 80; + } + + public abstract class SearchableListOverlay : SearchableListOverlay + { + private readonly Container scrollContainer; + + protected readonly SearchableListHeader Header; + protected readonly SearchableListFilterControl Filter; + protected readonly FillFlowContainer ScrollFlow; + + protected abstract Color4 BackgroundColour { get; } + protected abstract Color4 TrianglesColourLight { get; } + protected abstract Color4 TrianglesColourDark { get; } + protected abstract SearchableListHeader CreateHeader(); + protected abstract SearchableListFilterControl CreateFilterControl(); + + protected SearchableListOverlay() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = BackgroundColour, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] + { + new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 5, + ColourLight = TrianglesColourLight, + ColourDark = TrianglesColourDark, + }, + }, + }, + scrollContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + Children = new[] + { + ScrollFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 }, + Direction = FillDirection.Vertical, + }, + }, + }, + }, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + Header = CreateHeader(), + Filter = CreateFilterControl(), + }, + }, + }; + + Filter.Search.Exit = Hide; + } + + protected override void Update() + { + base.Update(); + + scrollContainer.Padding = new MarginPadding { Top = Header.Height + Filter.Height }; + } + + protected override void OnFocus(InputState state) + { + GetContainingInputManager().ChangeFocus(Filter.Search); + } + + protected override void PopIn() + { + base.PopIn(); + + Filter.Search.HoldFocus = true; + } + + protected override void PopOut() + { + base.PopOut(); + + Filter.Search.HoldFocus = false; + } + } +} diff --git a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs index a528a3c35d..33ddb8f53f 100644 --- a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs +++ b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; -using OpenTK; - -namespace osu.Game.Overlays.SearchableList -{ - public class SlimEnumDropdown : OsuEnumDropdown - { - protected override DropdownHeader CreateHeader() => new SlimDropdownHeader(); - - protected override DropdownMenu CreateMenu() => new SlimMenu(); - - private class SlimDropdownHeader : OsuDropdownHeader - { - public SlimDropdownHeader() - { - Height = 25; - Icon.Size = new Vector2(16); - Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - BackgroundColour = Color4.Black.Opacity(0.25f); - } - } - - private class SlimMenu : OsuDropdownMenu - { - public SlimMenu() - { - BackgroundColour = Color4.Black.Opacity(0.7f); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; +using OpenTK; + +namespace osu.Game.Overlays.SearchableList +{ + public class SlimEnumDropdown : OsuEnumDropdown + { + protected override DropdownHeader CreateHeader() => new SlimDropdownHeader(); + + protected override DropdownMenu CreateMenu() => new SlimMenu(); + + private class SlimDropdownHeader : OsuDropdownHeader + { + public SlimDropdownHeader() + { + Height = 25; + Icon.Size = new Vector2(16); + Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + BackgroundColour = Color4.Black.Opacity(0.25f); + } + } + + private class SlimMenu : OsuDropdownMenu + { + public SlimMenu() + { + BackgroundColour = Color4.Black.Opacity(0.7f); + } + } + } +} diff --git a/osu.Game/Overlays/Settings/DangerousSettingsButton.cs b/osu.Game/Overlays/Settings/DangerousSettingsButton.cs index 69c0c03552..9eb68b416a 100644 --- a/osu.Game/Overlays/Settings/DangerousSettingsButton.cs +++ b/osu.Game/Overlays/Settings/DangerousSettingsButton.cs @@ -1,23 +1,23 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Settings -{ - /// - /// A with pink colours to mark dangerous/destructive actions. - /// - public class DangerousSettingsButton : SettingsButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Pink; - - Triangles.ColourDark = colours.PinkDark; - Triangles.ColourLight = colours.PinkLight; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Settings +{ + /// + /// A with pink colours to mark dangerous/destructive actions. + /// + public class DangerousSettingsButton : SettingsButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Pink; + + Triangles.ColourDark = colours.PinkDark; + Triangles.ColourLight = colours.PinkLight; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs index 5260fb89d4..d637eb96f6 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs @@ -1,73 +1,73 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Graphics; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Overlays.Settings.Sections.Audio -{ - public class AudioDevicesSettings : SettingsSubsection - { - protected override string Header => "Devices"; - - private AudioManager audio; - private SettingsDropdown dropdown; - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - this.audio = audio; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (audio != null) - { - audio.OnNewDevice -= onDeviceChanged; - audio.OnLostDevice -= onDeviceChanged; - } - } - - private void updateItems() - { - var deviceItems = new List> { new KeyValuePair("Default", string.Empty) }; - deviceItems.AddRange(audio.AudioDeviceNames.Select(d => new KeyValuePair(d, d))); - - var preferredDeviceName = audio.AudioDevice.Value; - if (deviceItems.All(kv => kv.Value != preferredDeviceName)) - deviceItems.Add(new KeyValuePair(preferredDeviceName, preferredDeviceName)); - - // The option dropdown for audio device selection lists all audio - // device names. Dropdowns, however, may not have multiple identical - // keys. Thus, we remove duplicate audio device names from - // the dropdown. BASS does not give us a simple mechanism to select - // specific audio devices in such a case anyways. Such - // functionality would require involved OS-specific code. - dropdown.Items = deviceItems.Distinct().ToList(); - } - - private void onDeviceChanged(string name) => updateItems(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - Children = new Drawable[] - { - dropdown = new SettingsDropdown() - }; - - updateItems(); - - dropdown.Bindable = audio.AudioDevice; - - audio.OnNewDevice += onDeviceChanged; - audio.OnLostDevice += onDeviceChanged; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Overlays.Settings.Sections.Audio +{ + public class AudioDevicesSettings : SettingsSubsection + { + protected override string Header => "Devices"; + + private AudioManager audio; + private SettingsDropdown dropdown; + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + this.audio = audio; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (audio != null) + { + audio.OnNewDevice -= onDeviceChanged; + audio.OnLostDevice -= onDeviceChanged; + } + } + + private void updateItems() + { + var deviceItems = new List> { new KeyValuePair("Default", string.Empty) }; + deviceItems.AddRange(audio.AudioDeviceNames.Select(d => new KeyValuePair(d, d))); + + var preferredDeviceName = audio.AudioDevice.Value; + if (deviceItems.All(kv => kv.Value != preferredDeviceName)) + deviceItems.Add(new KeyValuePair(preferredDeviceName, preferredDeviceName)); + + // The option dropdown for audio device selection lists all audio + // device names. Dropdowns, however, may not have multiple identical + // keys. Thus, we remove duplicate audio device names from + // the dropdown. BASS does not give us a simple mechanism to select + // specific audio devices in such a case anyways. Such + // functionality would require involved OS-specific code. + dropdown.Items = deviceItems.Distinct().ToList(); + } + + private void onDeviceChanged(string name) => updateItems(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + Children = new Drawable[] + { + dropdown = new SettingsDropdown() + }; + + updateItems(); + + dropdown.Bindable = audio.AudioDevice; + + audio.OnNewDevice += onDeviceChanged; + audio.OnLostDevice += onDeviceChanged; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs index ad1eec3c68..35f0a19796 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.Audio -{ - public class MainMenuSettings : SettingsSubsection - { - protected override string Header => "Main Menu"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new[] - { - new SettingsCheckbox - { - LabelText = "Interface voices", - Bindable = config.GetBindable(OsuSetting.MenuVoice) - }, - new SettingsCheckbox - { - LabelText = "osu! music theme", - Bindable = config.GetBindable(OsuSetting.MenuMusic) - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Audio +{ + public class MainMenuSettings : SettingsSubsection + { + protected override string Header => "Main Menu"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new[] + { + new SettingsCheckbox + { + LabelText = "Interface voices", + Bindable = config.GetBindable(OsuSetting.MenuVoice) + }, + new SettingsCheckbox + { + LabelText = "osu! music theme", + Bindable = config.GetBindable(OsuSetting.MenuMusic) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 598195333c..8850f716e0 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -1,38 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Configuration; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings.Sections.Audio -{ - public class OffsetSettings : SettingsSubsection - { - protected override string Header => "Offset Adjustment"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new Drawable[] - { - new SettingsSlider - { - LabelText = "Audio Offset", - Bindable = config.GetBindable(OsuSetting.AudioOffset), - KeyboardStep = 100f - }, - new SettingsButton - { - Text = "Offset wizard" - } - }; - } - - private class OffsetSlider : OsuSliderBar - { - public override string TooltipText => Current.Value.ToString(@"0ms"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings.Sections.Audio +{ + public class OffsetSettings : SettingsSubsection + { + protected override string Header => "Offset Adjustment"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsSlider + { + LabelText = "Audio Offset", + Bindable = config.GetBindable(OsuSetting.AudioOffset), + KeyboardStep = 100f + }, + new SettingsButton + { + Text = "Offset wizard" + } + }; + } + + private class OffsetSlider : OsuSliderBar + { + public override string TooltipText => Current.Value.ToString(@"0ms"); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs index 92ee01dd7a..80896c163f 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Graphics; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.Audio -{ - public class VolumeSettings : SettingsSubsection - { - protected override string Header => "Volume"; - - [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config) - { - Children = new Drawable[] - { - new SettingsSlider { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.1f }, - new SettingsSlider { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.1f }, - new SettingsSlider { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.1f }, - new SettingsSlider { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.1f }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Audio +{ + public class VolumeSettings : SettingsSubsection + { + protected override string Header => "Volume"; + + [BackgroundDependencyLoader] + private void load(AudioManager audio, OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsSlider { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.1f }, + new SettingsSlider { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.1f }, + new SettingsSlider { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.1f }, + new SettingsSlider { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.1f }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/AudioSection.cs b/osu.Game/Overlays/Settings/Sections/AudioSection.cs index ad074f6328..d9326b686f 100644 --- a/osu.Game/Overlays/Settings/Sections/AudioSection.cs +++ b/osu.Game/Overlays/Settings/Sections/AudioSection.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.Audio; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class AudioSection : SettingsSection - { - public override string Header => "Audio"; - public override FontAwesome Icon => FontAwesome.fa_volume_up; - - public AudioSection() - { - Children = new Drawable[] - { - new AudioDevicesSettings(), - new VolumeSettings(), - new OffsetSettings(), - new MainMenuSettings(), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.Audio; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class AudioSection : SettingsSection + { + public override string Header => "Audio"; + public override FontAwesome Icon => FontAwesome.fa_volume_up; + + public AudioSection() + { + Children = new Drawable[] + { + new AudioDevicesSettings(), + new VolumeSettings(), + new OffsetSettings(), + new MainMenuSettings(), + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs index 9a5946bf4e..9f550413f3 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Runtime; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; - -namespace osu.Game.Overlays.Settings.Sections.Debug -{ - public class GCSettings : SettingsSubsection - { - protected override string Header => "Garbage Collector"; - - [BackgroundDependencyLoader] - private void load(FrameworkDebugConfigManager config) - { - Children = new Drawable[] - { - new SettingsEnumDropdown - { - LabelText = "Active mode", - Bindable = config.GetBindable(DebugSetting.ActiveGCMode) - }, - new SettingsButton - { - Text = "Force garbage collection", - Action = GC.Collect - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Runtime; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; + +namespace osu.Game.Overlays.Settings.Sections.Debug +{ + public class GCSettings : SettingsSubsection + { + protected override string Header => "Garbage Collector"; + + [BackgroundDependencyLoader] + private void load(FrameworkDebugConfigManager config) + { + Children = new Drawable[] + { + new SettingsEnumDropdown + { + LabelText = "Active mode", + Bindable = config.GetBindable(DebugSetting.ActiveGCMode) + }, + new SettingsButton + { + Text = "Force garbage collection", + Action = GC.Collect + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index ba591b9456..8b09a2a26a 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -1,37 +1,37 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; - -namespace osu.Game.Overlays.Settings.Sections.Debug -{ - public class GeneralSettings : SettingsSubsection - { - protected override string Header => "General"; - - [BackgroundDependencyLoader] - private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig) - { - Children = new Drawable[] - { - new SettingsCheckbox - { - LabelText = "Show log overlay", - Bindable = frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay) - }, - new SettingsCheckbox - { - LabelText = "Performance logging", - Bindable = frameworkConfig.GetBindable(FrameworkSetting.PerformanceLogging) - }, - new SettingsCheckbox - { - LabelText = "Bypass caching (slow)", - Bindable = config.GetBindable(DebugSetting.BypassCaching) - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; + +namespace osu.Game.Overlays.Settings.Sections.Debug +{ + public class GeneralSettings : SettingsSubsection + { + protected override string Header => "General"; + + [BackgroundDependencyLoader] + private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig) + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = "Show log overlay", + Bindable = frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay) + }, + new SettingsCheckbox + { + LabelText = "Performance logging", + Bindable = frameworkConfig.GetBindable(FrameworkSetting.PerformanceLogging) + }, + new SettingsCheckbox + { + LabelText = "Bypass caching (slow)", + Bindable = config.GetBindable(DebugSetting.BypassCaching) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/DebugSection.cs b/osu.Game/Overlays/Settings/Sections/DebugSection.cs index fb4d4f62a8..0502e98192 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSection.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSection.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.Debug; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class DebugSection : SettingsSection - { - public override string Header => "Debug"; - public override FontAwesome Icon => FontAwesome.fa_bug; - - public DebugSection() - { - Children = new Drawable[] - { - new GeneralSettings(), - new GCSettings(), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.Debug; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class DebugSection : SettingsSection + { + public override string Header => "Debug"; + public override FontAwesome Icon => FontAwesome.fa_bug; + + public DebugSection() + { + Children = new Drawable[] + { + new GeneralSettings(), + new GCSettings(), + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index b60b0d9531..647395cf69 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -1,44 +1,44 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.Gameplay -{ - public class GeneralSettings : SettingsSubsection - { - protected override string Header => "General"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new Drawable[] - { - new SettingsSlider - { - LabelText = "Background dim", - Bindable = config.GetBindable(OsuSetting.DimLevel), - KeyboardStep = 0.1f - }, - new SettingsSlider - { - LabelText = "Background blur", - Bindable = config.GetBindable(OsuSetting.BlurLevel), - KeyboardStep = 0.1f - }, - new SettingsCheckbox - { - LabelText = "Show score overlay", - Bindable = config.GetBindable(OsuSetting.ShowInterface) - }, - new SettingsCheckbox - { - LabelText = "Always show key overlay", - Bindable = config.GetBindable(OsuSetting.KeyOverlay) - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class GeneralSettings : SettingsSubsection + { + protected override string Header => "General"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsSlider + { + LabelText = "Background dim", + Bindable = config.GetBindable(OsuSetting.DimLevel), + KeyboardStep = 0.1f + }, + new SettingsSlider + { + LabelText = "Background blur", + Bindable = config.GetBindable(OsuSetting.BlurLevel), + KeyboardStep = 0.1f + }, + new SettingsCheckbox + { + LabelText = "Show score overlay", + Bindable = config.GetBindable(OsuSetting.ShowInterface) + }, + new SettingsCheckbox + { + LabelText = "Always show key overlay", + Bindable = config.GetBindable(OsuSetting.KeyOverlay) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ScrollingSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ScrollingSettings.cs index 4e8706137c..0e661aeba6 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ScrollingSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ScrollingSettings.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.Gameplay -{ - public class ScrollingSettings : SettingsSubsection - { - protected override string Header => "Scrolling"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new[] - { - new SettingsEnumDropdown - { - LabelText = "Visualise speed changes as", - Bindable = config.GetBindable(OsuSetting.SpeedChangeVisualisation), - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class ScrollingSettings : SettingsSubsection + { + protected override string Header => "Scrolling"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new[] + { + new SettingsEnumDropdown + { + LabelText = "Visualise speed changes as", + Bindable = config.GetBindable(OsuSetting.SpeedChangeVisualisation), + } + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index 4bbd87c7e6..7575105ef7 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -1,50 +1,50 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Configuration; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings.Sections.Gameplay -{ - public class SongSelectSettings : SettingsSubsection - { - protected override string Header => "Song Select"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new Drawable[] - { - new SettingsCheckbox - { - LabelText = "Show converted beatmaps", - Bindable = config.GetBindable(OsuSetting.ShowConvertedBeatmaps), - }, - new SettingsSlider - { - LabelText = "Display beatmaps from", - Bindable = config.GetBindable(OsuSetting.DisplayStarsMinimum), - KeyboardStep = 1f - }, - new SettingsSlider - { - LabelText = "up to", - Bindable = config.GetBindable(OsuSetting.DisplayStarsMaximum), - KeyboardStep = 1f - }, - new SettingsEnumDropdown - { - LabelText = "Random selection algorithm", - Bindable = config.GetBindable(OsuSetting.RandomSelectAlgorithm), - } - }; - } - - private class StarSlider : OsuSliderBar - { - public override string TooltipText => Current.Value.ToString(@"0.## stars"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class SongSelectSettings : SettingsSubsection + { + protected override string Header => "Song Select"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = "Show converted beatmaps", + Bindable = config.GetBindable(OsuSetting.ShowConvertedBeatmaps), + }, + new SettingsSlider + { + LabelText = "Display beatmaps from", + Bindable = config.GetBindable(OsuSetting.DisplayStarsMinimum), + KeyboardStep = 1f + }, + new SettingsSlider + { + LabelText = "up to", + Bindable = config.GetBindable(OsuSetting.DisplayStarsMaximum), + KeyboardStep = 1f + }, + new SettingsEnumDropdown + { + LabelText = "Random selection algorithm", + Bindable = config.GetBindable(OsuSetting.RandomSelectAlgorithm), + } + }; + } + + private class StarSlider : OsuSliderBar + { + public override string TooltipText => Current.Value.ToString(@"0.## stars"); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 8a2131fb1c..3851a73901 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -1,39 +1,39 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.Gameplay; -using osu.Game.Rulesets; -using System.Linq; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class GameplaySection : SettingsSection - { - public override string Header => "Gameplay"; - public override FontAwesome Icon => FontAwesome.fa_circle_o; - - public GameplaySection() - { - Children = new Drawable[] - { - new GeneralSettings(), - new SongSelectSettings(), - new ScrollingSettings() - }; - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - foreach(Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance())) - { - SettingsSubsection section = ruleset.CreateSettings(); - if (section != null) - Add(section); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.Gameplay; +using osu.Game.Rulesets; +using System.Linq; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class GameplaySection : SettingsSection + { + public override string Header => "Gameplay"; + public override FontAwesome Icon => FontAwesome.fa_circle_o; + + public GameplaySection() + { + Children = new Drawable[] + { + new GeneralSettings(), + new SongSelectSettings(), + new ScrollingSettings() + }; + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + foreach(Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance())) + { + SettingsSubsection section = ruleset.CreateSettings(); + if (section != null) + Add(section); + } + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index 113ef10bad..977d75079b 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; - -namespace osu.Game.Overlays.Settings.Sections.General -{ - public class LanguageSettings : SettingsSubsection - { - protected override string Header => "Language"; - - [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig) - { - Children = new Drawable[] - { - new SettingsCheckbox - { - LabelText = "Prefer metadata in original language", - Bindable = frameworkConfig.GetBindable(FrameworkSetting.ShowUnicode) - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; + +namespace osu.Game.Overlays.Settings.Sections.General +{ + public class LanguageSettings : SettingsSubsection + { + protected override string Header => "Language"; + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager frameworkConfig) + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = "Prefer metadata in original language", + Bindable = frameworkConfig.GetBindable(FrameworkSetting.ShowUnicode) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 2d07704853..9a42bdd2aa 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -1,379 +1,379 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Configuration; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using OpenTK; -using osu.Framework.Input; -using osu.Game.Users; -using System.ComponentModel; -using osu.Game.Graphics; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; -using Container = osu.Framework.Graphics.Containers.Container; - -namespace osu.Game.Overlays.Settings.Sections.General -{ - public class LoginSettings : FillFlowContainer, IOnlineComponent - { - private bool bounding = true; - private LoginForm form; - private OsuColour colours; - - private UserPanel panel; - private UserDropdown dropdown; - - /// - /// Called to request a hide of a parent displaying this container. - /// - public Action RequestHide; - - public override RectangleF BoundingBox => bounding ? base.BoundingBox : RectangleF.Empty; - - public bool Bounding - { - get { return bounding; } - set - { - bounding = value; - Invalidate(Invalidation.MiscGeometry); - } - } - - public LoginSettings() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - Spacing = new Vector2(0f, 5f); - } - - [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, APIAccess api) - { - this.colours = colours; - - api?.Register(this); - } - - public void APIStateChanged(APIAccess api, APIState state) - { - form = null; - - switch (state) - { - case APIState.Offline: - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "ACCOUNT", - Margin = new MarginPadding { Bottom = 5 }, - Font = @"Exo2.0-Black", - }, - form = new LoginForm() - }; - break; - case APIState.Failing: - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Connection failing :(", - }, - }; - break; - case APIState.Connecting: - Children = new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "Connecting...", - Margin = new MarginPadding { Top = 10, Bottom = 10 }, - }, - }; - break; - case APIState.Online: - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = 20, Right = 20 }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0f, 10f), - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "Signed in", - TextSize = 18, - Font = @"Exo2.0-Bold", - Margin = new MarginPadding { Top = 5, Bottom = 5 }, - }, - }, - }, - panel = new UserPanel(api.LocalUser.Value) - { - RelativeSizeAxes = Axes.X, - Action = RequestHide - }, - dropdown = new UserDropdown { RelativeSizeAxes = Axes.X }, - }, - }, - }; - - panel.Status.BindTo(api.LocalUser.Value.Status); - - dropdown.Current.ValueChanged += newValue => - { - switch (newValue) - { - case UserAction.Online: - api.LocalUser.Value.Status.Value = new UserStatusOnline(); - dropdown.StatusColour = colours.Green; - break; - case UserAction.DoNotDisturb: - api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb(); - dropdown.StatusColour = colours.Red; - break; - case UserAction.AppearOffline: - api.LocalUser.Value.Status.Value = new UserStatusOffline(); - dropdown.StatusColour = colours.Gray7; - break; - case UserAction.SignOut: - api.Logout(); - break; - } - }; - dropdown.Current.TriggerChange(); - - break; - } - - if (form != null) GetContainingInputManager()?.ChangeFocus(form); - } - - public override bool AcceptsFocus => true; - - protected override bool OnClick(InputState state) => true; - - protected override void OnFocus(InputState state) - { - if (form != null) GetContainingInputManager().ChangeFocus(form); - base.OnFocus(state); - } - - private class LoginForm : FillFlowContainer - { - private TextBox username; - private TextBox password; - private APIAccess api; - - private void performLogin() - { - if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text)) - api.Login(username.Text, password.Text); - } - - [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api, OsuConfigManager config) - { - this.api = api; - Direction = FillDirection.Vertical; - Spacing = new Vector2(0, 5); - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - Children = new Drawable[] - { - username = new OsuTextBox - { - PlaceholderText = "Email address", - RelativeSizeAxes = Axes.X, - Text = api?.ProvidedUsername ?? string.Empty, - TabbableContentContainer = this - }, - password = new OsuPasswordTextBox - { - PlaceholderText = "Password", - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - OnCommit = (sender, newText) => performLogin() - }, - new SettingsCheckbox - { - LabelText = "Remember email address", - Bindable = config.GetBindable(OsuSetting.SaveUsername), - }, - new SettingsCheckbox - { - LabelText = "Stay signed in", - Bindable = config.GetBindable(OsuSetting.SavePassword), - }, - new SettingsButton - { - Text = "Sign in", - Action = performLogin - }, - new SettingsButton - { - Text = "Register", - //Action = registerLink - } - }; - } - - public override bool AcceptsFocus => true; - - protected override bool OnClick(InputState state) => true; - - protected override void OnFocus(InputState state) - { - Schedule(() => { GetContainingInputManager().ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); - } - } - - private class UserDropdown : OsuEnumDropdown - { - protected override DropdownHeader CreateHeader() => new UserDropdownHeader(); - - protected override DropdownMenu CreateMenu() => new UserDropdownMenu(); - - public Color4 StatusColour - { - set - { - var h = Header as UserDropdownHeader; - if (h == null) return; - h.StatusColour = value; - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Gray5; - } - - private class UserDropdownMenu : OsuDropdownMenu - { - public UserDropdownMenu() - { - Masking = true; - CornerRadius = 5; - - Margin = new MarginPadding { Bottom = 5 }; - - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.25f), - Radius = 4, - }; - - ItemsContainer.Padding = new MarginPadding(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray3; - } - - protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableUserDropdownMenuItem(item); - - private class DrawableUserDropdownMenuItem : DrawableOsuDropdownMenuItem - { - public DrawableUserDropdownMenuItem(MenuItem item) - : base(item) - { - Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 5 }; - CornerRadius = 5; - } - - protected override Drawable CreateContent() => new Content - { - Label = { Margin = new MarginPadding { Left = UserDropdownHeader.LABEL_LEFT_MARGIN - 11 } } - }; - } - } - - private class UserDropdownHeader : OsuDropdownHeader - { - public const float LABEL_LEFT_MARGIN = 20; - - private readonly SpriteIcon statusIcon; - public Color4 StatusColour - { - set - { - statusIcon.FadeColour(value, 500, Easing.OutQuint); - } - } - - public UserDropdownHeader() - { - Foreground.Padding = new MarginPadding { Left = 10, Right = 10 }; - Margin = new MarginPadding { Bottom = 5 }; - Masking = true; - CornerRadius = 5; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.25f), - Radius = 4, - }; - - Icon.Size = new Vector2(14); - Icon.Margin = new MarginPadding(0); - - Foreground.Add(statusIcon = new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.fa_circle_o, - Size = new Vector2(14), - }); - - Text.Margin = new MarginPadding { Left = LABEL_LEFT_MARGIN }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray3; - } - } - } - - private enum UserAction - { - Online, - [Description(@"Do not disturb")] - DoNotDisturb, - [Description(@"Appear offline")] - AppearOffline, - [Description(@"Sign out")] - SignOut, - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Configuration; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using OpenTK; +using osu.Framework.Input; +using osu.Game.Users; +using System.ComponentModel; +using osu.Game.Graphics; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; +using Container = osu.Framework.Graphics.Containers.Container; + +namespace osu.Game.Overlays.Settings.Sections.General +{ + public class LoginSettings : FillFlowContainer, IOnlineComponent + { + private bool bounding = true; + private LoginForm form; + private OsuColour colours; + + private UserPanel panel; + private UserDropdown dropdown; + + /// + /// Called to request a hide of a parent displaying this container. + /// + public Action RequestHide; + + public override RectangleF BoundingBox => bounding ? base.BoundingBox : RectangleF.Empty; + + public bool Bounding + { + get { return bounding; } + set + { + bounding = value; + Invalidate(Invalidation.MiscGeometry); + } + } + + public LoginSettings() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Spacing = new Vector2(0f, 5f); + } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours, APIAccess api) + { + this.colours = colours; + + api?.Register(this); + } + + public void APIStateChanged(APIAccess api, APIState state) + { + form = null; + + switch (state) + { + case APIState.Offline: + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "ACCOUNT", + Margin = new MarginPadding { Bottom = 5 }, + Font = @"Exo2.0-Black", + }, + form = new LoginForm() + }; + break; + case APIState.Failing: + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Connection failing :(", + }, + }; + break; + case APIState.Connecting: + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Connecting...", + Margin = new MarginPadding { Top = 10, Bottom = 10 }, + }, + }; + break; + case APIState.Online: + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = 20, Right = 20 }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Signed in", + TextSize = 18, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Top = 5, Bottom = 5 }, + }, + }, + }, + panel = new UserPanel(api.LocalUser.Value) + { + RelativeSizeAxes = Axes.X, + Action = RequestHide + }, + dropdown = new UserDropdown { RelativeSizeAxes = Axes.X }, + }, + }, + }; + + panel.Status.BindTo(api.LocalUser.Value.Status); + + dropdown.Current.ValueChanged += newValue => + { + switch (newValue) + { + case UserAction.Online: + api.LocalUser.Value.Status.Value = new UserStatusOnline(); + dropdown.StatusColour = colours.Green; + break; + case UserAction.DoNotDisturb: + api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb(); + dropdown.StatusColour = colours.Red; + break; + case UserAction.AppearOffline: + api.LocalUser.Value.Status.Value = new UserStatusOffline(); + dropdown.StatusColour = colours.Gray7; + break; + case UserAction.SignOut: + api.Logout(); + break; + } + }; + dropdown.Current.TriggerChange(); + + break; + } + + if (form != null) GetContainingInputManager()?.ChangeFocus(form); + } + + public override bool AcceptsFocus => true; + + protected override bool OnClick(InputState state) => true; + + protected override void OnFocus(InputState state) + { + if (form != null) GetContainingInputManager().ChangeFocus(form); + base.OnFocus(state); + } + + private class LoginForm : FillFlowContainer + { + private TextBox username; + private TextBox password; + private APIAccess api; + + private void performLogin() + { + if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text)) + api.Login(username.Text, password.Text); + } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(APIAccess api, OsuConfigManager config) + { + this.api = api; + Direction = FillDirection.Vertical; + Spacing = new Vector2(0, 5); + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + Children = new Drawable[] + { + username = new OsuTextBox + { + PlaceholderText = "Email address", + RelativeSizeAxes = Axes.X, + Text = api?.ProvidedUsername ?? string.Empty, + TabbableContentContainer = this + }, + password = new OsuPasswordTextBox + { + PlaceholderText = "Password", + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + OnCommit = (sender, newText) => performLogin() + }, + new SettingsCheckbox + { + LabelText = "Remember email address", + Bindable = config.GetBindable(OsuSetting.SaveUsername), + }, + new SettingsCheckbox + { + LabelText = "Stay signed in", + Bindable = config.GetBindable(OsuSetting.SavePassword), + }, + new SettingsButton + { + Text = "Sign in", + Action = performLogin + }, + new SettingsButton + { + Text = "Register", + //Action = registerLink + } + }; + } + + public override bool AcceptsFocus => true; + + protected override bool OnClick(InputState state) => true; + + protected override void OnFocus(InputState state) + { + Schedule(() => { GetContainingInputManager().ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); + } + } + + private class UserDropdown : OsuEnumDropdown + { + protected override DropdownHeader CreateHeader() => new UserDropdownHeader(); + + protected override DropdownMenu CreateMenu() => new UserDropdownMenu(); + + public Color4 StatusColour + { + set + { + var h = Header as UserDropdownHeader; + if (h == null) return; + h.StatusColour = value; + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Gray5; + } + + private class UserDropdownMenu : OsuDropdownMenu + { + public UserDropdownMenu() + { + Masking = true; + CornerRadius = 5; + + Margin = new MarginPadding { Bottom = 5 }; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Radius = 4, + }; + + ItemsContainer.Padding = new MarginPadding(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray3; + } + + protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableUserDropdownMenuItem(item); + + private class DrawableUserDropdownMenuItem : DrawableOsuDropdownMenuItem + { + public DrawableUserDropdownMenuItem(MenuItem item) + : base(item) + { + Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 5 }; + CornerRadius = 5; + } + + protected override Drawable CreateContent() => new Content + { + Label = { Margin = new MarginPadding { Left = UserDropdownHeader.LABEL_LEFT_MARGIN - 11 } } + }; + } + } + + private class UserDropdownHeader : OsuDropdownHeader + { + public const float LABEL_LEFT_MARGIN = 20; + + private readonly SpriteIcon statusIcon; + public Color4 StatusColour + { + set + { + statusIcon.FadeColour(value, 500, Easing.OutQuint); + } + } + + public UserDropdownHeader() + { + Foreground.Padding = new MarginPadding { Left = 10, Right = 10 }; + Margin = new MarginPadding { Bottom = 5 }; + Masking = true; + CornerRadius = 5; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Radius = 4, + }; + + Icon.Size = new Vector2(14); + Icon.Margin = new MarginPadding(0); + + Foreground.Add(statusIcon = new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.fa_circle_o, + Size = new Vector2(14), + }); + + Text.Margin = new MarginPadding { Left = LABEL_LEFT_MARGIN }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray3; + } + } + } + + private enum UserAction + { + Online, + [Description(@"Do not disturb")] + DoNotDisturb, + [Description(@"Appear offline")] + AppearOffline, + [Description(@"Sign out")] + SignOut, + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index caee7639e4..34d13b1462 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Platform; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.General -{ - public class UpdateSettings : SettingsSubsection - { - protected override string Header => "Updates"; - - [BackgroundDependencyLoader] - private void load(Storage storage, OsuConfigManager config) - { - Children = new Drawable[] - { - new SettingsEnumDropdown - { - LabelText = "Release stream", - Bindable = config.GetBindable(OsuSetting.ReleaseStream), - }, - new SettingsButton - { - Text = "Open osu! folder", - Action = storage.OpenInNativeExplorer, - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.General +{ + public class UpdateSettings : SettingsSubsection + { + protected override string Header => "Updates"; + + [BackgroundDependencyLoader] + private void load(Storage storage, OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsEnumDropdown + { + LabelText = "Release stream", + Bindable = config.GetBindable(OsuSetting.ReleaseStream), + }, + new SettingsButton + { + Text = "Open osu! folder", + Action = storage.OpenInNativeExplorer, + } + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index f7dfd9471b..f75f01c034 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.General; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class GeneralSection : SettingsSection - { - public override string Header => "General"; - public override FontAwesome Icon => FontAwesome.fa_gear; - - public GeneralSection() - { - Children = new Drawable[] - { - new LanguageSettings(), - new UpdateSettings(), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.General; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class GeneralSection : SettingsSection + { + public override string Header => "General"; + public override FontAwesome Icon => FontAwesome.fa_gear; + + public GeneralSection() + { + Children = new Drawable[] + { + new LanguageSettings(), + new UpdateSettings(), + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index fa57a85454..a78cb29468 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -1,37 +1,37 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.Graphics -{ - public class DetailSettings : SettingsSubsection - { - protected override string Header => "Detail Settings"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new Drawable[] - { - new SettingsCheckbox - { - LabelText = "Storyboards", - Bindable = config.GetBindable(OsuSetting.ShowStoryboard) - }, - new SettingsCheckbox - { - LabelText = "Rotate cursor when dragging", - Bindable = config.GetBindable(OsuSetting.CursorRotation) - }, - new SettingsEnumDropdown - { - LabelText = "Screenshot format", - Bindable = config.GetBindable(OsuSetting.ScreenshotFormat) - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Graphics +{ + public class DetailSettings : SettingsSubsection + { + protected override string Header => "Detail Settings"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = "Storyboards", + Bindable = config.GetBindable(OsuSetting.ShowStoryboard) + }, + new SettingsCheckbox + { + LabelText = "Rotate cursor when dragging", + Bindable = config.GetBindable(OsuSetting.CursorRotation) + }, + new SettingsEnumDropdown + { + LabelText = "Screenshot format", + Bindable = config.GetBindable(OsuSetting.ScreenshotFormat) + } + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 8f775e686a..1f87a635de 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -1,76 +1,76 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Overlays.Settings.Sections.Graphics -{ - public class LayoutSettings : SettingsSubsection - { - protected override string Header => "Layout"; - - private FillFlowContainer letterboxSettings; - - private Bindable letterboxing; - - private const int transition_duration = 400; - - [BackgroundDependencyLoader] - private void load(FrameworkConfigManager config) - { - letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); - - Children = new Drawable[] - { - new SettingsEnumDropdown - { - LabelText = "Screen mode", - Bindable = config.GetBindable(FrameworkSetting.WindowMode), - }, - new SettingsCheckbox - { - LabelText = "Letterboxing", - Bindable = letterboxing, - }, - letterboxSettings = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - AutoSizeDuration = transition_duration, - AutoSizeEasing = Easing.OutQuint, - Masking = true, - - Children = new Drawable[] - { - new SettingsSlider - { - LabelText = "Horizontal position", - Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionX), - KeyboardStep = 0.1f - }, - new SettingsSlider - { - LabelText = "Vertical position", - Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionY), - KeyboardStep = 0.1f - }, - } - }, - }; - - letterboxing.ValueChanged += isVisible => - { - letterboxSettings.ClearTransforms(); - letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None; - - if (!isVisible) - letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); - }; - letterboxing.TriggerChange(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Overlays.Settings.Sections.Graphics +{ + public class LayoutSettings : SettingsSubsection + { + protected override string Header => "Layout"; + + private FillFlowContainer letterboxSettings; + + private Bindable letterboxing; + + private const int transition_duration = 400; + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager config) + { + letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); + + Children = new Drawable[] + { + new SettingsEnumDropdown + { + LabelText = "Screen mode", + Bindable = config.GetBindable(FrameworkSetting.WindowMode), + }, + new SettingsCheckbox + { + LabelText = "Letterboxing", + Bindable = letterboxing, + }, + letterboxSettings = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + AutoSizeDuration = transition_duration, + AutoSizeEasing = Easing.OutQuint, + Masking = true, + + Children = new Drawable[] + { + new SettingsSlider + { + LabelText = "Horizontal position", + Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionX), + KeyboardStep = 0.1f + }, + new SettingsSlider + { + LabelText = "Vertical position", + Bindable = config.GetBindable(FrameworkSetting.LetterboxPositionY), + KeyboardStep = 0.1f + }, + } + }, + }; + + letterboxing.ValueChanged += isVisible => + { + letterboxSettings.ClearTransforms(); + letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None; + + if (!isVisible) + letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); + }; + letterboxing.TriggerChange(); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs index e779538fc8..71d2b31946 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.Graphics -{ - public class MainMenuSettings : SettingsSubsection - { - protected override string Header => "User Interface"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new[] - { - new SettingsCheckbox - { - LabelText = "Parallax", - Bindable = config.GetBindable(OsuSetting.MenuParallax) - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Graphics +{ + public class MainMenuSettings : SettingsSubsection + { + protected override string Header => "User Interface"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new[] + { + new SettingsCheckbox + { + LabelText = "Parallax", + Bindable = config.GetBindable(OsuSetting.MenuParallax) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 7e9ff018c4..5f3c7aa7e9 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.Graphics -{ - public class RendererSettings : SettingsSubsection - { - protected override string Header => "Renderer"; - - [BackgroundDependencyLoader] - private void load(FrameworkConfigManager config, OsuConfigManager osuConfig) - { - // NOTE: Compatability mode omitted - Children = new Drawable[] - { - // TODO: this needs to be a custom dropdown at some point - new SettingsEnumDropdown - { - LabelText = "Frame limiter", - Bindable = config.GetBindable(FrameworkSetting.FrameSync) - }, - new SettingsCheckbox - { - LabelText = "Show FPS", - Bindable = osuConfig.GetBindable(OsuSetting.ShowFpsDisplay) - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Graphics +{ + public class RendererSettings : SettingsSubsection + { + protected override string Header => "Renderer"; + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager config, OsuConfigManager osuConfig) + { + // NOTE: Compatability mode omitted + Children = new Drawable[] + { + // TODO: this needs to be a custom dropdown at some point + new SettingsEnumDropdown + { + LabelText = "Frame limiter", + Bindable = config.GetBindable(FrameworkSetting.FrameSync) + }, + new SettingsCheckbox + { + LabelText = "Show FPS", + Bindable = osuConfig.GetBindable(OsuSetting.ShowFpsDisplay) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index a34a76ff40..1ce25bf517 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.Graphics; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class GraphicsSection : SettingsSection - { - public override string Header => "Graphics"; - public override FontAwesome Icon => FontAwesome.fa_laptop; - - public GraphicsSection() - { - Children = new Drawable[] - { - new RendererSettings(), - new LayoutSettings(), - new DetailSettings(), - new MainMenuSettings(), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.Graphics; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class GraphicsSection : SettingsSection + { + public override string Header => "Graphics"; + public override FontAwesome Icon => FontAwesome.fa_laptop; + + public GraphicsSection() + { + Children = new Drawable[] + { + new RendererSettings(), + new LayoutSettings(), + new DetailSettings(), + new MainMenuSettings(), + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs index 49ac650f92..456d1c9a2f 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; - -namespace osu.Game.Overlays.Settings.Sections.Input -{ - public class KeyboardSettings : SettingsSubsection - { - protected override string Header => "Keyboard"; - - public KeyboardSettings(KeyBindingOverlay keyConfig) - { - Children = new Drawable[] - { - new SettingsButton - { - Text = "Key Configuration", - Action = keyConfig.ToggleVisibility - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; + +namespace osu.Game.Overlays.Settings.Sections.Input +{ + public class KeyboardSettings : SettingsSubsection + { + protected override string Header => "Keyboard"; + + public KeyboardSettings(KeyBindingOverlay keyConfig) + { + Children = new Drawable[] + { + new SettingsButton + { + Text = "Key Configuration", + Action = keyConfig.ToggleVisibility + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index c368b8fea7..c4fced922f 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -1,145 +1,145 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Input; -using osu.Game.Configuration; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings.Sections.Input -{ - public class MouseSettings : SettingsSubsection - { - protected override string Header => "Mouse"; - - private readonly BindableBool rawInputToggle = new BindableBool(); - private Bindable activeInputHandlers; - private SensitivitySetting sensitivity; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager osuConfig, FrameworkConfigManager config) - { - Children = new Drawable[] - { - new SettingsCheckbox - { - LabelText = "Raw Input", - Bindable = rawInputToggle - }, - sensitivity = new SensitivitySetting - { - LabelText = "Cursor Sensitivity", - Bindable = config.GetBindable(FrameworkSetting.CursorSensitivity) - }, - new SettingsCheckbox - { - LabelText = "Map absolute input to window", - Bindable = config.GetBindable(FrameworkSetting.MapAbsoluteInputToWindow) - }, - new SettingsEnumDropdown - { - LabelText = "Confine mouse cursor to window", - Bindable = config.GetBindable(FrameworkSetting.ConfineMouseMode), - }, - new SettingsCheckbox - { - LabelText = "Disable mouse wheel during gameplay", - Bindable = osuConfig.GetBindable(OsuSetting.MouseDisableWheel) - }, - new SettingsCheckbox - { - LabelText = "Disable mouse buttons during gameplay", - Bindable = osuConfig.GetBindable(OsuSetting.MouseDisableButtons) - }, - }; - - rawInputToggle.ValueChanged += enabled => - { - // this is temporary until we support per-handler settings. - const string raw_mouse_handler = @"OpenTKRawMouseHandler"; - const string standard_mouse_handler = @"OpenTKMouseHandler"; - - activeInputHandlers.Value = enabled ? - activeInputHandlers.Value.Replace(standard_mouse_handler, raw_mouse_handler) : - activeInputHandlers.Value.Replace(raw_mouse_handler, standard_mouse_handler); - }; - - activeInputHandlers = config.GetBindable(FrameworkSetting.ActiveInputHandlers); - activeInputHandlers.ValueChanged += handlers => - { - bool raw = handlers.Contains("Raw"); - rawInputToggle.Value = raw; - sensitivity.Bindable.Disabled = !raw; - }; - - activeInputHandlers.TriggerChange(); - } - - private class SensitivitySetting : SettingsSlider - { - public override Bindable Bindable - { - get { return ((SensitivitySlider)Control).Sensitivity; } - - set - { - BindableDouble doubleValue = (BindableDouble)value; - - // create a second layer of bindable so we can only handle state changes when not being dragged. - ((SensitivitySlider)Control).Sensitivity = doubleValue; - - // this bindable will still act as the "interactive" bindable displayed during a drag. - base.Bindable = new BindableDouble(doubleValue.Value) - { - Default = doubleValue.Default, - MinValue = doubleValue.MinValue, - MaxValue = doubleValue.MaxValue - }; - - // one-way binding to update the sliderbar with changes from external actions. - doubleValue.DisabledChanged += disabled => base.Bindable.Disabled = disabled; - doubleValue.ValueChanged += newValue => base.Bindable.Value = newValue; - } - } - - public SensitivitySetting() - { - KeyboardStep = 0.01f; - } - } - - private class SensitivitySlider : OsuSliderBar - { - public Bindable Sensitivity; - - public SensitivitySlider() - { - Current.ValueChanged += newValue => - { - if (!isDragging && Sensitivity != null) - Sensitivity.Value = newValue; - }; - } - - private bool isDragging; - - protected override bool OnDragStart(InputState state) - { - isDragging = true; - return base.OnDragStart(state); - } - - protected override bool OnDragEnd(InputState state) - { - isDragging = false; - Current.TriggerChange(); - - return base.OnDragEnd(state); - } - - public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : Current.Value.ToString(@"0.##x"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings.Sections.Input +{ + public class MouseSettings : SettingsSubsection + { + protected override string Header => "Mouse"; + + private readonly BindableBool rawInputToggle = new BindableBool(); + private Bindable activeInputHandlers; + private SensitivitySetting sensitivity; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager osuConfig, FrameworkConfigManager config) + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = "Raw Input", + Bindable = rawInputToggle + }, + sensitivity = new SensitivitySetting + { + LabelText = "Cursor Sensitivity", + Bindable = config.GetBindable(FrameworkSetting.CursorSensitivity) + }, + new SettingsCheckbox + { + LabelText = "Map absolute input to window", + Bindable = config.GetBindable(FrameworkSetting.MapAbsoluteInputToWindow) + }, + new SettingsEnumDropdown + { + LabelText = "Confine mouse cursor to window", + Bindable = config.GetBindable(FrameworkSetting.ConfineMouseMode), + }, + new SettingsCheckbox + { + LabelText = "Disable mouse wheel during gameplay", + Bindable = osuConfig.GetBindable(OsuSetting.MouseDisableWheel) + }, + new SettingsCheckbox + { + LabelText = "Disable mouse buttons during gameplay", + Bindable = osuConfig.GetBindable(OsuSetting.MouseDisableButtons) + }, + }; + + rawInputToggle.ValueChanged += enabled => + { + // this is temporary until we support per-handler settings. + const string raw_mouse_handler = @"OpenTKRawMouseHandler"; + const string standard_mouse_handler = @"OpenTKMouseHandler"; + + activeInputHandlers.Value = enabled ? + activeInputHandlers.Value.Replace(standard_mouse_handler, raw_mouse_handler) : + activeInputHandlers.Value.Replace(raw_mouse_handler, standard_mouse_handler); + }; + + activeInputHandlers = config.GetBindable(FrameworkSetting.ActiveInputHandlers); + activeInputHandlers.ValueChanged += handlers => + { + bool raw = handlers.Contains("Raw"); + rawInputToggle.Value = raw; + sensitivity.Bindable.Disabled = !raw; + }; + + activeInputHandlers.TriggerChange(); + } + + private class SensitivitySetting : SettingsSlider + { + public override Bindable Bindable + { + get { return ((SensitivitySlider)Control).Sensitivity; } + + set + { + BindableDouble doubleValue = (BindableDouble)value; + + // create a second layer of bindable so we can only handle state changes when not being dragged. + ((SensitivitySlider)Control).Sensitivity = doubleValue; + + // this bindable will still act as the "interactive" bindable displayed during a drag. + base.Bindable = new BindableDouble(doubleValue.Value) + { + Default = doubleValue.Default, + MinValue = doubleValue.MinValue, + MaxValue = doubleValue.MaxValue + }; + + // one-way binding to update the sliderbar with changes from external actions. + doubleValue.DisabledChanged += disabled => base.Bindable.Disabled = disabled; + doubleValue.ValueChanged += newValue => base.Bindable.Value = newValue; + } + } + + public SensitivitySetting() + { + KeyboardStep = 0.01f; + } + } + + private class SensitivitySlider : OsuSliderBar + { + public Bindable Sensitivity; + + public SensitivitySlider() + { + Current.ValueChanged += newValue => + { + if (!isDragging && Sensitivity != null) + Sensitivity.Value = newValue; + }; + } + + private bool isDragging; + + protected override bool OnDragStart(InputState state) + { + isDragging = true; + return base.OnDragStart(state); + } + + protected override bool OnDragEnd(InputState state) + { + isDragging = false; + Current.TriggerChange(); + + return base.OnDragEnd(state); + } + + public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : Current.Value.ToString(@"0.##x"); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index ae4167fe52..df94c1ad08 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.Input; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class InputSection : SettingsSection - { - public override string Header => "Input"; - public override FontAwesome Icon => FontAwesome.fa_keyboard_o; - - public InputSection(KeyBindingOverlay keyConfig) - { - Children = new Drawable[] - { - new MouseSettings(), - new KeyboardSettings(keyConfig), - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.Input; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class InputSection : SettingsSection + { + public override string Header => "Input"; + public override FontAwesome Icon => FontAwesome.fa_keyboard_o; + + public InputSection(KeyBindingOverlay keyConfig) + { + Children = new Drawable[] + { + new MouseSettings(), + new KeyboardSettings(keyConfig), + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DeleteAllBeatmapsDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DeleteAllBeatmapsDialog.cs index 027b5b5aeb..ebabdd7a5d 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DeleteAllBeatmapsDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DeleteAllBeatmapsDialog.cs @@ -1,32 +1,32 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics; -using osu.Game.Overlays.Dialog; - -namespace osu.Game.Overlays.Settings.Sections.Maintenance -{ - public class DeleteAllBeatmapsDialog : PopupDialog - { - public DeleteAllBeatmapsDialog(Action deleteAction) - { - BodyText = "Everything?"; - - Icon = FontAwesome.fa_trash_o; - HeaderText = @"Confirm deletion of"; - Buttons = new PopupDialogButton[] - { - new PopupDialogOkButton - { - Text = @"Yes. Go for it.", - Action = deleteAction - }, - new PopupDialogCancelButton - { - Text = @"No! Abort mission!", - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class DeleteAllBeatmapsDialog : PopupDialog + { + public DeleteAllBeatmapsDialog(Action deleteAction) + { + BodyText = "Everything?"; + + Icon = FontAwesome.fa_trash_o; + HeaderText = @"Confirm deletion of"; + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"Yes. Go for it.", + Action = deleteAction + }, + new PopupDialogCancelButton + { + Text = @"No! Abort mission!", + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index d9fedd0225..93d1986e3a 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -1,73 +1,73 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using System.Threading.Tasks; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings.Sections.Maintenance -{ - public class GeneralSettings : SettingsSubsection - { - private TriangleButton importButton; - private TriangleButton deleteButton; - private TriangleButton restoreButton; - private TriangleButton undeleteButton; - - protected override string Header => "General"; - - [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps, DialogOverlay dialogOverlay) - { - Children = new Drawable[] - { - importButton = new SettingsButton - { - Text = "Import beatmaps from stable", - Action = () => - { - importButton.Enabled.Value = false; - beatmaps.ImportFromStable().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); - } - }, - deleteButton = new DangerousSettingsButton - { - Text = "Delete ALL beatmaps", - Action = () => - { - dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => - { - deleteButton.Enabled.Value = false; - Task.Run(() => beatmaps.Delete(beatmaps.GetAllUsableBeatmapSets())).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true)); - })); - } - }, - restoreButton = new SettingsButton - { - Text = "Restore all hidden difficulties", - Action = () => - { - restoreButton.Enabled.Value = false; - Task.Run(() => - { - foreach (var b in beatmaps.QueryBeatmaps(b => b.Hidden).ToList()) - beatmaps.Restore(b); - }).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true)); - } - }, - undeleteButton = new SettingsButton - { - Text = "Restore all recently deleted beatmaps", - Action = () => - { - undeleteButton.Enabled.Value = false; - Task.Run(() => beatmaps.Undelete(beatmaps.QueryBeatmapSets(b => b.DeletePending).ToList())).ContinueWith(t => Schedule(() => undeleteButton.Enabled.Value = true)); - } - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class GeneralSettings : SettingsSubsection + { + private TriangleButton importButton; + private TriangleButton deleteButton; + private TriangleButton restoreButton; + private TriangleButton undeleteButton; + + protected override string Header => "General"; + + [BackgroundDependencyLoader] + private void load(BeatmapManager beatmaps, DialogOverlay dialogOverlay) + { + Children = new Drawable[] + { + importButton = new SettingsButton + { + Text = "Import beatmaps from stable", + Action = () => + { + importButton.Enabled.Value = false; + beatmaps.ImportFromStable().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); + } + }, + deleteButton = new DangerousSettingsButton + { + Text = "Delete ALL beatmaps", + Action = () => + { + dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => + { + deleteButton.Enabled.Value = false; + Task.Run(() => beatmaps.Delete(beatmaps.GetAllUsableBeatmapSets())).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true)); + })); + } + }, + restoreButton = new SettingsButton + { + Text = "Restore all hidden difficulties", + Action = () => + { + restoreButton.Enabled.Value = false; + Task.Run(() => + { + foreach (var b in beatmaps.QueryBeatmaps(b => b.Hidden).ToList()) + beatmaps.Restore(b); + }).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true)); + } + }, + undeleteButton = new SettingsButton + { + Text = "Restore all recently deleted beatmaps", + Action = () => + { + undeleteButton.Enabled.Value = false; + Task.Run(() => beatmaps.Undelete(beatmaps.QueryBeatmapSets(b => b.DeletePending).ToList())).ContinueWith(t => Schedule(() => undeleteButton.Enabled.Value = true)); + } + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs index 808b32f881..aa933ca188 100644 --- a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.Maintenance; -using OpenTK; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class MaintenanceSection : SettingsSection - { - public override string Header => "Maintenance"; - public override FontAwesome Icon => FontAwesome.fa_wrench; - - public MaintenanceSection() - { - FlowContent.Spacing = new Vector2(0, 5); - Children = new Drawable[] - { - new GeneralSettings() - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings.Sections.Maintenance; +using OpenTK; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class MaintenanceSection : SettingsSection + { + public override string Header => "Maintenance"; + public override FontAwesome Icon => FontAwesome.fa_wrench; + + public MaintenanceSection() + { + FlowContent.Spacing = new Vector2(0, 5); + Children = new Drawable[] + { + new GeneralSettings() + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs index a002b8516c..28cb503288 100644 --- a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs +++ b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class OnlineSection : SettingsSection - { - public override string Header => "Online"; - public override FontAwesome Icon => FontAwesome.fa_globe; - - public OnlineSection() - { - Children = new Drawable[] - { - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class OnlineSection : SettingsSection + { + public override string Header => "Online"; + public override FontAwesome Icon => FontAwesome.fa_globe; + + public OnlineSection() + { + Children = new Drawable[] + { + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index a2215035dd..930b3c1eaa 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -1,82 +1,82 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Skinning; -using OpenTK; - -namespace osu.Game.Overlays.Settings.Sections -{ - public class SkinSection : SettingsSection - { - private SettingsDropdown skinDropdown; - - public override string Header => "Skin"; - - public override FontAwesome Icon => FontAwesome.fa_paint_brush; - - private SkinManager skins; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config, SkinManager skins) - { - this.skins = skins; - - FlowContent.Spacing = new Vector2(0, 5); - Children = new Drawable[] - { - skinDropdown = new SettingsDropdown(), - new SettingsSlider - { - LabelText = "Menu cursor size", - Bindable = config.GetBindable(OsuSetting.MenuCursorSize), - KeyboardStep = 0.1f - }, - new SettingsSlider - { - LabelText = "Gameplay cursor size", - Bindable = config.GetBindable(OsuSetting.GameplayCursorSize), - KeyboardStep = 0.1f - }, - new SettingsCheckbox - { - LabelText = "Adjust gameplay cursor size based on current beatmap", - Bindable = config.GetBindable(OsuSetting.AutoCursorSize) - }, - }; - - skins.ItemAdded += onItemsChanged; - skins.ItemRemoved += onItemsChanged; - - reloadSkins(); - - skinDropdown.Bindable = config.GetBindable(OsuSetting.Skin); - } - - private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); - - private void onItemsChanged(SkinInfo _) => Schedule(reloadSkins); - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (skins != null) - { - skins.ItemAdded -= onItemsChanged; - skins.ItemRemoved -= onItemsChanged; - } - } - - private class SizeSlider : OsuSliderBar - { - public override string TooltipText => Current.Value.ToString(@"0.##x"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Skinning; +using OpenTK; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class SkinSection : SettingsSection + { + private SettingsDropdown skinDropdown; + + public override string Header => "Skin"; + + public override FontAwesome Icon => FontAwesome.fa_paint_brush; + + private SkinManager skins; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, SkinManager skins) + { + this.skins = skins; + + FlowContent.Spacing = new Vector2(0, 5); + Children = new Drawable[] + { + skinDropdown = new SettingsDropdown(), + new SettingsSlider + { + LabelText = "Menu cursor size", + Bindable = config.GetBindable(OsuSetting.MenuCursorSize), + KeyboardStep = 0.1f + }, + new SettingsSlider + { + LabelText = "Gameplay cursor size", + Bindable = config.GetBindable(OsuSetting.GameplayCursorSize), + KeyboardStep = 0.1f + }, + new SettingsCheckbox + { + LabelText = "Adjust gameplay cursor size based on current beatmap", + Bindable = config.GetBindable(OsuSetting.AutoCursorSize) + }, + }; + + skins.ItemAdded += onItemsChanged; + skins.ItemRemoved += onItemsChanged; + + reloadSkins(); + + skinDropdown.Bindable = config.GetBindable(OsuSetting.Skin); + } + + private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); + + private void onItemsChanged(SkinInfo _) => Schedule(reloadSkins); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (skins != null) + { + skins.ItemAdded -= onItemsChanged; + skins.ItemRemoved -= onItemsChanged; + } + } + + private class SizeSlider : OsuSliderBar + { + public override string TooltipText => Current.Value.ToString(@"0.##x"); + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsButton.cs b/osu.Game/Overlays/Settings/SettingsButton.cs index 9fccc6476f..6689664988 100644 --- a/osu.Game/Overlays/Settings/SettingsButton.cs +++ b/osu.Game/Overlays/Settings/SettingsButton.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsButton : TriangleButton - { - public SettingsButton() - { - RelativeSizeAxes = Axes.X; - Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsButton : TriangleButton + { + public SettingsButton() + { + RelativeSizeAxes = Axes.X; + Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS }; + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsCheckbox.cs b/osu.Game/Overlays/Settings/SettingsCheckbox.cs index 773cb04313..2c0f510cb8 100644 --- a/osu.Game/Overlays/Settings/SettingsCheckbox.cs +++ b/osu.Game/Overlays/Settings/SettingsCheckbox.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsCheckbox : SettingsItem - { - private OsuCheckbox checkbox; - - protected override Drawable CreateControl() => checkbox = new OsuCheckbox(); - - public override string LabelText - { - get { return checkbox.LabelText; } - set { checkbox.LabelText = value; } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsCheckbox : SettingsItem + { + private OsuCheckbox checkbox; + + protected override Drawable CreateControl() => checkbox = new OsuCheckbox(); + + public override string LabelText + { + get { return checkbox.LabelText; } + set { checkbox.LabelText = value; } + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsDropdown.cs b/osu.Game/Overlays/Settings/SettingsDropdown.cs index 4356d8a599..33a8af7d91 100644 --- a/osu.Game/Overlays/Settings/SettingsDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsDropdown.cs @@ -1,37 +1,37 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsDropdown : SettingsItem - { - private Dropdown dropdown; - - private IEnumerable> items = new KeyValuePair[] { }; - public IEnumerable> Items - { - get - { - return items; - } - set - { - items = value; - if (dropdown != null) - dropdown.Items = value; - } - } - - protected override Drawable CreateControl() => dropdown = new OsuDropdown - { - Margin = new MarginPadding { Top = 5 }, - RelativeSizeAxes = Axes.X, - Items = Items, - }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsDropdown : SettingsItem + { + private Dropdown dropdown; + + private IEnumerable> items = new KeyValuePair[] { }; + public IEnumerable> Items + { + get + { + return items; + } + set + { + items = value; + if (dropdown != null) + dropdown.Items = value; + } + } + + protected override Drawable CreateControl() => dropdown = new OsuDropdown + { + Margin = new MarginPadding { Top = 5 }, + RelativeSizeAxes = Axes.X, + Items = Items, + }; + } +} diff --git a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs index d4a5a1a861..64811137a6 100644 --- a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsEnumDropdown : SettingsDropdown - { - protected override Drawable CreateControl() => new OsuEnumDropdown - { - Margin = new MarginPadding { Top = 5 }, - RelativeSizeAxes = Axes.X, - }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsEnumDropdown : SettingsDropdown + { + protected override Drawable CreateControl() => new OsuEnumDropdown + { + Margin = new MarginPadding { Top = 5 }, + RelativeSizeAxes = Axes.X, + }; + } +} diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index b9581bac8a..900f03fe7b 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -1,73 +1,73 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Development; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsFooter : FillFlowContainer - { - [BackgroundDependencyLoader] - private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - Padding = new MarginPadding { Top = 20, Bottom = 30 }; - - var modes = new List(); - - foreach (var ruleset in rulesets.AvailableRulesets) - { - var icon = new ConstrainedIconContainer - { - Icon = ruleset.CreateInstance().CreateIcon(), - Colour = Color4.Gray, - Size = new Vector2(20), - }; - - modes.Add(icon); - } - - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Direction = FillDirection.Full, - AutoSizeAxes = Axes.Both, - Children = modes, - Spacing = new Vector2(5), - Padding = new MarginPadding { Bottom = 10 }, - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = game.Name, - TextSize = 18, - Font = @"Exo2.0-Bold", - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - TextSize = 14, - Text = game.Version, - Colour = DebugUtils.IsDebug ? colours.Red : Color4.White, - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Development; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsFooter : FillFlowContainer + { + [BackgroundDependencyLoader] + private void load(OsuGameBase game, OsuColour colours, RulesetStore rulesets) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Padding = new MarginPadding { Top = 20, Bottom = 30 }; + + var modes = new List(); + + foreach (var ruleset in rulesets.AvailableRulesets) + { + var icon = new ConstrainedIconContainer + { + Icon = ruleset.CreateInstance().CreateIcon(), + Colour = Color4.Gray, + Size = new Vector2(20), + }; + + modes.Add(icon); + } + + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Full, + AutoSizeAxes = Axes.Both, + Children = modes, + Spacing = new Vector2(5), + Padding = new MarginPadding { Bottom = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = game.Name, + TextSize = 18, + Font = @"Exo2.0-Bold", + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + TextSize = 14, + Text = game.Version, + Colour = DebugUtils.IsDebug ? colours.Red : Color4.White, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsHeader.cs b/osu.Game/Overlays/Settings/SettingsHeader.cs index 7cdd24b48d..fc86a38689 100644 --- a/osu.Game/Overlays/Settings/SettingsHeader.cs +++ b/osu.Game/Overlays/Settings/SettingsHeader.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsHeader : Container - { - private readonly string heading; - private readonly string subheading; - - public SettingsHeader(string heading, string subheading) - { - this.heading = heading; - this.subheading = subheading; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = heading, - TextSize = 40, - Margin = new MarginPadding - { - Left = SettingsOverlay.CONTENT_MARGINS, - Top = Toolbar.Toolbar.TOOLTIP_HEIGHT - }, - }, - new OsuSpriteText - { - Colour = colours.Pink, - Text = subheading, - TextSize = 18, - Margin = new MarginPadding - { - Left = SettingsOverlay.CONTENT_MARGINS, - Bottom = 30 - }, - }, - } - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsHeader : Container + { + private readonly string heading; + private readonly string subheading; + + public SettingsHeader(string heading, string subheading) + { + this.heading = heading; + this.subheading = subheading; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = heading, + TextSize = 40, + Margin = new MarginPadding + { + Left = SettingsOverlay.CONTENT_MARGINS, + Top = Toolbar.Toolbar.TOOLTIP_HEIGHT + }, + }, + new OsuSpriteText + { + Colour = colours.Pink, + Text = subheading, + TextSize = 18, + Margin = new MarginPadding + { + Left = SettingsOverlay.CONTENT_MARGINS, + Bottom = 30 + }, + }, + } + } + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index cc290fe1bb..4c1ea1f32e 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -1,211 +1,211 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Allocation; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK; - -namespace osu.Game.Overlays.Settings -{ - public abstract class SettingsItem : Container, IFilterable - { - protected abstract Drawable CreateControl(); - - protected Drawable Control { get; } - - private IHasCurrentValue controlWithCurrent => Control as IHasCurrentValue; - - protected override Container Content => FlowContent; - - protected readonly FillFlowContainer FlowContent; - - private SpriteText text; - - private readonly RestoreDefaultValueButton restoreDefaultButton; - - public bool ShowsDefaultIndicator = true; - - public virtual string LabelText - { - get { return text?.Text ?? string.Empty; } - set - { - if (text == null) - { - // construct lazily for cases where the label is not needed (may be provided by the Control). - Add(text = new OsuSpriteText()); - FlowContent.SetLayoutPosition(text, -1); - } - - text.Text = value; - } - } - - // hold a reference to the provided bindable so we don't have to in every settings section. - private Bindable bindable; - - public virtual Bindable Bindable - { - get { return bindable; } - - set - { - bindable = value; - controlWithCurrent?.Current.BindTo(bindable); - if (ShowsDefaultIndicator) - { - restoreDefaultButton.Bindable = bindable.GetBoundCopy(); - restoreDefaultButton.Bindable.TriggerChange(); - } - } - } - - public IEnumerable FilterTerms => new[] { LabelText }; - - public bool MatchingFilter - { - set - { - // probably needs a better transition. - this.FadeTo(value ? 1 : 0); - } - } - - protected SettingsItem() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Right = SettingsOverlay.CONTENT_MARGINS }; - - InternalChildren = new Drawable[] - { - restoreDefaultButton = new RestoreDefaultValueButton(), - FlowContent = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS }, - Child = Control = CreateControl() - }, - }; - } - - [BackgroundDependencyLoader] - private void load() - { - if (controlWithCurrent != null) - controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; }; - } - - private class RestoreDefaultValueButton : Container, IHasTooltip - { - private Bindable bindable; - - public Bindable Bindable - { - get { return bindable; } - set - { - bindable = value; - bindable.ValueChanged += newValue => UpdateState(); - bindable.DisabledChanged += disabled => UpdateState(); - } - } - - private Color4 buttonColour; - - private bool hovering; - - public RestoreDefaultValueButton() - { - RelativeSizeAxes = Axes.Y; - Width = SettingsOverlay.CONTENT_MARGINS; - Alpha = 0f; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - buttonColour = colour.Yellow; - - Child = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - CornerRadius = 3, - Masking = true, - Colour = buttonColour, - EdgeEffect = new EdgeEffectParameters - { - Colour = buttonColour.Opacity(0.1f), - Type = EdgeEffectType.Glow, - Radius = 2, - }, - Size = new Vector2(0.33f, 0.8f), - Child = new Box { RelativeSizeAxes = Axes.Both }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - UpdateState(); - } - - public string TooltipText => "Revert to default"; - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => true; - - protected override bool OnClick(InputState state) - { - if (bindable != null && !bindable.Disabled) - bindable.SetDefault(); - return true; - } - - protected override bool OnHover(InputState state) - { - hovering = true; - UpdateState(); - return false; - } - - protected override void OnHoverLost(InputState state) - { - hovering = false; - UpdateState(); - } - - public void SetButtonColour(Color4 buttonColour) - { - this.buttonColour = buttonColour; - UpdateState(); - } - - public void UpdateState() - { - if (bindable == null) - return; - - this.FadeTo(bindable.IsDefault ? 0f : - hovering && !bindable.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); - this.FadeColour(bindable.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Allocation; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK; + +namespace osu.Game.Overlays.Settings +{ + public abstract class SettingsItem : Container, IFilterable + { + protected abstract Drawable CreateControl(); + + protected Drawable Control { get; } + + private IHasCurrentValue controlWithCurrent => Control as IHasCurrentValue; + + protected override Container Content => FlowContent; + + protected readonly FillFlowContainer FlowContent; + + private SpriteText text; + + private readonly RestoreDefaultValueButton restoreDefaultButton; + + public bool ShowsDefaultIndicator = true; + + public virtual string LabelText + { + get { return text?.Text ?? string.Empty; } + set + { + if (text == null) + { + // construct lazily for cases where the label is not needed (may be provided by the Control). + Add(text = new OsuSpriteText()); + FlowContent.SetLayoutPosition(text, -1); + } + + text.Text = value; + } + } + + // hold a reference to the provided bindable so we don't have to in every settings section. + private Bindable bindable; + + public virtual Bindable Bindable + { + get { return bindable; } + + set + { + bindable = value; + controlWithCurrent?.Current.BindTo(bindable); + if (ShowsDefaultIndicator) + { + restoreDefaultButton.Bindable = bindable.GetBoundCopy(); + restoreDefaultButton.Bindable.TriggerChange(); + } + } + } + + public IEnumerable FilterTerms => new[] { LabelText }; + + public bool MatchingFilter + { + set + { + // probably needs a better transition. + this.FadeTo(value ? 1 : 0); + } + } + + protected SettingsItem() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Right = SettingsOverlay.CONTENT_MARGINS }; + + InternalChildren = new Drawable[] + { + restoreDefaultButton = new RestoreDefaultValueButton(), + FlowContent = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS }, + Child = Control = CreateControl() + }, + }; + } + + [BackgroundDependencyLoader] + private void load() + { + if (controlWithCurrent != null) + controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; }; + } + + private class RestoreDefaultValueButton : Container, IHasTooltip + { + private Bindable bindable; + + public Bindable Bindable + { + get { return bindable; } + set + { + bindable = value; + bindable.ValueChanged += newValue => UpdateState(); + bindable.DisabledChanged += disabled => UpdateState(); + } + } + + private Color4 buttonColour; + + private bool hovering; + + public RestoreDefaultValueButton() + { + RelativeSizeAxes = Axes.Y; + Width = SettingsOverlay.CONTENT_MARGINS; + Alpha = 0f; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + buttonColour = colour.Yellow; + + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + CornerRadius = 3, + Masking = true, + Colour = buttonColour, + EdgeEffect = new EdgeEffectParameters + { + Colour = buttonColour.Opacity(0.1f), + Type = EdgeEffectType.Glow, + Radius = 2, + }, + Size = new Vector2(0.33f, 0.8f), + Child = new Box { RelativeSizeAxes = Axes.Both }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + UpdateState(); + } + + public string TooltipText => "Revert to default"; + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => true; + + protected override bool OnClick(InputState state) + { + if (bindable != null && !bindable.Disabled) + bindable.SetDefault(); + return true; + } + + protected override bool OnHover(InputState state) + { + hovering = true; + UpdateState(); + return false; + } + + protected override void OnHoverLost(InputState state) + { + hovering = false; + UpdateState(); + } + + public void SetButtonColour(Color4 buttonColour) + { + this.buttonColour = buttonColour; + UpdateState(); + } + + public void UpdateState() + { + if (bindable == null) + return; + + this.FadeTo(bindable.IsDefault ? 0f : + hovering && !bindable.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); + this.FadeColour(bindable.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsLabel.cs b/osu.Game/Overlays/Settings/SettingsLabel.cs index db7712c873..2df4073191 100644 --- a/osu.Game/Overlays/Settings/SettingsLabel.cs +++ b/osu.Game/Overlays/Settings/SettingsLabel.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsLabel : SettingsItem - { - protected override Drawable CreateControl() => null; - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - Colour = colour.Gray6; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsLabel : SettingsItem + { + protected override Drawable CreateControl() => null; + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + Colour = colour.Gray6; + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 546b40a1f2..9555f0fbc5 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -1,91 +1,91 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Overlays.Settings -{ - public abstract class SettingsSection : Container, IHasFilterableChildren - { - protected FillFlowContainer FlowContent; - protected override Container Content => FlowContent; - - public abstract FontAwesome Icon { get; } - public abstract string Header { get; } - - public IEnumerable FilterableChildren => Children.OfType(); - public IEnumerable FilterTerms => new[] { Header }; - - private const int header_size = 26; - private const int header_margin = 25; - private const int border_size = 2; - - public bool MatchingFilter - { - set { this.FadeTo(value ? 1 : 0); } - } - - protected SettingsSection() - { - Margin = new MarginPadding { Top = 20 }; - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - - FlowContent = new FillFlowContainer - { - Margin = new MarginPadding - { - Top = header_size + header_margin - }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 30), - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AddRangeInternal(new Drawable[] - { - new Box - { - Colour = new Color4(0, 0, 0, 255), - RelativeSizeAxes = Axes.X, - Height = border_size, - }, - new Container - { - Padding = new MarginPadding - { - Top = 20 + border_size, - Bottom = 10, - }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - new OsuSpriteText - { - TextSize = header_size, - Text = Header, - Colour = colours.Yellow, - Margin = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS } - }, - FlowContent - } - }, - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Overlays.Settings +{ + public abstract class SettingsSection : Container, IHasFilterableChildren + { + protected FillFlowContainer FlowContent; + protected override Container Content => FlowContent; + + public abstract FontAwesome Icon { get; } + public abstract string Header { get; } + + public IEnumerable FilterableChildren => Children.OfType(); + public IEnumerable FilterTerms => new[] { Header }; + + private const int header_size = 26; + private const int header_margin = 25; + private const int border_size = 2; + + public bool MatchingFilter + { + set { this.FadeTo(value ? 1 : 0); } + } + + protected SettingsSection() + { + Margin = new MarginPadding { Top = 20 }; + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + + FlowContent = new FillFlowContainer + { + Margin = new MarginPadding + { + Top = header_size + header_margin + }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 30), + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AddRangeInternal(new Drawable[] + { + new Box + { + Colour = new Color4(0, 0, 0, 255), + RelativeSizeAxes = Axes.X, + Height = border_size, + }, + new Container + { + Padding = new MarginPadding + { + Top = 20 + border_size, + Bottom = 10, + }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + new OsuSpriteText + { + TextSize = header_size, + Text = Header, + Colour = colours.Yellow, + Margin = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS } + }, + FlowContent + } + }, + }); + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsSlider.cs b/osu.Game/Overlays/Settings/SettingsSlider.cs index 708d9437a5..a3698c36e6 100644 --- a/osu.Game/Overlays/Settings/SettingsSlider.cs +++ b/osu.Game/Overlays/Settings/SettingsSlider.cs @@ -1,36 +1,36 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsSlider : SettingsSlider> - where T : struct, IEquatable, IComparable, IConvertible - { - } - - public class SettingsSlider : SettingsItem - where T : struct, IEquatable, IComparable, IConvertible - where U : OsuSliderBar, new() - { - protected override Drawable CreateControl() => new U - { - Margin = new MarginPadding { Top = 5, Bottom = 5 }, - RelativeSizeAxes = Axes.X - }; - - public float KeyboardStep; - - [BackgroundDependencyLoader] - private void load() - { - var slider = Control as U; - if (slider != null) - slider.KeyboardStep = KeyboardStep; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsSlider : SettingsSlider> + where T : struct, IEquatable, IComparable, IConvertible + { + } + + public class SettingsSlider : SettingsItem + where T : struct, IEquatable, IComparable, IConvertible + where U : OsuSliderBar, new() + { + protected override Drawable CreateControl() => new U + { + Margin = new MarginPadding { Top = 5, Bottom = 5 }, + RelativeSizeAxes = Axes.X + }; + + public float KeyboardStep; + + [BackgroundDependencyLoader] + private void load() + { + var slider = Control as U; + if (slider != null) + slider.KeyboardStep = KeyboardStep; + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs index f0191f3af1..82589a99bd 100644 --- a/osu.Game/Overlays/Settings/SettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs @@ -1,62 +1,62 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; - -namespace osu.Game.Overlays.Settings -{ - public abstract class SettingsSubsection : FillFlowContainer, IHasFilterableChildren - { - protected override Container Content => FlowContent; - - protected readonly FillFlowContainer FlowContent; - - protected abstract string Header { get; } - - public IEnumerable FilterableChildren => Children.OfType(); - public IEnumerable FilterTerms => new[] { Header }; - public bool MatchingFilter - { - set - { - this.FadeTo(value ? 1 : 0); - } - } - - protected SettingsSubsection() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - - FlowContent = new FillFlowContainer - { - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }; - } - - [BackgroundDependencyLoader] - private void load() - { - AddRangeInternal(new Drawable[] - { - new OsuSpriteText - { - Text = Header.ToUpper(), - Margin = new MarginPadding { Bottom = 10, Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS }, - Font = @"Exo2.0-Black", - }, - FlowContent - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; + +namespace osu.Game.Overlays.Settings +{ + public abstract class SettingsSubsection : FillFlowContainer, IHasFilterableChildren + { + protected override Container Content => FlowContent; + + protected readonly FillFlowContainer FlowContent; + + protected abstract string Header { get; } + + public IEnumerable FilterableChildren => Children.OfType(); + public IEnumerable FilterTerms => new[] { Header }; + public bool MatchingFilter + { + set + { + this.FadeTo(value ? 1 : 0); + } + } + + protected SettingsSubsection() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + + FlowContent = new FillFlowContainer + { + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }; + } + + [BackgroundDependencyLoader] + private void load() + { + AddRangeInternal(new Drawable[] + { + new OsuSpriteText + { + Text = Header.ToUpper(), + Margin = new MarginPadding { Bottom = 10, Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS }, + Font = @"Exo2.0-Black", + }, + FlowContent + }); + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsTextBox.cs b/osu.Game/Overlays/Settings/SettingsTextBox.cs index 8f901ef252..ce9218bbe7 100644 --- a/osu.Game/Overlays/Settings/SettingsTextBox.cs +++ b/osu.Game/Overlays/Settings/SettingsTextBox.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings -{ - public class SettingsTextBox : SettingsItem - { - protected override Drawable CreateControl() => new OsuTextBox(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsTextBox : SettingsItem + { + protected override Drawable CreateControl() => new OsuTextBox(); + } +} diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs index 8792eafdbe..50452a7110 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/Settings/Sidebar.cs @@ -1,140 +1,140 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Framework.Threading; -using osu.Game.Overlays.Toolbar; - -namespace osu.Game.Overlays.Settings -{ - public class Sidebar : Container, IStateful - { - private readonly FillFlowContainer content; - public const float DEFAULT_WIDTH = ToolbarButton.WIDTH; - public const int EXPANDED_WIDTH = 200; - - public event Action StateChanged; - - protected override Container Content => content; - - public Sidebar() - { - RelativeSizeAxes = Axes.Y; - InternalChildren = new Drawable[] - { - new Box - { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both, - }, - new SidebarScrollContainer - { - Children = new[] - { - content = new FillFlowContainer - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - } - } - }, - }; - } - - private ScheduledDelegate expandEvent; - private ExpandedState state; - - protected override bool OnHover(InputState state) - { - queueExpandIfHovering(); - return true; - } - - protected override void OnHoverLost(InputState state) - { - expandEvent?.Cancel(); - lastHoveredButton = null; - State = ExpandedState.Contracted; - - base.OnHoverLost(state); - } - - protected override bool OnMouseMove(InputState state) - { - queueExpandIfHovering(); - return base.OnMouseMove(state); - } - - private class SidebarScrollContainer : ScrollContainer - { - public SidebarScrollContainer() - { - Content.Anchor = Anchor.CentreLeft; - Content.Origin = Anchor.CentreLeft; - RelativeSizeAxes = Axes.Both; - } - } - - public ExpandedState State - { - get { return state; } - set - { - expandEvent?.Cancel(); - - if (state == value) return; - - state = value; - - switch (state) - { - default: - this.ResizeTo(new Vector2(DEFAULT_WIDTH, Height), 500, Easing.OutQuint); - break; - case ExpandedState.Expanded: - this.ResizeTo(new Vector2(EXPANDED_WIDTH, Height), 500, Easing.OutQuint); - break; - } - - StateChanged?.Invoke(State); - } - } - - private Drawable lastHoveredButton; - - private Drawable hoveredButton => content.Children.FirstOrDefault(c => c.IsHovered); - - private void queueExpandIfHovering() - { - // only expand when we hover a different button. - if (lastHoveredButton == hoveredButton) return; - - if (!IsHovered) return; - - if (State != ExpandedState.Expanded) - { - expandEvent?.Cancel(); - expandEvent = Scheduler.AddDelayed(() => State = ExpandedState.Expanded, 750); - } - - lastHoveredButton = hoveredButton; - } - } - - public enum ExpandedState - { - Contracted, - Expanded, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.Threading; +using osu.Game.Overlays.Toolbar; + +namespace osu.Game.Overlays.Settings +{ + public class Sidebar : Container, IStateful + { + private readonly FillFlowContainer content; + public const float DEFAULT_WIDTH = ToolbarButton.WIDTH; + public const int EXPANDED_WIDTH = 200; + + public event Action StateChanged; + + protected override Container Content => content; + + public Sidebar() + { + RelativeSizeAxes = Axes.Y; + InternalChildren = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + }, + new SidebarScrollContainer + { + Children = new[] + { + content = new FillFlowContainer + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + } + } + }, + }; + } + + private ScheduledDelegate expandEvent; + private ExpandedState state; + + protected override bool OnHover(InputState state) + { + queueExpandIfHovering(); + return true; + } + + protected override void OnHoverLost(InputState state) + { + expandEvent?.Cancel(); + lastHoveredButton = null; + State = ExpandedState.Contracted; + + base.OnHoverLost(state); + } + + protected override bool OnMouseMove(InputState state) + { + queueExpandIfHovering(); + return base.OnMouseMove(state); + } + + private class SidebarScrollContainer : ScrollContainer + { + public SidebarScrollContainer() + { + Content.Anchor = Anchor.CentreLeft; + Content.Origin = Anchor.CentreLeft; + RelativeSizeAxes = Axes.Both; + } + } + + public ExpandedState State + { + get { return state; } + set + { + expandEvent?.Cancel(); + + if (state == value) return; + + state = value; + + switch (state) + { + default: + this.ResizeTo(new Vector2(DEFAULT_WIDTH, Height), 500, Easing.OutQuint); + break; + case ExpandedState.Expanded: + this.ResizeTo(new Vector2(EXPANDED_WIDTH, Height), 500, Easing.OutQuint); + break; + } + + StateChanged?.Invoke(State); + } + } + + private Drawable lastHoveredButton; + + private Drawable hoveredButton => content.Children.FirstOrDefault(c => c.IsHovered); + + private void queueExpandIfHovering() + { + // only expand when we hover a different button. + if (lastHoveredButton == hoveredButton) return; + + if (!IsHovered) return; + + if (State != ExpandedState.Expanded) + { + expandEvent?.Cancel(); + expandEvent = Scheduler.AddDelayed(() => State = ExpandedState.Expanded, 750); + } + + lastHoveredButton = hoveredButton; + } + } + + public enum ExpandedState + { + Contracted, + Expanded, + } +} diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index a4c35204f4..0a3a30480b 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -1,128 +1,128 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Settings -{ - public class SidebarButton : OsuButton - { - private readonly SpriteIcon drawableIcon; - private readonly SpriteText headerText; - private readonly Box selectionIndicator; - private readonly Container text; - public new Action Action; - - private SettingsSection section; - public SettingsSection Section - { - get - { - return section; - } - set - { - section = value; - headerText.Text = value.Header; - drawableIcon.Icon = value.Icon; - } - } - - private bool selected; - public bool Selected - { - get { return selected; } - set - { - selected = value; - if (selected) - { - selectionIndicator.FadeIn(50); - text.FadeColour(Color4.White, 50); - } - else - { - selectionIndicator.FadeOut(50); - text.FadeColour(OsuColour.Gray(0.6f), 50); - } - } - } - - public SidebarButton() - { - BackgroundColour = OsuColour.Gray(60); - Background.Alpha = 0; - - Height = Sidebar.DEFAULT_WIDTH; - RelativeSizeAxes = Axes.X; - - AddRange(new Drawable[] - { - text = new Container - { - Width = Sidebar.DEFAULT_WIDTH, - RelativeSizeAxes = Axes.Y, - Colour = OsuColour.Gray(0.6f), - Children = new Drawable[] - { - headerText = new OsuSpriteText - { - Position = new Vector2(Sidebar.DEFAULT_WIDTH + 10, 0), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - drawableIcon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(20), - }, - } - }, - selectionIndicator = new Box - { - Alpha = 0, - RelativeSizeAxes = Axes.Y, - Width = 5, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - } - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - selectionIndicator.Colour = colours.Yellow; - } - - protected override bool OnClick(InputState state) - { - Action?.Invoke(section); - return base.OnClick(state); - } - - protected override bool OnHover(InputState state) - { - Background.FadeTo(0.4f, 200); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - Background.FadeTo(0, 200); - base.OnHoverLost(state); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + public class SidebarButton : OsuButton + { + private readonly SpriteIcon drawableIcon; + private readonly SpriteText headerText; + private readonly Box selectionIndicator; + private readonly Container text; + public new Action Action; + + private SettingsSection section; + public SettingsSection Section + { + get + { + return section; + } + set + { + section = value; + headerText.Text = value.Header; + drawableIcon.Icon = value.Icon; + } + } + + private bool selected; + public bool Selected + { + get { return selected; } + set + { + selected = value; + if (selected) + { + selectionIndicator.FadeIn(50); + text.FadeColour(Color4.White, 50); + } + else + { + selectionIndicator.FadeOut(50); + text.FadeColour(OsuColour.Gray(0.6f), 50); + } + } + } + + public SidebarButton() + { + BackgroundColour = OsuColour.Gray(60); + Background.Alpha = 0; + + Height = Sidebar.DEFAULT_WIDTH; + RelativeSizeAxes = Axes.X; + + AddRange(new Drawable[] + { + text = new Container + { + Width = Sidebar.DEFAULT_WIDTH, + RelativeSizeAxes = Axes.Y, + Colour = OsuColour.Gray(0.6f), + Children = new Drawable[] + { + headerText = new OsuSpriteText + { + Position = new Vector2(Sidebar.DEFAULT_WIDTH + 10, 0), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + drawableIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + }, + } + }, + selectionIndicator = new Box + { + Alpha = 0, + RelativeSizeAxes = Axes.Y, + Width = 5, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + selectionIndicator.Colour = colours.Yellow; + } + + protected override bool OnClick(InputState state) + { + Action?.Invoke(section); + return base.OnClick(state); + } + + protected override bool OnHover(InputState state) + { + Background.FadeTo(0.4f, 200); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + Background.FadeTo(0, 200); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index c003046242..55326b53ed 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -1,224 +1,224 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings; - -namespace osu.Game.Overlays -{ - public abstract class SettingsOverlay : OsuFocusedOverlayContainer - { - public const float CONTENT_MARGINS = 15; - - public const float TRANSITION_LENGTH = 600; - - private const float sidebar_width = Sidebar.DEFAULT_WIDTH; - - protected const float WIDTH = 400; - - private const float sidebar_padding = 10; - - protected Container ContentContainer; - - protected override Container Content => ContentContainer; - - protected Sidebar Sidebar; - private SidebarButton selectedSidebarButton; - - protected SettingsSectionsContainer SectionsContainer; - - private SearchTextBox searchTextBox; - - /// - /// Provide a source for the toolbar height. - /// - public Func GetToolbarHeight; - - private readonly bool showSidebar; - - protected Box Background; - - protected SettingsOverlay(bool showSidebar) - { - this.showSidebar = showSidebar; - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; - } - - protected virtual IEnumerable CreateSections() => null; - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = ContentContainer = new Container - { - Width = WIDTH, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - Background = new Box - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Scale = new Vector2(2, 1), // over-extend to the left for transitions - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.6f, - }, - SectionsContainer = new SettingsSectionsContainer - { - Masking = true, - RelativeSizeAxes = Axes.Both, - ExpandableHeader = CreateHeader(), - FixedHeader = searchTextBox = new SearchTextBox - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Width = 0.95f, - Margin = new MarginPadding - { - Top = 20, - Bottom = 20 - }, - Exit = Hide, - }, - Footer = CreateFooter() - }, - } - }; - - if (showSidebar) - { - AddInternal(Sidebar = new Sidebar { Width = sidebar_width }); - - SectionsContainer.SelectedSection.ValueChanged += section => - { - selectedSidebarButton.Selected = false; - selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section); - selectedSidebarButton.Selected = true; - }; - } - - searchTextBox.Current.ValueChanged += newValue => SectionsContainer.SearchContainer.SearchTerm = newValue; - - CreateSections()?.ForEach(AddSection); - } - - protected void AddSection(SettingsSection section) - { - SectionsContainer.Add(section); - - if (Sidebar != null) - { - var button = new SidebarButton - { - Section = section, - Action = s => - { - SectionsContainer.ScrollTo(s); - Sidebar.State = ExpandedState.Contracted; - }, - }; - - Sidebar.Add(button); - - if (selectedSidebarButton == null) - { - selectedSidebarButton = Sidebar.Children.First(); - selectedSidebarButton.Selected = true; - } - } - } - - protected virtual Drawable CreateHeader() => new Container(); - - protected virtual Drawable CreateFooter() => new Container(); - - protected override void PopIn() - { - base.PopIn(); - - ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); - - Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); - this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); - - searchTextBox.HoldFocus = true; - } - - protected virtual float ExpandedPosition => 0; - - protected override void PopOut() - { - base.PopOut(); - - ContentContainer.MoveToX(-WIDTH, TRANSITION_LENGTH, Easing.OutQuint); - - Sidebar?.MoveToX(-sidebar_width, TRANSITION_LENGTH, Easing.OutQuint); - this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); - - searchTextBox.HoldFocus = false; - if (searchTextBox.HasFocus) - GetContainingInputManager().ChangeFocus(null); - } - - public override bool AcceptsFocus => true; - - protected override void OnFocus(InputState state) - { - GetContainingInputManager().ChangeFocus(searchTextBox); - base.OnFocus(state); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - ContentContainer.Margin = new MarginPadding { Left = Sidebar?.DrawWidth ?? 0 }; - ContentContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; - } - - protected class SettingsSectionsContainer : SectionsContainer - { - public SearchContainer SearchContainer; - - protected override FlowContainer CreateScrollContentContainer() - => SearchContainer = new SearchContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - }; - - public SettingsSectionsContainer() - { - HeaderBackground = new Box - { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both - }; - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - // no null check because the usage of this class is strict - HeaderBackground.Alpha = -ExpandableHeader.Y / ExpandableHeader.LayoutSize.Y * 0.5f; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Overlays +{ + public abstract class SettingsOverlay : OsuFocusedOverlayContainer + { + public const float CONTENT_MARGINS = 15; + + public const float TRANSITION_LENGTH = 600; + + private const float sidebar_width = Sidebar.DEFAULT_WIDTH; + + protected const float WIDTH = 400; + + private const float sidebar_padding = 10; + + protected Container ContentContainer; + + protected override Container Content => ContentContainer; + + protected Sidebar Sidebar; + private SidebarButton selectedSidebarButton; + + protected SettingsSectionsContainer SectionsContainer; + + private SearchTextBox searchTextBox; + + /// + /// Provide a source for the toolbar height. + /// + public Func GetToolbarHeight; + + private readonly bool showSidebar; + + protected Box Background; + + protected SettingsOverlay(bool showSidebar) + { + this.showSidebar = showSidebar; + RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; + } + + protected virtual IEnumerable CreateSections() => null; + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = ContentContainer = new Container + { + Width = WIDTH, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + Background = new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Scale = new Vector2(2, 1), // over-extend to the left for transitions + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f, + }, + SectionsContainer = new SettingsSectionsContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + ExpandableHeader = CreateHeader(), + FixedHeader = searchTextBox = new SearchTextBox + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Width = 0.95f, + Margin = new MarginPadding + { + Top = 20, + Bottom = 20 + }, + Exit = Hide, + }, + Footer = CreateFooter() + }, + } + }; + + if (showSidebar) + { + AddInternal(Sidebar = new Sidebar { Width = sidebar_width }); + + SectionsContainer.SelectedSection.ValueChanged += section => + { + selectedSidebarButton.Selected = false; + selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section); + selectedSidebarButton.Selected = true; + }; + } + + searchTextBox.Current.ValueChanged += newValue => SectionsContainer.SearchContainer.SearchTerm = newValue; + + CreateSections()?.ForEach(AddSection); + } + + protected void AddSection(SettingsSection section) + { + SectionsContainer.Add(section); + + if (Sidebar != null) + { + var button = new SidebarButton + { + Section = section, + Action = s => + { + SectionsContainer.ScrollTo(s); + Sidebar.State = ExpandedState.Contracted; + }, + }; + + Sidebar.Add(button); + + if (selectedSidebarButton == null) + { + selectedSidebarButton = Sidebar.Children.First(); + selectedSidebarButton.Selected = true; + } + } + } + + protected virtual Drawable CreateHeader() => new Container(); + + protected virtual Drawable CreateFooter() => new Container(); + + protected override void PopIn() + { + base.PopIn(); + + ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); + + Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); + this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); + + searchTextBox.HoldFocus = true; + } + + protected virtual float ExpandedPosition => 0; + + protected override void PopOut() + { + base.PopOut(); + + ContentContainer.MoveToX(-WIDTH, TRANSITION_LENGTH, Easing.OutQuint); + + Sidebar?.MoveToX(-sidebar_width, TRANSITION_LENGTH, Easing.OutQuint); + this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); + + searchTextBox.HoldFocus = false; + if (searchTextBox.HasFocus) + GetContainingInputManager().ChangeFocus(null); + } + + public override bool AcceptsFocus => true; + + protected override void OnFocus(InputState state) + { + GetContainingInputManager().ChangeFocus(searchTextBox); + base.OnFocus(state); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + ContentContainer.Margin = new MarginPadding { Left = Sidebar?.DrawWidth ?? 0 }; + ContentContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; + } + + protected class SettingsSectionsContainer : SectionsContainer + { + public SearchContainer SearchContainer; + + protected override FlowContainer CreateScrollContentContainer() + => SearchContainer = new SearchContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + }; + + public SettingsSectionsContainer() + { + HeaderBackground = new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both + }; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // no null check because the usage of this class is strict + HeaderBackground.Alpha = -ExpandableHeader.Y / ExpandableHeader.LayoutSize.Y * 0.5f; + } + } + } +} diff --git a/osu.Game/Overlays/Social/FilterControl.cs b/osu.Game/Overlays/Social/FilterControl.cs index 382b3fd0e7..1b9ce1033f 100644 --- a/osu.Game/Overlays/Social/FilterControl.cs +++ b/osu.Game/Overlays/Social/FilterControl.cs @@ -1,32 +1,32 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.SearchableList; - -namespace osu.Game.Overlays.Social -{ - public class FilterControl : SearchableListFilterControl - { - protected override Color4 BackgroundColour => OsuColour.FromHex(@"47253a"); - protected override SocialSortCriteria DefaultTab => SocialSortCriteria.Rank; - - public FilterControl() - { - Tabs.Margin = new MarginPadding { Top = 10 }; - } - } - - public enum SocialSortCriteria - { - Rank, - Name, - Location, - //[Description("Time Zone")] - //TimeZone, - //[Description("World Map")] - //WorldMap, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.SearchableList; + +namespace osu.Game.Overlays.Social +{ + public class FilterControl : SearchableListFilterControl + { + protected override Color4 BackgroundColour => OsuColour.FromHex(@"47253a"); + protected override SocialSortCriteria DefaultTab => SocialSortCriteria.Rank; + + public FilterControl() + { + Tabs.Margin = new MarginPadding { Top = 10 }; + } + } + + public enum SocialSortCriteria + { + Rank, + Name, + Location, + //[Description("Time Zone")] + //TimeZone, + //[Description("World Map")] + //WorldMap, + } +} diff --git a/osu.Game/Overlays/Social/Header.cs b/osu.Game/Overlays/Social/Header.cs index 89224e1315..df97bdf448 100644 --- a/osu.Game/Overlays/Social/Header.cs +++ b/osu.Game/Overlays/Social/Header.cs @@ -1,65 +1,65 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Overlays.SearchableList; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Framework.Allocation; -using System.ComponentModel; - -namespace osu.Game.Overlays.Social -{ - public class Header : SearchableListHeader - { - private OsuSpriteText browser; - - protected override Color4 BackgroundColour => OsuColour.FromHex(@"38202e"); - - protected override SocialTab DefaultTab => SocialTab.AllPlayers; - protected override FontAwesome Icon => FontAwesome.fa_users; - - protected override Drawable CreateHeaderText() - { - return new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new[] - { - new OsuSpriteText - { - Text = "social ", - TextSize = 25, - }, - browser = new OsuSpriteText - { - Text = "browser", - TextSize = 25, - Font = @"Exo2.0-Light", - }, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - browser.Colour = colours.Pink; - } - } - - public enum SocialTab - { - [Description("All Players")] - AllPlayers, - [Description("Friends")] - Friends, - //[Description("Team Members")] - //TeamMembers, - //[Description("Chat Channels")] - //ChatChannels, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Overlays.SearchableList; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Framework.Allocation; +using System.ComponentModel; + +namespace osu.Game.Overlays.Social +{ + public class Header : SearchableListHeader + { + private OsuSpriteText browser; + + protected override Color4 BackgroundColour => OsuColour.FromHex(@"38202e"); + + protected override SocialTab DefaultTab => SocialTab.AllPlayers; + protected override FontAwesome Icon => FontAwesome.fa_users; + + protected override Drawable CreateHeaderText() + { + return new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = "social ", + TextSize = 25, + }, + browser = new OsuSpriteText + { + Text = "browser", + TextSize = 25, + Font = @"Exo2.0-Light", + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + browser.Colour = colours.Pink; + } + } + + public enum SocialTab + { + [Description("All Players")] + AllPlayers, + [Description("Friends")] + Friends, + //[Description("Team Members")] + //TeamMembers, + //[Description("Chat Channels")] + //ChatChannels, + } +} diff --git a/osu.Game/Overlays/Social/SocialGridPanel.cs b/osu.Game/Overlays/Social/SocialGridPanel.cs index f9fbce123d..0051e1e0e5 100644 --- a/osu.Game/Overlays/Social/SocialGridPanel.cs +++ b/osu.Game/Overlays/Social/SocialGridPanel.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Users; - -namespace osu.Game.Overlays.Social -{ - public class SocialGridPanel : SocialPanel - { - public SocialGridPanel(User user) : base(user) - { - Width = 300; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Users; + +namespace osu.Game.Overlays.Social +{ + public class SocialGridPanel : SocialPanel + { + public SocialGridPanel(User user) : base(user) + { + Width = 300; + } + } +} diff --git a/osu.Game/Overlays/Social/SocialListPanel.cs b/osu.Game/Overlays/Social/SocialListPanel.cs index 0f102005d6..ff39e3b236 100644 --- a/osu.Game/Overlays/Social/SocialListPanel.cs +++ b/osu.Game/Overlays/Social/SocialListPanel.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Users; - -namespace osu.Game.Overlays.Social -{ - public class SocialListPanel : SocialPanel - { - public SocialListPanel(User user) : base(user) - { - RelativeSizeAxes = Axes.X; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Users; + +namespace osu.Game.Overlays.Social +{ + public class SocialListPanel : SocialPanel + { + public SocialListPanel(User user) : base(user) + { + RelativeSizeAxes = Axes.X; + } + } +} diff --git a/osu.Game/Overlays/Social/SocialPanel.cs b/osu.Game/Overlays/Social/SocialPanel.cs index 54fb88f929..2411db7535 100644 --- a/osu.Game/Overlays/Social/SocialPanel.cs +++ b/osu.Game/Overlays/Social/SocialPanel.cs @@ -1,60 +1,60 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Users; - -namespace osu.Game.Overlays.Social -{ - public class SocialPanel : UserPanel - { - private const double hover_transition_time = 400; - - public SocialPanel(User user) : base(user) - { - } - - private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 2f, - Colour = Color4.Black.Opacity(0.25f), - }; - - private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 5f), - Radius = 10f, - Colour = Color4.Black.Opacity(0.3f), - }; - - protected override bool OnHover(InputState state) - { - Content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint); - Content.MoveToY(-4, hover_transition_time, Easing.OutQuint); - - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - Content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint); - Content.MoveToY(0, hover_transition_time, Easing.OutQuint); - - base.OnHoverLost(state); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - this.FadeInFromZero(200, Easing.Out); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Users; + +namespace osu.Game.Overlays.Social +{ + public class SocialPanel : UserPanel + { + private const double hover_transition_time = 400; + + public SocialPanel(User user) : base(user) + { + } + + private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 2f, + Colour = Color4.Black.Opacity(0.25f), + }; + + private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 5f), + Radius = 10f, + Colour = Color4.Black.Opacity(0.3f), + }; + + protected override bool OnHover(InputState state) + { + Content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint); + Content.MoveToY(-4, hover_transition_time, Easing.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + Content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint); + Content.MoveToY(0, hover_transition_time, Easing.OutQuint); + + base.OnHoverLost(state); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.FadeInFromZero(200, Easing.Out); + } + } +} diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs index ddcb933e5d..d224d4c92d 100644 --- a/osu.Game/Overlays/SocialOverlay.cs +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -1,213 +1,213 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.SearchableList; -using osu.Game.Overlays.Social; -using osu.Game.Users; -using osu.Framework.Configuration; -using osu.Framework.Threading; - -namespace osu.Game.Overlays -{ - public class SocialOverlay : SearchableListOverlay, IOnlineComponent - { - private APIAccess api; - private readonly LoadingAnimation loading; - private FillFlowContainer panels; - - protected override Color4 BackgroundColour => OsuColour.FromHex(@"60284b"); - protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"672b51"); - protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"5c2648"); - - protected override SearchableListHeader CreateHeader() => new Header(); - protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); - - private IEnumerable users; - public IEnumerable Users - { - get { return users; } - set - { - if (users?.Equals(value) ?? false) - return; - - users = value?.ToList(); - } - } - - public SocialOverlay() - { - FirstWaveColour = OsuColour.FromHex(@"cb5fa0"); - SecondWaveColour = OsuColour.FromHex(@"b04384"); - ThirdWaveColour = OsuColour.FromHex(@"9b2b6e"); - FourthWaveColour = OsuColour.FromHex(@"6d214d"); - - Add(loading = new LoadingAnimation()); - - Filter.Search.Current.ValueChanged += text => - { - if (!string.IsNullOrEmpty(text)) - { - // force searching in players until searching for friends is supported - Header.Tabs.Current.Value = SocialTab.AllPlayers; - - if (Filter.Tabs.Current.Value != SocialSortCriteria.Rank) - Filter.Tabs.Current.Value = SocialSortCriteria.Rank; - } - }; - - Header.Tabs.Current.ValueChanged += tab => Scheduler.AddOnce(updateSearch); - - Filter.Tabs.Current.ValueChanged += sortCriteria => Scheduler.AddOnce(updateSearch); - - Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels; - Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += sortOrder => Scheduler.AddOnce(updateSearch); - - currentQuery.ValueChanged += query => - { - queryChangedDebounce?.Cancel(); - - if (string.IsNullOrEmpty(query)) - Scheduler.AddOnce(updateSearch); - else - queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500); - }; - - currentQuery.BindTo(Filter.Search.Current); - } - - [BackgroundDependencyLoader] - private void load(APIAccess api) - { - this.api = api; - api.Register(this); - } - - private void recreatePanels(PanelDisplayStyle displayStyle) - { - clearPanels(); - - if (Users == null) - return; - - var newPanels = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10f), - Margin = new MarginPadding { Top = 10 }, - ChildrenEnumerable = Users.Select(u => - { - SocialPanel panel; - switch (displayStyle) - { - case PanelDisplayStyle.Grid: - panel = new SocialGridPanel(u) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - }; - break; - default: - panel = new SocialListPanel(u); - break; - } - panel.Status.BindTo(u.Status); - return panel; - }) - }; - - LoadComponentAsync(newPanels, f => - { - if(panels != null) - ScrollFlow.Remove(panels); - - ScrollFlow.Add(panels = newPanels); - }); - } - - private APIRequest getUsersRequest; - - private readonly Bindable currentQuery = new Bindable(); - - private ScheduledDelegate queryChangedDebounce; - - private void updateSearch() - { - queryChangedDebounce?.Cancel(); - - if (!IsLoaded) - return; - - Users = null; - clearPanels(); - loading.Hide(); - getUsersRequest?.Cancel(); - - if (api?.IsLoggedIn != true) - return; - - switch (Header.Tabs.Current.Value) - { - case SocialTab.Friends: - var friendRequest = new GetFriendsRequest(); // TODO filter arguments? - friendRequest.Success += updateUsers; - api.Queue(getUsersRequest = friendRequest); - break; - default: - var userRequest = new GetUsersRequest(); // TODO filter arguments! - userRequest.Success += response => updateUsers(response.Select(r => r.User)); - api.Queue(getUsersRequest = userRequest); - break; - } - loading.Show(); - } - - private void updateUsers(IEnumerable newUsers) - { - Users = newUsers; - loading.Hide(); - recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); - } - - private void clearPanels() - { - if (panels != null) - { - panels.Expire(); - panels = null; - } - } - - public void APIStateChanged(APIAccess api, APIState state) - { - switch (state) - { - case APIState.Online: - Scheduler.AddOnce(updateSearch); - break; - default: - Users = null; - clearPanels(); - break; - } - } - } - - public enum SortDirection - { - Ascending, - Descending - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.SearchableList; +using osu.Game.Overlays.Social; +using osu.Game.Users; +using osu.Framework.Configuration; +using osu.Framework.Threading; + +namespace osu.Game.Overlays +{ + public class SocialOverlay : SearchableListOverlay, IOnlineComponent + { + private APIAccess api; + private readonly LoadingAnimation loading; + private FillFlowContainer panels; + + protected override Color4 BackgroundColour => OsuColour.FromHex(@"60284b"); + protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"672b51"); + protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"5c2648"); + + protected override SearchableListHeader CreateHeader() => new Header(); + protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); + + private IEnumerable users; + public IEnumerable Users + { + get { return users; } + set + { + if (users?.Equals(value) ?? false) + return; + + users = value?.ToList(); + } + } + + public SocialOverlay() + { + FirstWaveColour = OsuColour.FromHex(@"cb5fa0"); + SecondWaveColour = OsuColour.FromHex(@"b04384"); + ThirdWaveColour = OsuColour.FromHex(@"9b2b6e"); + FourthWaveColour = OsuColour.FromHex(@"6d214d"); + + Add(loading = new LoadingAnimation()); + + Filter.Search.Current.ValueChanged += text => + { + if (!string.IsNullOrEmpty(text)) + { + // force searching in players until searching for friends is supported + Header.Tabs.Current.Value = SocialTab.AllPlayers; + + if (Filter.Tabs.Current.Value != SocialSortCriteria.Rank) + Filter.Tabs.Current.Value = SocialSortCriteria.Rank; + } + }; + + Header.Tabs.Current.ValueChanged += tab => Scheduler.AddOnce(updateSearch); + + Filter.Tabs.Current.ValueChanged += sortCriteria => Scheduler.AddOnce(updateSearch); + + Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels; + Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += sortOrder => Scheduler.AddOnce(updateSearch); + + currentQuery.ValueChanged += query => + { + queryChangedDebounce?.Cancel(); + + if (string.IsNullOrEmpty(query)) + Scheduler.AddOnce(updateSearch); + else + queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500); + }; + + currentQuery.BindTo(Filter.Search.Current); + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + api.Register(this); + } + + private void recreatePanels(PanelDisplayStyle displayStyle) + { + clearPanels(); + + if (Users == null) + return; + + var newPanels = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10f), + Margin = new MarginPadding { Top = 10 }, + ChildrenEnumerable = Users.Select(u => + { + SocialPanel panel; + switch (displayStyle) + { + case PanelDisplayStyle.Grid: + panel = new SocialGridPanel(u) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }; + break; + default: + panel = new SocialListPanel(u); + break; + } + panel.Status.BindTo(u.Status); + return panel; + }) + }; + + LoadComponentAsync(newPanels, f => + { + if(panels != null) + ScrollFlow.Remove(panels); + + ScrollFlow.Add(panels = newPanels); + }); + } + + private APIRequest getUsersRequest; + + private readonly Bindable currentQuery = new Bindable(); + + private ScheduledDelegate queryChangedDebounce; + + private void updateSearch() + { + queryChangedDebounce?.Cancel(); + + if (!IsLoaded) + return; + + Users = null; + clearPanels(); + loading.Hide(); + getUsersRequest?.Cancel(); + + if (api?.IsLoggedIn != true) + return; + + switch (Header.Tabs.Current.Value) + { + case SocialTab.Friends: + var friendRequest = new GetFriendsRequest(); // TODO filter arguments? + friendRequest.Success += updateUsers; + api.Queue(getUsersRequest = friendRequest); + break; + default: + var userRequest = new GetUsersRequest(); // TODO filter arguments! + userRequest.Success += response => updateUsers(response.Select(r => r.User)); + api.Queue(getUsersRequest = userRequest); + break; + } + loading.Show(); + } + + private void updateUsers(IEnumerable newUsers) + { + Users = newUsers; + loading.Hide(); + recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); + } + + private void clearPanels() + { + if (panels != null) + { + panels.Expire(); + panels = null; + } + } + + public void APIStateChanged(APIAccess api, APIState state) + { + switch (state) + { + case APIState.Online: + Scheduler.AddOnce(updateSearch); + break; + default: + Users = null; + clearPanels(); + break; + } + } + } + + public enum SortDirection + { + Ascending, + Descending + } +} diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 8ac4e44a84..424a457110 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -1,135 +1,135 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Graphics; -using OpenTK; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Overlays.Toolbar -{ - public class Toolbar : OverlayContainer - { - public const float HEIGHT = 40; - public const float TOOLTIP_HEIGHT = 30; - - public Action OnHome; - - private readonly ToolbarUserArea userArea; - - protected override bool BlockPassThroughMouse => false; - - private const double transition_time = 500; - - private const float alpha_hovering = 0.8f; - private const float alpha_normal = 0.6f; - - public Toolbar() - { - Children = new Drawable[] - { - new ToolbarBackground(), - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Children = new Drawable[] - { - new ToolbarSettingsButton(), - new ToolbarHomeButton - { - Action = () => OnHome?.Invoke() - }, - new ToolbarModeSelector() - } - }, - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Direction = FillDirection.Horizontal, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Children = new Drawable[] - { - new ToolbarDirectButton(), - new ToolbarChatButton(), - new ToolbarSocialButton(), - new ToolbarMusicButton(), - //new ToolbarButton - //{ - // Icon = FontAwesome.fa_search - //}, - userArea = new ToolbarUserArea(), - new ToolbarNotificationButton(), - } - } - }; - - RelativeSizeAxes = Axes.X; - Size = new Vector2(1, HEIGHT); - } - - public class ToolbarBackground : Container - { - private readonly Box solidBackground; - private readonly Box gradientBackground; - - public ToolbarBackground() - { - RelativeSizeAxes = Axes.Both; - Children = new Drawable[] - { - solidBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.1f), - Alpha = alpha_normal, - }, - gradientBackground = new Box - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomLeft, - Alpha = 0, - Height = 90, - Colour = ColourInfo.GradientVertical( - OsuColour.Gray(0.1f).Opacity(0.5f), OsuColour.Gray(0.1f).Opacity(0)), - }, - }; - } - - protected override bool OnHover(InputState state) - { - solidBackground.FadeTo(alpha_hovering, transition_time, Easing.OutQuint); - gradientBackground.FadeIn(transition_time, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(InputState state) - { - solidBackground.FadeTo(alpha_normal, transition_time, Easing.OutQuint); - gradientBackground.FadeOut(transition_time, Easing.OutQuint); - } - } - - protected override void PopIn() - { - this.MoveToY(0, transition_time, Easing.OutQuint); - this.FadeIn(transition_time / 2, Easing.OutQuint); - } - - protected override void PopOut() - { - userArea?.LoginOverlay.Hide(); - - this.MoveToY(-DrawSize.Y, transition_time, Easing.OutQuint); - this.FadeOut(transition_time); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Overlays.Toolbar +{ + public class Toolbar : OverlayContainer + { + public const float HEIGHT = 40; + public const float TOOLTIP_HEIGHT = 30; + + public Action OnHome; + + private readonly ToolbarUserArea userArea; + + protected override bool BlockPassThroughMouse => false; + + private const double transition_time = 500; + + private const float alpha_hovering = 0.8f; + private const float alpha_normal = 0.6f; + + public Toolbar() + { + Children = new Drawable[] + { + new ToolbarBackground(), + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] + { + new ToolbarSettingsButton(), + new ToolbarHomeButton + { + Action = () => OnHome?.Invoke() + }, + new ToolbarModeSelector() + } + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Direction = FillDirection.Horizontal, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] + { + new ToolbarDirectButton(), + new ToolbarChatButton(), + new ToolbarSocialButton(), + new ToolbarMusicButton(), + //new ToolbarButton + //{ + // Icon = FontAwesome.fa_search + //}, + userArea = new ToolbarUserArea(), + new ToolbarNotificationButton(), + } + } + }; + + RelativeSizeAxes = Axes.X; + Size = new Vector2(1, HEIGHT); + } + + public class ToolbarBackground : Container + { + private readonly Box solidBackground; + private readonly Box gradientBackground; + + public ToolbarBackground() + { + RelativeSizeAxes = Axes.Both; + Children = new Drawable[] + { + solidBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.1f), + Alpha = alpha_normal, + }, + gradientBackground = new Box + { + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomLeft, + Alpha = 0, + Height = 90, + Colour = ColourInfo.GradientVertical( + OsuColour.Gray(0.1f).Opacity(0.5f), OsuColour.Gray(0.1f).Opacity(0)), + }, + }; + } + + protected override bool OnHover(InputState state) + { + solidBackground.FadeTo(alpha_hovering, transition_time, Easing.OutQuint); + gradientBackground.FadeIn(transition_time, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + solidBackground.FadeTo(alpha_normal, transition_time, Easing.OutQuint); + gradientBackground.FadeOut(transition_time, Easing.OutQuint); + } + } + + protected override void PopIn() + { + this.MoveToY(0, transition_time, Easing.OutQuint); + this.FadeIn(transition_time / 2, Easing.OutQuint); + } + + protected override void PopOut() + { + userArea?.LoginOverlay.Hide(); + + this.MoveToY(-DrawSize.Y, transition_time, Easing.OutQuint); + this.FadeOut(transition_time); + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index f3a25b79a1..c7870a72de 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -1,199 +1,199 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarButton : OsuClickableContainer - { - public const float WIDTH = Toolbar.HEIGHT * 1.4f; - - public void SetIcon(Drawable icon) - { - IconContainer.Icon = icon; - IconContainer.Show(); - } - - public void SetIcon(FontAwesome icon) => SetIcon(new SpriteIcon - { - Size = new Vector2(20), - Icon = icon - }); - - public FontAwesome Icon - { - set { SetIcon(value); } - } - - public string Text - { - get { return DrawableText.Text; } - set - { - DrawableText.Text = value; - } - } - - public string TooltipMain - { - get { return tooltip1.Text; } - set - { - tooltip1.Text = value; - } - } - - public string TooltipSub - { - get { return tooltip2.Text; } - set - { - tooltip2.Text = value; - } - } - - protected virtual Anchor TooltipAnchor => Anchor.TopLeft; - - protected ConstrainedIconContainer IconContainer; - protected SpriteText DrawableText; - protected Box HoverBackground; - private readonly FillFlowContainer tooltipContainer; - private readonly SpriteText tooltip1; - private readonly SpriteText tooltip2; - protected FillFlowContainer Flow; - - public ToolbarButton() : base(HoverSampleSet.Loud) - { - Width = WIDTH; - RelativeSizeAxes = Axes.Y; - - Children = new Drawable[] - { - HoverBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(80).Opacity(180), - Blending = BlendingMode.Additive, - Alpha = 0, - }, - Flow = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Padding = new MarginPadding { Left = Toolbar.HEIGHT / 2, Right = Toolbar.HEIGHT / 2 }, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Children = new Drawable[] - { - IconContainer = new ConstrainedIconContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20), - Alpha = 0, - }, - DrawableText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - }, - }, - tooltipContainer = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.Both, //stops us being considered in parent's autosize - Anchor = (TooltipAnchor & Anchor.x0) > 0 ? Anchor.BottomLeft : Anchor.BottomRight, - Origin = TooltipAnchor, - Position = new Vector2((TooltipAnchor & Anchor.x0) > 0 ? 5 : -5, 5), - Alpha = 0, - Children = new[] - { - tooltip1 = new OsuSpriteText - { - Anchor = TooltipAnchor, - Origin = TooltipAnchor, - Shadow = true, - TextSize = 22, - Font = @"Exo2.0-Bold", - }, - tooltip2 = new OsuSpriteText - { - Anchor = TooltipAnchor, - Origin = TooltipAnchor, - Shadow = true, - TextSize = 16 - } - } - } - }; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; - - protected override bool OnClick(InputState state) - { - HoverBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); - return base.OnClick(state); - } - - protected override bool OnHover(InputState state) - { - HoverBackground.FadeIn(200); - tooltipContainer.FadeIn(100); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - HoverBackground.FadeOut(200); - tooltipContainer.FadeOut(100); - } - } - - public class OpaqueBackground : Container - { - public OpaqueBackground() - { - RelativeSizeAxes = Axes.Both; - Masking = true; - MaskingSmoothness = 0; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(40), - Radius = 5, - }; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(30) - }, - new Triangles - { - RelativeSizeAxes = Axes.Both, - ColourLight = OsuColour.Gray(40), - ColourDark = OsuColour.Gray(20), - }, - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarButton : OsuClickableContainer + { + public const float WIDTH = Toolbar.HEIGHT * 1.4f; + + public void SetIcon(Drawable icon) + { + IconContainer.Icon = icon; + IconContainer.Show(); + } + + public void SetIcon(FontAwesome icon) => SetIcon(new SpriteIcon + { + Size = new Vector2(20), + Icon = icon + }); + + public FontAwesome Icon + { + set { SetIcon(value); } + } + + public string Text + { + get { return DrawableText.Text; } + set + { + DrawableText.Text = value; + } + } + + public string TooltipMain + { + get { return tooltip1.Text; } + set + { + tooltip1.Text = value; + } + } + + public string TooltipSub + { + get { return tooltip2.Text; } + set + { + tooltip2.Text = value; + } + } + + protected virtual Anchor TooltipAnchor => Anchor.TopLeft; + + protected ConstrainedIconContainer IconContainer; + protected SpriteText DrawableText; + protected Box HoverBackground; + private readonly FillFlowContainer tooltipContainer; + private readonly SpriteText tooltip1; + private readonly SpriteText tooltip2; + protected FillFlowContainer Flow; + + public ToolbarButton() : base(HoverSampleSet.Loud) + { + Width = WIDTH; + RelativeSizeAxes = Axes.Y; + + Children = new Drawable[] + { + HoverBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(80).Opacity(180), + Blending = BlendingMode.Additive, + Alpha = 0, + }, + Flow = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding { Left = Toolbar.HEIGHT / 2, Right = Toolbar.HEIGHT / 2 }, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] + { + IconContainer = new ConstrainedIconContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + Alpha = 0, + }, + DrawableText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + }, + }, + tooltipContainer = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.Both, //stops us being considered in parent's autosize + Anchor = (TooltipAnchor & Anchor.x0) > 0 ? Anchor.BottomLeft : Anchor.BottomRight, + Origin = TooltipAnchor, + Position = new Vector2((TooltipAnchor & Anchor.x0) > 0 ? 5 : -5, 5), + Alpha = 0, + Children = new[] + { + tooltip1 = new OsuSpriteText + { + Anchor = TooltipAnchor, + Origin = TooltipAnchor, + Shadow = true, + TextSize = 22, + Font = @"Exo2.0-Bold", + }, + tooltip2 = new OsuSpriteText + { + Anchor = TooltipAnchor, + Origin = TooltipAnchor, + Shadow = true, + TextSize = 16 + } + } + } + }; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + + protected override bool OnClick(InputState state) + { + HoverBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); + return base.OnClick(state); + } + + protected override bool OnHover(InputState state) + { + HoverBackground.FadeIn(200); + tooltipContainer.FadeIn(100); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + HoverBackground.FadeOut(200); + tooltipContainer.FadeOut(100); + } + } + + public class OpaqueBackground : Container + { + public OpaqueBackground() + { + RelativeSizeAxes = Axes.Both; + Masking = true; + MaskingSmoothness = 0; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(40), + Radius = 5, + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(30) + }, + new Triangles + { + RelativeSizeAxes = Axes.Both, + ColourLight = OsuColour.Gray(40), + ColourDark = OsuColour.Gray(20), + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs index fd78db5a8b..3d9b943d16 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarChatButton : ToolbarOverlayToggleButton - { - public ToolbarChatButton() - { - SetIcon(FontAwesome.fa_comments); - } - - [BackgroundDependencyLoader(true)] - private void load(ChatOverlay chat) - { - StateContainer = chat; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarChatButton : ToolbarOverlayToggleButton + { + public ToolbarChatButton() + { + SetIcon(FontAwesome.fa_comments); + } + + [BackgroundDependencyLoader(true)] + private void load(ChatOverlay chat) + { + StateContainer = chat; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs index 5922b6212c..ffc5ecd682 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarDirectButton : ToolbarOverlayToggleButton - { - public ToolbarDirectButton() - { - SetIcon(FontAwesome.fa_osu_chevron_down_o); - } - - [BackgroundDependencyLoader(true)] - private void load(DirectOverlay direct) - { - StateContainer = direct; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarDirectButton : ToolbarOverlayToggleButton + { + public ToolbarDirectButton() + { + SetIcon(FontAwesome.fa_osu_chevron_down_o); + } + + [BackgroundDependencyLoader(true)] + private void load(DirectOverlay direct) + { + StateContainer = direct; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs index 3bce67765a..e79b73e725 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarHomeButton : ToolbarButton - { - public ToolbarHomeButton() - { - Icon = FontAwesome.fa_home; - TooltipMain = "Home"; - TooltipSub = "Return to the main menu"; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarHomeButton : ToolbarButton + { + public ToolbarHomeButton() + { + Icon = FontAwesome.fa_home; + TooltipMain = "Home"; + TooltipSub = "Return to the main menu"; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs index 451af65ce7..90b9abb2e4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs @@ -1,57 +1,57 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarModeButton : ToolbarButton - { - private RulesetInfo ruleset; - public RulesetInfo Ruleset - { - get { return ruleset; } - set - { - ruleset = value; - - var rInstance = ruleset.CreateInstance(); - - TooltipMain = rInstance.Description; - TooltipSub = $"Play some {rInstance.Description}"; - SetIcon(rInstance.CreateIcon()); - } - } - - public bool Active - { - set - { - if (value) - { - IconContainer.Colour = Color4.White; - IconContainer.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = new Color4(255, 194, 224, 100), - Radius = 15, - Roundness = 15, - }; - } - else - { - IconContainer.Colour = new Color4(255, 194, 224, 255); - IconContainer.EdgeEffect = new EdgeEffectParameters(); - } - } - } - - protected override void LoadComplete() - { - base.LoadComplete(); - IconContainer.Scale *= 1.4f; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarModeButton : ToolbarButton + { + private RulesetInfo ruleset; + public RulesetInfo Ruleset + { + get { return ruleset; } + set + { + ruleset = value; + + var rInstance = ruleset.CreateInstance(); + + TooltipMain = rInstance.Description; + TooltipSub = $"Play some {rInstance.Description}"; + SetIcon(rInstance.CreateIcon()); + } + } + + public bool Active + { + set + { + if (value) + { + IconContainer.Colour = Color4.White; + IconContainer.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = new Color4(255, 194, 224, 100), + Radius = 15, + Roundness = 15, + }; + } + else + { + IconContainer.Colour = new Color4(255, 194, 224, 255); + IconContainer.EdgeEffect = new EdgeEffectParameters(); + } + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + IconContainer.Scale *= 1.4f; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 6d58c78c37..05866f7002 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -1,127 +1,127 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Caching; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarModeSelector : Container - { - private const float padding = 10; - - private readonly FillFlowContainer modeButtons; - private readonly Drawable modeButtonLine; - private ToolbarModeButton activeButton; - - private readonly Bindable ruleset = new Bindable(); - - public ToolbarModeSelector() - { - RelativeSizeAxes = Axes.Y; - - Children = new[] - { - new OpaqueBackground(), - modeButtons = new FillFlowContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Direction = FillDirection.Horizontal, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Padding = new MarginPadding { Left = padding, Right = padding }, - }, - modeButtonLine = new Container - { - Size = new Vector2(padding * 2 + ToolbarButton.WIDTH, 3), - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = new Color4(255, 194, 224, 100), - Radius = 15, - Roundness = 15, - }, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - } - } - } - }; - } - - [BackgroundDependencyLoader(true)] - private void load(RulesetStore rulesets, OsuGame game) - { - foreach (var r in rulesets.AvailableRulesets) - { - modeButtons.Add(new ToolbarModeButton - { - Ruleset = r, - Action = delegate - { - ruleset.Value = r; - } - }); - } - - ruleset.ValueChanged += rulesetChanged; - ruleset.DisabledChanged += disabledChanged; - if (game != null) - ruleset.BindTo(game.Ruleset); - else - ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); - } - - public override bool HandleKeyboardInput => !ruleset.Disabled; - public override bool HandleMouseInput => !ruleset.Disabled; - - private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); - - protected override void Update() - { - base.Update(); - Size = new Vector2(modeButtons.DrawSize.X, 1); - } - - private void rulesetChanged(RulesetInfo ruleset) - { - foreach (ToolbarModeButton m in modeButtons.Children.Cast()) - { - bool isActive = m.Ruleset.ID == ruleset.ID; - m.Active = isActive; - if (isActive) - activeButton = m; - } - - activeMode.Invalidate(); - } - - private Cached activeMode = new Cached(); - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - if (!activeMode.IsValid) - { - modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, Easing.OutQuint); - activeMode.Validate(); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Caching; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarModeSelector : Container + { + private const float padding = 10; + + private readonly FillFlowContainer modeButtons; + private readonly Drawable modeButtonLine; + private ToolbarModeButton activeButton; + + private readonly Bindable ruleset = new Bindable(); + + public ToolbarModeSelector() + { + RelativeSizeAxes = Axes.Y; + + Children = new[] + { + new OpaqueBackground(), + modeButtons = new FillFlowContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Direction = FillDirection.Horizontal, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding { Left = padding, Right = padding }, + }, + modeButtonLine = new Container + { + Size = new Vector2(padding * 2 + ToolbarButton.WIDTH, 3), + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = new Color4(255, 194, 224, 100), + Radius = 15, + Roundness = 15, + }, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + } + } + } + }; + } + + [BackgroundDependencyLoader(true)] + private void load(RulesetStore rulesets, OsuGame game) + { + foreach (var r in rulesets.AvailableRulesets) + { + modeButtons.Add(new ToolbarModeButton + { + Ruleset = r, + Action = delegate + { + ruleset.Value = r; + } + }); + } + + ruleset.ValueChanged += rulesetChanged; + ruleset.DisabledChanged += disabledChanged; + if (game != null) + ruleset.BindTo(game.Ruleset); + else + ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); + } + + public override bool HandleKeyboardInput => !ruleset.Disabled; + public override bool HandleMouseInput => !ruleset.Disabled; + + private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); + + protected override void Update() + { + base.Update(); + Size = new Vector2(modeButtons.DrawSize.X, 1); + } + + private void rulesetChanged(RulesetInfo ruleset) + { + foreach (ToolbarModeButton m in modeButtons.Children.Cast()) + { + bool isActive = m.Ruleset.ID == ruleset.ID; + m.Active = isActive; + if (isActive) + activeButton = m; + } + + activeMode.Invalidate(); + } + + private Cached activeMode = new Cached(); + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (!activeMode.IsValid) + { + modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, Easing.OutQuint); + activeMode.Validate(); + } + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index 428467fece..94a7e69e3f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarMusicButton : ToolbarOverlayToggleButton - { - public ToolbarMusicButton() - { - Icon = FontAwesome.fa_music; - } - - [BackgroundDependencyLoader(true)] - private void load(MusicController music) - { - StateContainer = music; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarMusicButton : ToolbarOverlayToggleButton + { + public ToolbarMusicButton() + { + Icon = FontAwesome.fa_music; + } + + [BackgroundDependencyLoader(true)] + private void load(MusicController music) + { + StateContainer = music; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index 9cff2e4a77..ef7e5f07bb 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -1,112 +1,112 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarNotificationButton : ToolbarOverlayToggleButton - { - protected override Anchor TooltipAnchor => Anchor.TopRight; - - public BindableInt NotificationCount = new BindableInt(); - - private readonly CountCircle countDisplay; - - public ToolbarNotificationButton() - { - Icon = FontAwesome.fa_bars; - TooltipMain = "Notifications"; - TooltipSub = "Waiting for 'ya"; - - Add(countDisplay = new CountCircle - { - Alpha = 0, - Height = 16, - RelativePositionAxes = Axes.Both, - Origin = Anchor.Centre, - Position = new Vector2(0.7f, 0.25f), - }); - } - - [BackgroundDependencyLoader(true)] - private void load(NotificationOverlay notificationOverlay) - { - StateContainer = notificationOverlay; - - if (notificationOverlay != null) - NotificationCount.BindTo(notificationOverlay.UnreadCount); - - NotificationCount.ValueChanged += count => - { - if (count == 0) - countDisplay.FadeOut(200, Easing.OutQuint); - else - { - countDisplay.Count = count; - countDisplay.FadeIn(200, Easing.OutQuint); - } - }; - } - - private class CountCircle : CompositeDrawable - { - private readonly OsuSpriteText countText; - private readonly Circle circle; - - private int count; - - public int Count - { - get { return count; } - set - { - if (count == value) - return; - - if (value > count) - { - circle.FlashColour(Color4.White, 600, Easing.OutQuint); - this.ScaleTo(1.1f).Then().ScaleTo(1, 600, Easing.OutElastic); - } - - count = value; - countText.Text = value.ToString("#,0"); - } - } - - public CountCircle() - { - AutoSizeAxes = Axes.X; - - InternalChildren = new Drawable[] - { - circle = new Circle - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Red - }, - countText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = -1, - TextSize = 14, - Padding = new MarginPadding(5), - Colour = Color4.White, - UseFullGlyphHeight = true, - Font = "Exo2.0-Bold", - } - }; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarNotificationButton : ToolbarOverlayToggleButton + { + protected override Anchor TooltipAnchor => Anchor.TopRight; + + public BindableInt NotificationCount = new BindableInt(); + + private readonly CountCircle countDisplay; + + public ToolbarNotificationButton() + { + Icon = FontAwesome.fa_bars; + TooltipMain = "Notifications"; + TooltipSub = "Waiting for 'ya"; + + Add(countDisplay = new CountCircle + { + Alpha = 0, + Height = 16, + RelativePositionAxes = Axes.Both, + Origin = Anchor.Centre, + Position = new Vector2(0.7f, 0.25f), + }); + } + + [BackgroundDependencyLoader(true)] + private void load(NotificationOverlay notificationOverlay) + { + StateContainer = notificationOverlay; + + if (notificationOverlay != null) + NotificationCount.BindTo(notificationOverlay.UnreadCount); + + NotificationCount.ValueChanged += count => + { + if (count == 0) + countDisplay.FadeOut(200, Easing.OutQuint); + else + { + countDisplay.Count = count; + countDisplay.FadeIn(200, Easing.OutQuint); + } + }; + } + + private class CountCircle : CompositeDrawable + { + private readonly OsuSpriteText countText; + private readonly Circle circle; + + private int count; + + public int Count + { + get { return count; } + set + { + if (count == value) + return; + + if (value > count) + { + circle.FlashColour(Color4.White, 600, Easing.OutQuint); + this.ScaleTo(1.1f).Then().ScaleTo(1, 600, Easing.OutElastic); + } + + count = value; + countText.Text = value.ToString("#,0"); + } + } + + public CountCircle() + { + AutoSizeAxes = Axes.X; + + InternalChildren = new Drawable[] + { + circle = new Circle + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Red + }, + countText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = -1, + TextSize = 14, + Padding = new MarginPadding(5), + Colour = Color4.White, + UseFullGlyphHeight = true, + Font = "Exo2.0-Bold", + } + }; + } + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index 0f7f34c36a..0c8483dec3 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarOverlayToggleButton : ToolbarButton - { - private readonly Box stateBackground; - - private OverlayContainer stateContainer; - - public OverlayContainer StateContainer - { - get { return stateContainer; } - set - { - stateContainer = value; - if (stateContainer != null) - { - Action = stateContainer.ToggleVisibility; - stateContainer.StateChanged += stateChanged; - } - } - } - - public ToolbarOverlayToggleButton() - { - Add(stateBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(150).Opacity(180), - Blending = BlendingMode.Additive, - Depth = 2, - Alpha = 0, - }); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - if (stateContainer != null) - stateContainer.StateChanged -= stateChanged; - } - - private void stateChanged(Visibility state) - { - switch (state) - { - case Visibility.Hidden: - stateBackground.FadeOut(200); - break; - case Visibility.Visible: - stateBackground.FadeIn(200); - break; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarOverlayToggleButton : ToolbarButton + { + private readonly Box stateBackground; + + private OverlayContainer stateContainer; + + public OverlayContainer StateContainer + { + get { return stateContainer; } + set + { + stateContainer = value; + if (stateContainer != null) + { + Action = stateContainer.ToggleVisibility; + stateContainer.StateChanged += stateChanged; + } + } + } + + public ToolbarOverlayToggleButton() + { + Add(stateBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(150).Opacity(180), + Blending = BlendingMode.Additive, + Depth = 2, + Alpha = 0, + }); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + if (stateContainer != null) + stateContainer.StateChanged -= stateChanged; + } + + private void stateChanged(Visibility state) + { + switch (state) + { + case Visibility.Hidden: + stateBackground.FadeOut(200); + break; + case Visibility.Visible: + stateBackground.FadeIn(200); + break; + } + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs index 1b0c821b54..5c3eaf7840 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarSettingsButton : ToolbarOverlayToggleButton - { - public ToolbarSettingsButton() - { - Icon = FontAwesome.fa_gear; - TooltipMain = "Settings"; - TooltipSub = "Change your settings"; - } - - [BackgroundDependencyLoader(true)] - private void load(MainSettings settings) - { - StateContainer = settings; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarSettingsButton : ToolbarOverlayToggleButton + { + public ToolbarSettingsButton() + { + Icon = FontAwesome.fa_gear; + TooltipMain = "Settings"; + TooltipSub = "Change your settings"; + } + + [BackgroundDependencyLoader(true)] + private void load(MainSettings settings) + { + StateContainer = settings; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index 519210d6e2..f45950cb4c 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarSocialButton : ToolbarOverlayToggleButton - { - public ToolbarSocialButton() - { - Icon = FontAwesome.fa_users; - } - - [BackgroundDependencyLoader(true)] - private void load(SocialOverlay chat) - { - StateContainer = chat; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarSocialButton : ToolbarOverlayToggleButton + { + public ToolbarSocialButton() + { + Icon = FontAwesome.fa_users; + } + + [BackgroundDependencyLoader(true)] + private void load(SocialOverlay chat) + { + StateContainer = chat; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs index ba015119a0..c30d58b0d6 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; -using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarUserArea : Container - { - public LoginOverlay LoginOverlay; - private ToolbarUserButton button; - - public override RectangleF BoundingBox => button.BoundingBox; - - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; - - Children = new Drawable[] { - button = new ToolbarUserButton - { - Action = () => LoginOverlay.ToggleVisibility(), - }, - LoginOverlay = new LoginOverlay - { - BypassAutoSizeAxes = Axes.Both, - Position = new Vector2(0, 1), - RelativePositionAxes = Axes.Y, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarUserArea : Container + { + public LoginOverlay LoginOverlay; + private ToolbarUserButton button; + + public override RectangleF BoundingBox => button.BoundingBox; + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; + + Children = new Drawable[] { + button = new ToolbarUserButton + { + Action = () => LoginOverlay.ToggleVisibility(), + }, + LoginOverlay = new LoginOverlay + { + BypassAutoSizeAxes = Axes.Both, + Position = new Vector2(0, 1), + RelativePositionAxes = Axes.Y, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + } + }; + } + } +} diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 16586adc0c..b01a4c48b6 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Online.API; -using osu.Game.Users; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Toolbar -{ - public class ToolbarUserButton : ToolbarButton, IOnlineComponent - { - private readonly UpdateableAvatar avatar; - - public ToolbarUserButton() - { - AutoSizeAxes = Axes.X; - - DrawableText.Font = @"Exo2.0-MediumItalic"; - - Add(new OpaqueBackground { Depth = 1 }); - - Flow.Add(avatar = new UpdateableAvatar - { - Masking = true, - Size = new Vector2(32), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - CornerRadius = 4, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 4, - Colour = Color4.Black.Opacity(0.1f), - } - }); - } - - [BackgroundDependencyLoader] - private void load(APIAccess api) - { - api.Register(this); - } - - public void APIStateChanged(APIAccess api, APIState state) - { - switch (state) - { - default: - Text = @"Guest"; - avatar.User = new User(); - break; - case APIState.Online: - Text = api.LocalUser.Value.Username; - avatar.User = api.LocalUser; - break; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Users; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarUserButton : ToolbarButton, IOnlineComponent + { + private readonly UpdateableAvatar avatar; + + public ToolbarUserButton() + { + AutoSizeAxes = Axes.X; + + DrawableText.Font = @"Exo2.0-MediumItalic"; + + Add(new OpaqueBackground { Depth = 1 }); + + Flow.Add(avatar = new UpdateableAvatar + { + Masking = true, + Size = new Vector2(32), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + CornerRadius = 4, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 4, + Colour = Color4.Black.Opacity(0.1f), + } + }); + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + api.Register(this); + } + + public void APIStateChanged(APIAccess api, APIState state) + { + switch (state) + { + default: + Text = @"Guest"; + avatar.User = new User(); + break; + case APIState.Online: + Text = api.LocalUser.Value.Username; + avatar.User = api.LocalUser; + break; + } + } + } +} diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index aed0a6d7c6..c6e8c60f92 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -1,227 +1,227 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -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.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Profile; -using osu.Game.Overlays.Profile.Sections; -using osu.Game.Users; - -namespace osu.Game.Overlays -{ - public class UserProfileOverlay : WaveOverlayContainer - { - private ProfileSection lastSection; - private ProfileSection[] sections; - private GetUserRequest userReq; - private APIAccess api; - protected ProfileHeader Header; - private SectionsContainer sectionsContainer; - private ProfileTabControl tabs; - - public const float CONTENT_X_MARGIN = 50; - - public UserProfileOverlay() - { - FirstWaveColour = OsuColour.Gray(0.4f); - SecondWaveColour = OsuColour.Gray(0.3f); - ThirdWaveColour = OsuColour.Gray(0.2f); - FourthWaveColour = OsuColour.Gray(0.1f); - - RelativeSizeAxes = Axes.Both; - RelativePositionAxes = Axes.Both; - Width = 0.85f; - Anchor = Anchor.TopCentre; - Origin = Anchor.TopCentre; - - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0), - Type = EdgeEffectType.Shadow, - Radius = 10 - }; - } - - [BackgroundDependencyLoader] - private void load(APIAccess api) - { - this.api = api; - } - - protected override void PopIn() - { - base.PopIn(); - FadeEdgeEffectTo(0.5f, APPEAR_DURATION, Easing.In); - } - - protected override void PopOut() - { - base.PopOut(); - FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); - } - - public void ShowUser(long userId) - { - if (userId == Header.User.Id) - return; - - ShowUser(new User { Id = userId }); - } - - public void ShowUser(User user, bool fetchOnline = true) - { - userReq?.Cancel(); - Clear(); - lastSection = null; - - sections = new ProfileSection[] - { - //new AboutSection(), - new RecentSection(), - new RanksSection(), - //new MedalsSection(), - new HistoricalSection(), - new BeatmapsSection(), - new KudosuSection() - }; - tabs = new ProfileTabControl - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Height = 30 - }; - - Add(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f) - }); - - Header = new ProfileHeader(user); - - Add(sectionsContainer = new SectionsContainer - { - RelativeSizeAxes = Axes.Both, - ExpandableHeader = Header, - FixedHeader = tabs, - HeaderBackground = new Box - { - Colour = OsuColour.Gray(34), - RelativeSizeAxes = Axes.Both - } - }); - sectionsContainer.SelectedSection.ValueChanged += s => - { - if (lastSection != s) - { - lastSection = s; - tabs.Current.Value = lastSection; - } - }; - - tabs.Current.ValueChanged += s => - { - if (lastSection == null) - { - lastSection = sectionsContainer.Children.FirstOrDefault(); - if (lastSection != null) - tabs.Current.Value = lastSection; - return; - } - if (lastSection != s) - { - lastSection = s; - sectionsContainer.ScrollTo(lastSection); - } - }; - - if (fetchOnline) - { - userReq = new GetUserRequest(user.Id); - userReq.Success += userLoadComplete; - api.Queue(userReq); - } - else - { - userReq = null; - userLoadComplete(user); - } - - Show(); - sectionsContainer.ScrollToTop(); - } - - private void userLoadComplete(User user) - { - Header.User = user; - - if (user.ProfileOrder != null) - { - foreach (string id in user.ProfileOrder) - { - var sec = sections.FirstOrDefault(s => s.Identifier == id); - if (sec != null) - { - sec.User.Value = user; - - sectionsContainer.Add(sec); - tabs.AddItem(sec); - } - } - } - } - - private class ProfileTabControl : PageTabControl - { - private readonly Box bottom; - - public ProfileTabControl() - { - TabContainer.RelativeSizeAxes &= ~Axes.X; - TabContainer.AutoSizeAxes |= Axes.X; - TabContainer.Anchor |= Anchor.x1; - TabContainer.Origin |= Anchor.x1; - Add(bottom = new Box - { - RelativeSizeAxes = Axes.X, - Height = 1, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - EdgeSmoothness = new Vector2(1) - }); - } - - protected override TabItem CreateTabItem(ProfileSection value) => new ProfileTabItem(value); - - protected override Dropdown CreateDropdown() => null; - - private class ProfileTabItem : PageTabItem - { - public ProfileTabItem(ProfileSection value) : base(value) - { - Text.Text = value.Title; - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - bottom.Colour = colours.Yellow; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +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.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Profile; +using osu.Game.Overlays.Profile.Sections; +using osu.Game.Users; + +namespace osu.Game.Overlays +{ + public class UserProfileOverlay : WaveOverlayContainer + { + private ProfileSection lastSection; + private ProfileSection[] sections; + private GetUserRequest userReq; + private APIAccess api; + protected ProfileHeader Header; + private SectionsContainer sectionsContainer; + private ProfileTabControl tabs; + + public const float CONTENT_X_MARGIN = 50; + + public UserProfileOverlay() + { + FirstWaveColour = OsuColour.Gray(0.4f); + SecondWaveColour = OsuColour.Gray(0.3f); + ThirdWaveColour = OsuColour.Gray(0.2f); + FourthWaveColour = OsuColour.Gray(0.1f); + + RelativeSizeAxes = Axes.Both; + RelativePositionAxes = Axes.Both; + Width = 0.85f; + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0), + Type = EdgeEffectType.Shadow, + Radius = 10 + }; + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + this.api = api; + } + + protected override void PopIn() + { + base.PopIn(); + FadeEdgeEffectTo(0.5f, APPEAR_DURATION, Easing.In); + } + + protected override void PopOut() + { + base.PopOut(); + FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); + } + + public void ShowUser(long userId) + { + if (userId == Header.User.Id) + return; + + ShowUser(new User { Id = userId }); + } + + public void ShowUser(User user, bool fetchOnline = true) + { + userReq?.Cancel(); + Clear(); + lastSection = null; + + sections = new ProfileSection[] + { + //new AboutSection(), + new RecentSection(), + new RanksSection(), + //new MedalsSection(), + new HistoricalSection(), + new BeatmapsSection(), + new KudosuSection() + }; + tabs = new ProfileTabControl + { + RelativeSizeAxes = Axes.X, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Height = 30 + }; + + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }); + + Header = new ProfileHeader(user); + + Add(sectionsContainer = new SectionsContainer + { + RelativeSizeAxes = Axes.Both, + ExpandableHeader = Header, + FixedHeader = tabs, + HeaderBackground = new Box + { + Colour = OsuColour.Gray(34), + RelativeSizeAxes = Axes.Both + } + }); + sectionsContainer.SelectedSection.ValueChanged += s => + { + if (lastSection != s) + { + lastSection = s; + tabs.Current.Value = lastSection; + } + }; + + tabs.Current.ValueChanged += s => + { + if (lastSection == null) + { + lastSection = sectionsContainer.Children.FirstOrDefault(); + if (lastSection != null) + tabs.Current.Value = lastSection; + return; + } + if (lastSection != s) + { + lastSection = s; + sectionsContainer.ScrollTo(lastSection); + } + }; + + if (fetchOnline) + { + userReq = new GetUserRequest(user.Id); + userReq.Success += userLoadComplete; + api.Queue(userReq); + } + else + { + userReq = null; + userLoadComplete(user); + } + + Show(); + sectionsContainer.ScrollToTop(); + } + + private void userLoadComplete(User user) + { + Header.User = user; + + if (user.ProfileOrder != null) + { + foreach (string id in user.ProfileOrder) + { + var sec = sections.FirstOrDefault(s => s.Identifier == id); + if (sec != null) + { + sec.User.Value = user; + + sectionsContainer.Add(sec); + tabs.AddItem(sec); + } + } + } + } + + private class ProfileTabControl : PageTabControl + { + private readonly Box bottom; + + public ProfileTabControl() + { + TabContainer.RelativeSizeAxes &= ~Axes.X; + TabContainer.AutoSizeAxes |= Axes.X; + TabContainer.Anchor |= Anchor.x1; + TabContainer.Origin |= Anchor.x1; + Add(bottom = new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + EdgeSmoothness = new Vector2(1) + }); + } + + protected override TabItem CreateTabItem(ProfileSection value) => new ProfileTabItem(value); + + protected override Dropdown CreateDropdown() => null; + + private class ProfileTabItem : PageTabItem + { + public ProfileTabItem(ProfileSection value) : base(value) + { + Text.Text = value.Title; + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + bottom.Colour = colours.Yellow; + } + } + } +} diff --git a/osu.Game/Overlays/Volume/MuteButton.cs b/osu.Game/Overlays/Volume/MuteButton.cs index adfc9c610f..b62c639ee3 100644 --- a/osu.Game/Overlays/Volume/MuteButton.cs +++ b/osu.Game/Overlays/Volume/MuteButton.cs @@ -1,83 +1,83 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Volume -{ - public class MuteButton : Container, IHasCurrentValue - { - public Bindable Current { get; } = new Bindable(); - - private Color4 hoveredColour, unhoveredColour; - private const float width = 100; - public const float HEIGHT = 35; - - public MuteButton() - { - Masking = true; - BorderThickness = 3; - CornerRadius = HEIGHT / 2; - Size = new Vector2(width, HEIGHT); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoveredColour = colours.YellowDark; - BorderColour = unhoveredColour = colours.Gray1.Opacity(0.9f); - - SpriteIcon icon; - AddRange(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray1, - Alpha = 0.9f, - }, - icon = new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20), - } - }); - - Current.ValueChanged += newValue => - { - icon.Icon = newValue ? FontAwesome.fa_volume_off : FontAwesome.fa_volume_up; - icon.Margin = new MarginPadding { Left = newValue ? width / 2 - 15 : width / 2 - 10 }; //Magic numbers to line up both icons because they're different widths - }; - Current.TriggerChange(); - } - - protected override bool OnHover(InputState state) - { - this.TransformTo("BorderColour", hoveredColour, 500, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(InputState state) - { - this.TransformTo("BorderColour", unhoveredColour, 500, Easing.OutQuint); - } - - protected override bool OnClick(InputState state) - { - Current.Value = !Current.Value; - return true; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Volume +{ + public class MuteButton : Container, IHasCurrentValue + { + public Bindable Current { get; } = new Bindable(); + + private Color4 hoveredColour, unhoveredColour; + private const float width = 100; + public const float HEIGHT = 35; + + public MuteButton() + { + Masking = true; + BorderThickness = 3; + CornerRadius = HEIGHT / 2; + Size = new Vector2(width, HEIGHT); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoveredColour = colours.YellowDark; + BorderColour = unhoveredColour = colours.Gray1.Opacity(0.9f); + + SpriteIcon icon; + AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray1, + Alpha = 0.9f, + }, + icon = new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + } + }); + + Current.ValueChanged += newValue => + { + icon.Icon = newValue ? FontAwesome.fa_volume_off : FontAwesome.fa_volume_up; + icon.Margin = new MarginPadding { Left = newValue ? width / 2 - 15 : width / 2 - 10 }; //Magic numbers to line up both icons because they're different widths + }; + Current.TriggerChange(); + } + + protected override bool OnHover(InputState state) + { + this.TransformTo("BorderColour", hoveredColour, 500, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + this.TransformTo("BorderColour", unhoveredColour, 500, Easing.OutQuint); + } + + protected override bool OnClick(InputState state) + { + Current.Value = !Current.Value; + return true; + } + } +} diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index a5be7dc445..572b3f0c27 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.Input.Bindings; -using osu.Game.Input.Bindings; - -namespace osu.Game.Overlays.Volume -{ - public class VolumeControlReceptor : Container, IKeyBindingHandler, IHandleGlobalInput - { - public Func ActionRequested; - - public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false; - public bool OnReleased(GlobalAction action) => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; + +namespace osu.Game.Overlays.Volume +{ + public class VolumeControlReceptor : Container, IKeyBindingHandler, IHandleGlobalInput + { + public Func ActionRequested; + + public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false; + public bool OnReleased(GlobalAction action) => false; + } +} diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index b1951f4d72..64106967f4 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -1,193 +1,193 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Globalization; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Bindings; -using osu.Framework.MathUtils; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Input.Bindings; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays.Volume -{ - public class VolumeMeter : Container, IKeyBindingHandler - { - private CircularProgress volumeCircle; - public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 }; - private readonly float circleSize; - private readonly Color4 meterColour; - private readonly string name; - - private OsuSpriteText text; - private BufferedContainer maxGlow; - - public VolumeMeter(string name, float circleSize, Color4 meterColour) - { - this.circleSize = circleSize; - this.meterColour = meterColour; - this.name = name; - - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Add(new Container - { - Size = new Vector2(120, 20), - CornerRadius = 10, - Masking = true, - Margin = new MarginPadding { Left = circleSize + 10 }, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray1, - Alpha = 0.9f, - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = "Exo2.0-Bold", - Text = name - } - } - }); - - CircularProgress bgProgress; - - Add(new CircularContainer - { - Masking = true, - Size = new Vector2(circleSize), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray1, - Alpha = 0.9f, - }, - bgProgress = new CircularProgress - { - RelativeSizeAxes = Axes.Both, - InnerRadius = 0.05f, - Rotation = 180, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Colour = colours.Gray2, - Size = new Vector2(0.8f) - }, - new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.8f), - Padding = new MarginPadding(-Blur.KernelSize(5)), - Rotation = 180, - Child = (volumeCircle = new CircularProgress - { - RelativeSizeAxes = Axes.Both, - InnerRadius = 0.05f, - }).WithEffect(new GlowEffect - { - Colour = meterColour, - Strength = 2, - PadExtent = true - }), - }, - maxGlow = (text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = "Venera", - TextSize = 0.16f * circleSize - }).WithEffect(new GlowEffect - { - Colour = Color4.Transparent, - PadExtent = true, - }) - } - }); - - Bindable.ValueChanged += newVolume => { this.TransformTo("DisplayVolume", newVolume, 400, Easing.OutQuint); }; - bgProgress.Current.Value = 0.75f; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Bindable.TriggerChange(); - } - - private double displayVolume; - - protected double DisplayVolume - { - get => displayVolume; - set - { - displayVolume = value; - - if (displayVolume > 0.99f) - { - text.Text = "MAX"; - maxGlow.EffectColour = meterColour.Opacity(2f); - } - else - { - maxGlow.EffectColour = Color4.Transparent; - text.Text = Math.Round(displayVolume * 100).ToString(CultureInfo.CurrentCulture); - } - - volumeCircle.Current.Value = displayVolume * 0.75f; - } - } - - public double Volume - { - get => Bindable; - private set => Bindable.Value = value; - } - - public void Increase() => Volume += 0.05f; - - public void Decrease() => Volume -= 0.05f; - - public bool OnPressed(GlobalAction action) - { - if (!IsHovered) return false; - - switch (action) - { - case GlobalAction.DecreaseVolume: - Decrease(); - return true; - case GlobalAction.IncreaseVolume: - Increase(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Globalization; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; +using osu.Framework.MathUtils; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Volume +{ + public class VolumeMeter : Container, IKeyBindingHandler + { + private CircularProgress volumeCircle; + public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 }; + private readonly float circleSize; + private readonly Color4 meterColour; + private readonly string name; + + private OsuSpriteText text; + private BufferedContainer maxGlow; + + public VolumeMeter(string name, float circleSize, Color4 meterColour) + { + this.circleSize = circleSize; + this.meterColour = meterColour; + this.name = name; + + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Add(new Container + { + Size = new Vector2(120, 20), + CornerRadius = 10, + Masking = true, + Margin = new MarginPadding { Left = circleSize + 10 }, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray1, + Alpha = 0.9f, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = "Exo2.0-Bold", + Text = name + } + } + }); + + CircularProgress bgProgress; + + Add(new CircularContainer + { + Masking = true, + Size = new Vector2(circleSize), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray1, + Alpha = 0.9f, + }, + bgProgress = new CircularProgress + { + RelativeSizeAxes = Axes.Both, + InnerRadius = 0.05f, + Rotation = 180, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colours.Gray2, + Size = new Vector2(0.8f) + }, + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.8f), + Padding = new MarginPadding(-Blur.KernelSize(5)), + Rotation = 180, + Child = (volumeCircle = new CircularProgress + { + RelativeSizeAxes = Axes.Both, + InnerRadius = 0.05f, + }).WithEffect(new GlowEffect + { + Colour = meterColour, + Strength = 2, + PadExtent = true + }), + }, + maxGlow = (text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = "Venera", + TextSize = 0.16f * circleSize + }).WithEffect(new GlowEffect + { + Colour = Color4.Transparent, + PadExtent = true, + }) + } + }); + + Bindable.ValueChanged += newVolume => { this.TransformTo("DisplayVolume", newVolume, 400, Easing.OutQuint); }; + bgProgress.Current.Value = 0.75f; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Bindable.TriggerChange(); + } + + private double displayVolume; + + protected double DisplayVolume + { + get => displayVolume; + set + { + displayVolume = value; + + if (displayVolume > 0.99f) + { + text.Text = "MAX"; + maxGlow.EffectColour = meterColour.Opacity(2f); + } + else + { + maxGlow.EffectColour = Color4.Transparent; + text.Text = Math.Round(displayVolume * 100).ToString(CultureInfo.CurrentCulture); + } + + volumeCircle.Current.Value = displayVolume * 0.75f; + } + } + + public double Volume + { + get => Bindable; + private set => Bindable.Value = value; + } + + public void Increase() => Volume += 0.05f; + + public void Decrease() => Volume -= 0.05f; + + public bool OnPressed(GlobalAction action) + { + if (!IsHovered) return false; + + switch (action) + { + case GlobalAction.DecreaseVolume: + Decrease(); + return true; + case GlobalAction.IncreaseVolume: + Increase(); + return true; + } + + return false; + } + + public bool OnReleased(GlobalAction action) => false; + } +} diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index 17a4b139b0..da63495fec 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -1,147 +1,147 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Threading; -using osu.Game.Graphics; -using osu.Game.Input.Bindings; -using osu.Game.Overlays.Volume; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Overlays -{ - public class VolumeOverlay : OverlayContainer - { - private const float offset = 10; - - private VolumeMeter volumeMeterMaster; - private VolumeMeter volumeMeterEffect; - private VolumeMeter volumeMeterMusic; - private MuteButton muteButton; - - protected override bool BlockPassThroughMouse => false; - - private readonly BindableDouble muteAdjustment = new BindableDouble(); - - [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuColour colours) - { - AutoSizeAxes = Axes.X; - RelativeSizeAxes = Axes.Y; - - AddRange(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Y, - Width = 300, - Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.75f), Color4.Black.Opacity(0)) - }, - new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Spacing = new Vector2(0, offset), - Margin = new MarginPadding { Left = offset }, - Children = new Drawable[] - { - volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) - { - Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } //to counter the mute button and re-center the volume meters - }, - volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), - volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), - muteButton = new MuteButton - { - Margin = new MarginPadding { Top = 100 } - } - } - }, - }); - - volumeMeterMaster.Bindable.BindTo(audio.Volume); - volumeMeterEffect.Bindable.BindTo(audio.VolumeSample); - volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack); - - muteButton.Current.ValueChanged += mute => - { - if (mute) - audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment); - else - audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment); - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - volumeMeterMaster.Bindable.ValueChanged += _ => settingChanged(); - volumeMeterEffect.Bindable.ValueChanged += _ => settingChanged(); - volumeMeterMusic.Bindable.ValueChanged += _ => settingChanged(); - muteButton.Current.ValueChanged += _ => settingChanged(); - } - - private void settingChanged() - { - Show(); - schedulePopOut(); - } - - public bool Adjust(GlobalAction action) - { - switch (action) - { - case GlobalAction.DecreaseVolume: - if (State == Visibility.Hidden) - Show(); - else - volumeMeterMaster.Decrease(); - return true; - case GlobalAction.IncreaseVolume: - if (State == Visibility.Hidden) - Show(); - else - volumeMeterMaster.Increase(); - return true; - case GlobalAction.ToggleMute: - Show(); - muteButton.Current.Value = !muteButton.Current; - return true; - } - - return false; - } - - private ScheduledDelegate popOutDelegate; - - protected override void PopIn() - { - ClearTransforms(); - this.FadeIn(100); - - schedulePopOut(); - } - - protected override void PopOut() - { - this.FadeOut(100); - } - - private void schedulePopOut() - { - popOutDelegate?.Cancel(); - this.Delay(1000).Schedule(Hide, out popOutDelegate); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Threading; +using osu.Game.Graphics; +using osu.Game.Input.Bindings; +using osu.Game.Overlays.Volume; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays +{ + public class VolumeOverlay : OverlayContainer + { + private const float offset = 10; + + private VolumeMeter volumeMeterMaster; + private VolumeMeter volumeMeterEffect; + private VolumeMeter volumeMeterMusic; + private MuteButton muteButton; + + protected override bool BlockPassThroughMouse => false; + + private readonly BindableDouble muteAdjustment = new BindableDouble(); + + [BackgroundDependencyLoader] + private void load(AudioManager audio, OsuColour colours) + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Y, + Width = 300, + Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.75f), Color4.Black.Opacity(0)) + }, + new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Spacing = new Vector2(0, offset), + Margin = new MarginPadding { Left = offset }, + Children = new Drawable[] + { + volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) + { + Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } //to counter the mute button and re-center the volume meters + }, + volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), + volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), + muteButton = new MuteButton + { + Margin = new MarginPadding { Top = 100 } + } + } + }, + }); + + volumeMeterMaster.Bindable.BindTo(audio.Volume); + volumeMeterEffect.Bindable.BindTo(audio.VolumeSample); + volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack); + + muteButton.Current.ValueChanged += mute => + { + if (mute) + audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment); + else + audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment); + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + volumeMeterMaster.Bindable.ValueChanged += _ => settingChanged(); + volumeMeterEffect.Bindable.ValueChanged += _ => settingChanged(); + volumeMeterMusic.Bindable.ValueChanged += _ => settingChanged(); + muteButton.Current.ValueChanged += _ => settingChanged(); + } + + private void settingChanged() + { + Show(); + schedulePopOut(); + } + + public bool Adjust(GlobalAction action) + { + switch (action) + { + case GlobalAction.DecreaseVolume: + if (State == Visibility.Hidden) + Show(); + else + volumeMeterMaster.Decrease(); + return true; + case GlobalAction.IncreaseVolume: + if (State == Visibility.Hidden) + Show(); + else + volumeMeterMaster.Increase(); + return true; + case GlobalAction.ToggleMute: + Show(); + muteButton.Current.Value = !muteButton.Current; + return true; + } + + return false; + } + + private ScheduledDelegate popOutDelegate; + + protected override void PopIn() + { + ClearTransforms(); + this.FadeIn(100); + + schedulePopOut(); + } + + protected override void PopOut() + { + this.FadeOut(100); + } + + private void schedulePopOut() + { + popOutDelegate?.Cancel(); + this.Delay(1000).Schedule(Hide, out popOutDelegate); + } + } +} diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index 074d83a5ad..6a083a77e5 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -1,203 +1,203 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using System; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays -{ - public abstract class WaveOverlayContainer : OsuFocusedOverlayContainer - { - protected const float APPEAR_DURATION = 800; - protected const float DISAPPEAR_DURATION = 500; - - private const Easing easing_show = Easing.OutSine; - private const Easing easing_hide = Easing.InSine; - - private readonly Wave firstWave; - private readonly Wave secondWave; - private readonly Wave thirdWave; - private readonly Wave fourthWave; - - private readonly Container wavesContainer; - - private readonly Container contentContainer; - - protected override bool BlockPassThroughKeyboard => true; - - protected override Container Content => contentContainer; - - protected Color4 FirstWaveColour - { - get - { - return firstWave.Colour; - } - set - { - if (firstWave.Colour == value) return; - firstWave.Colour = value; - } - } - - protected Color4 SecondWaveColour - { - get - { - return secondWave.Colour; - } - set - { - if (secondWave.Colour == value) return; - secondWave.Colour = value; - } - } - - protected Color4 ThirdWaveColour - { - get - { - return thirdWave.Colour; - } - set - { - if (thirdWave.Colour == value) return; - thirdWave.Colour = value; - } - } - - protected Color4 FourthWaveColour - { - get - { - return fourthWave.Colour; - } - set - { - if (fourthWave.Colour == value) return; - fourthWave.Colour = value; - } - } - - protected WaveOverlayContainer() - { - Masking = true; - - AddInternal(wavesContainer = new Container - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Masking = true, - Children = new[] - { - firstWave = new Wave - { - Rotation = 13, - FinalPosition = -930, - }, - secondWave = new Wave - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Rotation = -7, - FinalPosition = -560, - }, - thirdWave = new Wave - { - Rotation = 4, - FinalPosition = -390, - }, - fourthWave = new Wave - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Rotation = -2, - FinalPosition = -220, - }, - }, - }); - - AddInternal(contentContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }); - } - - protected override void PopIn() - { - base.PopIn(); - - foreach (var w in wavesContainer.Children) - w.State = Visibility.Visible; - - this.FadeIn(100, Easing.OutQuint); - contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint); - - this.FadeIn(100, Easing.OutQuint); - } - - protected override void PopOut() - { - base.PopOut(); - - this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); - contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In); - - foreach (var w in wavesContainer.Children) - w.State = Visibility.Hidden; - - this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - // This is done as an optimization, such that invisible parts of the waves - // are masked away, and thus do not consume fill rate. - wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y)); - } - - private class Wave : VisibilityContainer - { - public float FinalPosition; - - protected override bool StartHidden => true; - - public Wave() - { - RelativeSizeAxes = Axes.X; - Width = 1.5f; - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(50), - Radius = 20f, - }; - - Child = new Box { RelativeSizeAxes = Axes.Both }; - } - - protected override void Update() - { - base.Update(); - - // We can not use RelativeSizeAxes for Height, because the height - // of our parent diminishes as the content moves up. - Height = Parent.Parent.DrawSize.Y * 1.5f; - } - - protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show); - protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using System; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays +{ + public abstract class WaveOverlayContainer : OsuFocusedOverlayContainer + { + protected const float APPEAR_DURATION = 800; + protected const float DISAPPEAR_DURATION = 500; + + private const Easing easing_show = Easing.OutSine; + private const Easing easing_hide = Easing.InSine; + + private readonly Wave firstWave; + private readonly Wave secondWave; + private readonly Wave thirdWave; + private readonly Wave fourthWave; + + private readonly Container wavesContainer; + + private readonly Container contentContainer; + + protected override bool BlockPassThroughKeyboard => true; + + protected override Container Content => contentContainer; + + protected Color4 FirstWaveColour + { + get + { + return firstWave.Colour; + } + set + { + if (firstWave.Colour == value) return; + firstWave.Colour = value; + } + } + + protected Color4 SecondWaveColour + { + get + { + return secondWave.Colour; + } + set + { + if (secondWave.Colour == value) return; + secondWave.Colour = value; + } + } + + protected Color4 ThirdWaveColour + { + get + { + return thirdWave.Colour; + } + set + { + if (thirdWave.Colour == value) return; + thirdWave.Colour = value; + } + } + + protected Color4 FourthWaveColour + { + get + { + return fourthWave.Colour; + } + set + { + if (fourthWave.Colour == value) return; + fourthWave.Colour = value; + } + } + + protected WaveOverlayContainer() + { + Masking = true; + + AddInternal(wavesContainer = new Container + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Masking = true, + Children = new[] + { + firstWave = new Wave + { + Rotation = 13, + FinalPosition = -930, + }, + secondWave = new Wave + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Rotation = -7, + FinalPosition = -560, + }, + thirdWave = new Wave + { + Rotation = 4, + FinalPosition = -390, + }, + fourthWave = new Wave + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Rotation = -2, + FinalPosition = -220, + }, + }, + }); + + AddInternal(contentContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }); + } + + protected override void PopIn() + { + base.PopIn(); + + foreach (var w in wavesContainer.Children) + w.State = Visibility.Visible; + + this.FadeIn(100, Easing.OutQuint); + contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint); + + this.FadeIn(100, Easing.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + + this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); + contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In); + + foreach (var w in wavesContainer.Children) + w.State = Visibility.Hidden; + + this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // This is done as an optimization, such that invisible parts of the waves + // are masked away, and thus do not consume fill rate. + wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y)); + } + + private class Wave : VisibilityContainer + { + public float FinalPosition; + + protected override bool StartHidden => true; + + public Wave() + { + RelativeSizeAxes = Axes.X; + Width = 1.5f; + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(50), + Radius = 20f, + }; + + Child = new Box { RelativeSizeAxes = Axes.Both }; + } + + protected override void Update() + { + base.Update(); + + // We can not use RelativeSizeAxes for Height, because the height + // of our parent diminishes as the content moves up. + Height = Parent.Parent.DrawSize.Y * 1.5f; + } + + protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show); + protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide); + } + } +} diff --git a/osu.Game/Properties/AssemblyInfo.cs b/osu.Game/Properties/AssemblyInfo.cs index 9384740308..df9045b802 100644 --- a/osu.Game/Properties/AssemblyInfo.cs +++ b/osu.Game/Properties/AssemblyInfo.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Runtime.CompilerServices; - -// We publish our internal attributes to other sub-projects of the framework. -// Note, that we omit visual tests as they are meant to test the framework -// behavior "in the wild". - -[assembly: InternalsVisibleTo("osu.Game.Tests")] -[assembly: InternalsVisibleTo("osu.Game.Tests.Dynamic")] +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Runtime.CompilerServices; + +// We publish our internal attributes to other sub-projects of the framework. +// Note, that we omit visual tests as they are meant to test the framework +// behavior "in the wild". + +[assembly: InternalsVisibleTo("osu.Game.Tests")] +[assembly: InternalsVisibleTo("osu.Game.Tests.Dynamic")] diff --git a/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs index 56eac730b0..09c74d8bac 100644 --- a/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration.Tracking; - -namespace osu.Game.Rulesets.Configuration -{ - public interface IRulesetConfigManager : ITrackableConfigManager - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration.Tracking; + +namespace osu.Game.Rulesets.Configuration +{ + public interface IRulesetConfigManager : ITrackableConfigManager + { + } +} diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index 9f244f6267..4ecf1eefb2 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Configuration; - -namespace osu.Game.Rulesets.Configuration -{ - public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager - where T : struct - { - protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant) : base(settings, ruleset, variant) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Configuration; + +namespace osu.Game.Rulesets.Configuration +{ + public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager + where T : struct + { + protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant) : base(settings, ruleset, variant) + { + } + } +} diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1e7416e63d..1820053d3d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -1,149 +1,149 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Logging; -using osu.Framework.Timing; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Edit.Screens.Compose.Layers; -using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; - -namespace osu.Game.Rulesets.Edit -{ - public abstract class HitObjectComposer : CompositeDrawable - { - private readonly Ruleset ruleset; - - protected ICompositionTool CurrentTool { get; private set; } - - private RulesetContainer rulesetContainer; - private readonly List layerContainers = new List(); - - private readonly Bindable beatmap = new Bindable(); - - protected HitObjectComposer(Ruleset ruleset) - { - this.ruleset = ruleset; - - RelativeSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame, IFrameBasedClock framedClock) - { - beatmap.BindTo(osuGame.Beatmap); - - try - { - rulesetContainer = CreateRulesetContainer(ruleset, beatmap.Value); - rulesetContainer.Clock = framedClock; - } - catch (Exception e) - { - Logger.Error(e, "Could not load beatmap sucessfully!"); - return; - } - - var layerBelowRuleset = new BorderLayer - { - RelativeSizeAxes = Axes.Both, - Child = CreateLayerContainer() - }; - - var layerAboveRuleset = CreateLayerContainer(); - layerAboveRuleset.Child = new HitObjectMaskLayer(rulesetContainer.Playfield, this); - - layerContainers.Add(layerBelowRuleset); - layerContainers.Add(layerAboveRuleset); - - RadioButtonCollection toolboxCollection; - InternalChild = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new FillFlowContainer - { - Name = "Sidebar", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 10 }, - Children = new Drawable[] - { - new ToolboxGroup { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } } - } - }, - new Container - { - Name = "Content", - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - layerBelowRuleset, - rulesetContainer, - layerAboveRuleset - } - } - }, - }, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 200), - } - }; - - toolboxCollection.Items = - CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) - .Prepend(new RadioButton("Select", () => setCompositionTool(null))) - .ToList(); - - toolboxCollection.Items[0].Select(); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - layerContainers.ForEach(l => - { - l.Anchor = rulesetContainer.Playfield.Anchor; - l.Origin = rulesetContainer.Playfield.Origin; - l.Position = rulesetContainer.Playfield.Position; - l.Size = rulesetContainer.Playfield.Size; - }); - } - - private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; - - protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); - - protected abstract IReadOnlyList CompositionTools { get; } - - /// - /// Creates a for a specific . - /// - /// The to create the overlay for. - public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null; - - /// - /// Creates a which outlines s - /// and handles hitobject pattern adjustments. - /// - public virtual MaskSelection CreateMaskSelection() => new MaskSelection(); - - /// - /// Creates a which provides a layer above or below the . - /// - protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Screens.Compose.Layers; +using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; + +namespace osu.Game.Rulesets.Edit +{ + public abstract class HitObjectComposer : CompositeDrawable + { + private readonly Ruleset ruleset; + + protected ICompositionTool CurrentTool { get; private set; } + + private RulesetContainer rulesetContainer; + private readonly List layerContainers = new List(); + + private readonly Bindable beatmap = new Bindable(); + + protected HitObjectComposer(Ruleset ruleset) + { + this.ruleset = ruleset; + + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame, IFrameBasedClock framedClock) + { + beatmap.BindTo(osuGame.Beatmap); + + try + { + rulesetContainer = CreateRulesetContainer(ruleset, beatmap.Value); + rulesetContainer.Clock = framedClock; + } + catch (Exception e) + { + Logger.Error(e, "Could not load beatmap sucessfully!"); + return; + } + + var layerBelowRuleset = new BorderLayer + { + RelativeSizeAxes = Axes.Both, + Child = CreateLayerContainer() + }; + + var layerAboveRuleset = CreateLayerContainer(); + layerAboveRuleset.Child = new HitObjectMaskLayer(rulesetContainer.Playfield, this); + + layerContainers.Add(layerBelowRuleset); + layerContainers.Add(layerAboveRuleset); + + RadioButtonCollection toolboxCollection; + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new FillFlowContainer + { + Name = "Sidebar", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 10 }, + Children = new Drawable[] + { + new ToolboxGroup { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } } + } + }, + new Container + { + Name = "Content", + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + layerBelowRuleset, + rulesetContainer, + layerAboveRuleset + } + } + }, + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 200), + } + }; + + toolboxCollection.Items = + CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) + .Prepend(new RadioButton("Select", () => setCompositionTool(null))) + .ToList(); + + toolboxCollection.Items[0].Select(); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + layerContainers.ForEach(l => + { + l.Anchor = rulesetContainer.Playfield.Anchor; + l.Origin = rulesetContainer.Playfield.Origin; + l.Position = rulesetContainer.Playfield.Position; + l.Size = rulesetContainer.Playfield.Size; + }); + } + + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; + + protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); + + protected abstract IReadOnlyList CompositionTools { get; } + + /// + /// Creates a for a specific . + /// + /// The to create the overlay for. + public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null; + + /// + /// Creates a which outlines s + /// and handles hitobject pattern adjustments. + /// + public virtual MaskSelection CreateMaskSelection() => new MaskSelection(); + + /// + /// Creates a which provides a layer above or below the . + /// + protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both }; + } +} diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index 9f055ffc5d..ad7c27ad80 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -1,146 +1,146 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Input; -using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; - -namespace osu.Game.Rulesets.Edit -{ - /// - /// A mask placed above a adding editing functionality. - /// - public class HitObjectMask : CompositeDrawable, IStateful - { - /// - /// Invoked when this has been selected. - /// - public event Action Selected; - - /// - /// Invoked when this has been deselected. - /// - public event Action Deselected; - - /// - /// Invoked when this has requested selection. - /// Will fire even if already selected. Does not actually perform selection. - /// - public event Action SelectionRequested; - - /// - /// Invoked when this has requested drag. - /// - public event Action DragRequested; - - /// - /// The which this applies to. - /// - public readonly DrawableHitObject HitObject; - - protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == SelectionState.Selected; - public override bool HandleMouseInput => ShouldBeAlive; - public override bool RemoveWhenNotAlive => false; - - public HitObjectMask(DrawableHitObject hitObject) - { - HitObject = hitObject; - - AlwaysPresent = true; - Alpha = 0; - } - - private SelectionState state; - - public event Action StateChanged; - - public SelectionState State - { - get => state; - set - { - if (state == value) return; - - state = value; - switch (state) - { - case SelectionState.Selected: - Show(); - Selected?.Invoke(this); - break; - case SelectionState.NotSelected: - Hide(); - Deselected?.Invoke(this); - break; - } - } - } - - /// - /// Selects this , causing it to become visible. - /// - public void Select() => State = SelectionState.Selected; - - /// - /// Deselects this , causing it to become invisible. - /// - public void Deselect() => State = SelectionState.NotSelected; - - public bool IsSelected => State == SelectionState.Selected; - - private bool selectionRequested; - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - selectionRequested = false; - - if (State == SelectionState.NotSelected) - { - SelectionRequested?.Invoke(this, state); - selectionRequested = true; - } - - return IsSelected; - } - - protected override bool OnClick(InputState state) - { - if (State == SelectionState.Selected && !selectionRequested) - { - selectionRequested = true; - SelectionRequested?.Invoke(this, state); - return true; - } - - return base.OnClick(state); - } - - protected override bool OnDragStart(InputState state) => true; - - protected override bool OnDrag(InputState state) - { - DragRequested?.Invoke(this, state); - return true; - } - - /// - /// The screen-space point that causes this to be selected. - /// - public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre; - - /// - /// The screen-space quad that outlines this for selections. - /// - public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; - } - - public enum SelectionState - { - NotSelected, - Selected - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Input; +using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; + +namespace osu.Game.Rulesets.Edit +{ + /// + /// A mask placed above a adding editing functionality. + /// + public class HitObjectMask : CompositeDrawable, IStateful + { + /// + /// Invoked when this has been selected. + /// + public event Action Selected; + + /// + /// Invoked when this has been deselected. + /// + public event Action Deselected; + + /// + /// Invoked when this has requested selection. + /// Will fire even if already selected. Does not actually perform selection. + /// + public event Action SelectionRequested; + + /// + /// Invoked when this has requested drag. + /// + public event Action DragRequested; + + /// + /// The which this applies to. + /// + public readonly DrawableHitObject HitObject; + + protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == SelectionState.Selected; + public override bool HandleMouseInput => ShouldBeAlive; + public override bool RemoveWhenNotAlive => false; + + public HitObjectMask(DrawableHitObject hitObject) + { + HitObject = hitObject; + + AlwaysPresent = true; + Alpha = 0; + } + + private SelectionState state; + + public event Action StateChanged; + + public SelectionState State + { + get => state; + set + { + if (state == value) return; + + state = value; + switch (state) + { + case SelectionState.Selected: + Show(); + Selected?.Invoke(this); + break; + case SelectionState.NotSelected: + Hide(); + Deselected?.Invoke(this); + break; + } + } + } + + /// + /// Selects this , causing it to become visible. + /// + public void Select() => State = SelectionState.Selected; + + /// + /// Deselects this , causing it to become invisible. + /// + public void Deselect() => State = SelectionState.NotSelected; + + public bool IsSelected => State == SelectionState.Selected; + + private bool selectionRequested; + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + selectionRequested = false; + + if (State == SelectionState.NotSelected) + { + SelectionRequested?.Invoke(this, state); + selectionRequested = true; + } + + return IsSelected; + } + + protected override bool OnClick(InputState state) + { + if (State == SelectionState.Selected && !selectionRequested) + { + selectionRequested = true; + SelectionRequested?.Invoke(this, state); + return true; + } + + return base.OnClick(state); + } + + protected override bool OnDragStart(InputState state) => true; + + protected override bool OnDrag(InputState state) + { + DragRequested?.Invoke(this, state); + return true; + } + + /// + /// The screen-space point that causes this to be selected. + /// + public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre; + + /// + /// The screen-space quad that outlines this for selections. + /// + public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; + } + + public enum SelectionState + { + NotSelected, + Selected + } +} diff --git a/osu.Game/Rulesets/Edit/ToolboxGroup.cs b/osu.Game/Rulesets/Edit/ToolboxGroup.cs index e153607f47..4de0c518e6 100644 --- a/osu.Game/Rulesets/Edit/ToolboxGroup.cs +++ b/osu.Game/Rulesets/Edit/ToolboxGroup.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Screens.Play.PlayerSettings; - -namespace osu.Game.Rulesets.Edit -{ - public class ToolboxGroup : PlayerSettingsGroup - { - protected override string Title => "toolbox"; - - public ToolboxGroup() - { - RelativeSizeAxes = Axes.X; - Width = 1; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Screens.Play.PlayerSettings; + +namespace osu.Game.Rulesets.Edit +{ + public class ToolboxGroup : PlayerSettingsGroup + { + protected override string Title => "toolbox"; + + public ToolboxGroup() + { + RelativeSizeAxes = Axes.X; + Width = 1; + } + } +} diff --git a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs index a548795df3..2c3720fc8f 100644 --- a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs +++ b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Edit.Tools -{ - public class HitObjectCompositionTool : ICompositionTool - where T : HitObject - { - public string Name => typeof(T).Name; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Edit.Tools +{ + public class HitObjectCompositionTool : ICompositionTool + where T : HitObject + { + public string Name => typeof(T).Name; + } +} diff --git a/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs index db5fecf525..ce8b139b43 100644 --- a/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs +++ b/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Edit.Tools -{ - public interface ICompositionTool - { - string Name { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Edit.Tools +{ + public interface ICompositionTool + { + string Name { get; } + } +} diff --git a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs index fa101ed835..7107b6c763 100644 --- a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs +++ b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Edit.Types -{ - public interface IHasEditablePosition : IHasPosition - { - void OffsetPosition(Vector2 offset); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Edit.Types +{ + public interface IHasEditablePosition : IHasPosition + { + void OffsetPosition(Vector2 offset); + } +} diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index a1a27c0d43..74ee025823 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -1,111 +1,111 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Judgements -{ - /// - /// A drawable object which visualises the hit result of a . - /// - public class DrawableJudgement : Container - { - private const float judgement_size = 80; - - private OsuColour colours; - - protected readonly Judgement Judgement; - - public readonly DrawableHitObject JudgedObject; - - protected SpriteText JudgementText; - - /// - /// Creates a drawable which visualises a . - /// - /// The judgement to visualise. - /// The object which was judged. - public DrawableJudgement(Judgement judgement, DrawableHitObject judgedObject) - { - Judgement = judgement; - JudgedObject = judgedObject; - - Size = new Vector2(judgement_size); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - this.colours = colours; - - Child = new SkinnableDrawable($"Play/{Judgement.Result}", _ => JudgementText = new OsuSpriteText - { - Text = Judgement.Result.GetDescription().ToUpper(), - Font = @"Venera", - Colour = judgementColour(Judgement.Result), - Scale = new Vector2(0.85f, 1), - TextSize = 12 - }, restrictSize: false); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - this.FadeInFromZero(100, Easing.OutQuint); - - switch (Judgement.Result) - { - case HitResult.None: - break; - case HitResult.Miss: - this.ScaleTo(1.6f); - this.ScaleTo(1, 100, Easing.In); - - this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); - this.RotateTo(40, 800, Easing.InQuint); - - this.Delay(600).FadeOut(200); - break; - default: - this.ScaleTo(0.9f); - this.ScaleTo(1, 500, Easing.OutElastic); - - this.Delay(100).FadeOut(400); - break; - } - - Expire(true); - } - - private Color4 judgementColour(HitResult judgement) - { - switch (judgement) - { - case HitResult.Perfect: - case HitResult.Great: - return colours.Blue; - case HitResult.Ok: - case HitResult.Good: - return colours.Green; - case HitResult.Meh: - return colours.Yellow; - case HitResult.Miss: - return colours.Red; - } - - return Color4.White; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Judgements +{ + /// + /// A drawable object which visualises the hit result of a . + /// + public class DrawableJudgement : Container + { + private const float judgement_size = 80; + + private OsuColour colours; + + protected readonly Judgement Judgement; + + public readonly DrawableHitObject JudgedObject; + + protected SpriteText JudgementText; + + /// + /// Creates a drawable which visualises a . + /// + /// The judgement to visualise. + /// The object which was judged. + public DrawableJudgement(Judgement judgement, DrawableHitObject judgedObject) + { + Judgement = judgement; + JudgedObject = judgedObject; + + Size = new Vector2(judgement_size); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + this.colours = colours; + + Child = new SkinnableDrawable($"Play/{Judgement.Result}", _ => JudgementText = new OsuSpriteText + { + Text = Judgement.Result.GetDescription().ToUpper(), + Font = @"Venera", + Colour = judgementColour(Judgement.Result), + Scale = new Vector2(0.85f, 1), + TextSize = 12 + }, restrictSize: false); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.FadeInFromZero(100, Easing.OutQuint); + + switch (Judgement.Result) + { + case HitResult.None: + break; + case HitResult.Miss: + this.ScaleTo(1.6f); + this.ScaleTo(1, 100, Easing.In); + + this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); + this.RotateTo(40, 800, Easing.InQuint); + + this.Delay(600).FadeOut(200); + break; + default: + this.ScaleTo(0.9f); + this.ScaleTo(1, 500, Easing.OutElastic); + + this.Delay(100).FadeOut(400); + break; + } + + Expire(true); + } + + private Color4 judgementColour(HitResult judgement) + { + switch (judgement) + { + case HitResult.Perfect: + case HitResult.Great: + return colours.Blue; + case HitResult.Ok: + case HitResult.Good: + return colours.Green; + case HitResult.Meh: + return colours.Yellow; + case HitResult.Miss: + return colours.Red; + } + + return Color4.White; + } + } +} diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index c2216fea51..587f2c8d15 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -1,70 +1,70 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Judgements -{ - public class Judgement - { - /// - /// Whether this judgement is the result of a hit or a miss. - /// - public HitResult Result; - - /// - /// The maximum that can be achieved. - /// - public virtual HitResult MaxResult => HitResult.Perfect; - - /// - /// The combo prior to this judgement occurring. - /// - public int ComboAtJudgement; - - /// - /// The highest combo achieved prior to this judgement occurring. - /// - public int HighestComboAtJudgement; - - /// - /// Whether a successful hit occurred. - /// - public bool IsHit => Result > HitResult.Miss; - - /// - /// Whether this judgement is the final judgement for the hit object. - /// - public bool Final = true; - - /// - /// The offset from a perfect hit at which this judgement occurred. - /// Populated when added via . - /// - public double TimeOffset { get; set; } - - /// - /// Whether the should affect the combo portion of the score. - /// If false, the will be considered for the bonus portion of the score. - /// - public virtual bool AffectsCombo => true; - - /// - /// The numeric representation for the result achieved. - /// - public int NumericResult => NumericResultFor(Result); - - /// - /// The numeric representation for the maximum achievable result. - /// - public int MaxNumericResult => NumericResultFor(MaxResult); - - /// - /// Convert a to a numeric score representation. - /// - /// The value to convert. - /// The number. - protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Judgements +{ + public class Judgement + { + /// + /// Whether this judgement is the result of a hit or a miss. + /// + public HitResult Result; + + /// + /// The maximum that can be achieved. + /// + public virtual HitResult MaxResult => HitResult.Perfect; + + /// + /// The combo prior to this judgement occurring. + /// + public int ComboAtJudgement; + + /// + /// The highest combo achieved prior to this judgement occurring. + /// + public int HighestComboAtJudgement; + + /// + /// Whether a successful hit occurred. + /// + public bool IsHit => Result > HitResult.Miss; + + /// + /// Whether this judgement is the final judgement for the hit object. + /// + public bool Final = true; + + /// + /// The offset from a perfect hit at which this judgement occurred. + /// Populated when added via . + /// + public double TimeOffset { get; set; } + + /// + /// Whether the should affect the combo portion of the score. + /// If false, the will be considered for the bonus portion of the score. + /// + public virtual bool AffectsCombo => true; + + /// + /// The numeric representation for the result achieved. + /// + public int NumericResult => NumericResultFor(Result); + + /// + /// The numeric representation for the maximum achievable result. + /// + public int MaxNumericResult => NumericResultFor(MaxResult); + + /// + /// Convert a to a numeric score representation. + /// + /// The value to convert. + /// The number. + protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0; + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs index 81268e0ad4..6a4042a906 100644 --- a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mods -{ - /// - /// Represents a mod which can override (and block) a fail. - /// - public interface IApplicableFailOverride : IApplicableMod - { - /// - /// Whether we should allow failing at the current point in time. - /// - bool AllowFail { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + /// + /// Represents a mod which can override (and block) a fail. + /// + public interface IApplicableFailOverride : IApplicableMod + { + /// + /// Whether we should allow failing at the current point in time. + /// + bool AllowFail { get; } + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableMod.cs b/osu.Game/Rulesets/Mods/IApplicableMod.cs index 8e56a90d47..30046c3dc2 100644 --- a/osu.Game/Rulesets/Mods/IApplicableMod.cs +++ b/osu.Game/Rulesets/Mods/IApplicableMod.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mods -{ - /// - /// The base interface for a mod which can be applied in some way. - /// If this is not implemented by a mod, it will not be available for use in-game. - /// - public interface IApplicableMod - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + /// + /// The base interface for a mod which can be applied in some way. + /// If this is not implemented by a mod, it will not be available for use in-game. + /// + public interface IApplicableMod + { + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs index d89d6f20d8..a03a003810 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// Interface for a that applies changes to a . - /// - /// The type of converted . - public interface IApplicableToBeatmapConverter : IApplicableMod - where TObject : HitObject - { - /// - /// Applies this to a . - /// - /// The to apply to. - void ApplyToBeatmapConverter(BeatmapConverter beatmapConverter); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// Interface for a that applies changes to a . + /// + /// The type of converted . + public interface IApplicableToBeatmapConverter : IApplicableMod + where TObject : HitObject + { + /// + /// Applies this to a . + /// + /// The to apply to. + void ApplyToBeatmapConverter(BeatmapConverter beatmapConverter); + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToClock.cs b/osu.Game/Rulesets/Mods/IApplicableToClock.cs index 46c1648951..a9570d80c2 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToClock.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToClock.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Timing; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// An interface for mods that make adjustments to the track. - /// - public interface IApplicableToClock : IApplicableMod - { - void ApplyToClock(IAdjustableClock clock); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Timing; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for mods that make adjustments to the track. + /// + public interface IApplicableToClock : IApplicableMod + { + void ApplyToClock(IAdjustableClock clock); + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs index a87ae701c1..3f32253544 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// An interface for mods that make general adjustments to difficulty. - /// - public interface IApplicableToDifficulty : IApplicableMod - { - void ApplyToDifficulty(BeatmapDifficulty difficulty); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for mods that make general adjustments to difficulty. + /// + public interface IApplicableToDifficulty : IApplicableMod + { + void ApplyToDifficulty(BeatmapDifficulty difficulty); + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs index cd39f90ac4..d871cdd322 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// An interface for s that can be applied to s. - /// - public interface IApplicableToDrawableHitObjects : IApplicableMod - { - /// - /// Applies this to a list of s. - /// - /// The list of s to apply to. - void ApplyToDrawableHitObjects(IEnumerable drawables); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for s that can be applied to s. + /// + public interface IApplicableToDrawableHitObjects : IApplicableMod + { + /// + /// Applies this to a list of s. + /// + /// The list of s to apply to. + void ApplyToDrawableHitObjects(IEnumerable drawables); + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs index c20abe0151..0fd2e398c8 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// An interface for s that can be applied to s. - /// - public interface IApplicableToHitObject : IApplicableMod - where TObject : HitObject - { - /// - /// Applies this to a . - /// - /// The to apply to. - void ApplyToHitObject(TObject hitObject); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for s that can be applied to s. + /// + public interface IApplicableToHitObject : IApplicableMod + where TObject : HitObject + { + /// + /// Applies this to a . + /// + /// The to apply to. + void ApplyToHitObject(TObject hitObject); + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs b/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs index f03866e7bb..a3c5ab3df4 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// An interface for s that can be applied to s. - /// - public interface IApplicableToRulesetContainer : IApplicableMod - where TObject : HitObject - { - /// - /// Applies this to a . - /// - /// The to apply to. - void ApplyToRulesetContainer(RulesetContainer rulesetContainer); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for s that can be applied to s. + /// + public interface IApplicableToRulesetContainer : IApplicableMod + where TObject : HitObject + { + /// + /// Applies this to a . + /// + /// The to apply to. + void ApplyToRulesetContainer(RulesetContainer rulesetContainer); + } +} diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs index f45bbab194..772eac478c 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// An interface for mods that make general adjustments to score processor. - /// - public interface IApplicableToScoreProcessor : IApplicableMod - { - void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for mods that make general adjustments to score processor. + /// + public interface IApplicableToScoreProcessor : IApplicableMod + { + void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); + } +} diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 26cdcde9fe..9fb554b5c5 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -1,59 +1,59 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; -using System; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// The base class for gameplay modifiers. - /// - public abstract class Mod - { - /// - /// The name of this mod. - /// - public abstract string Name { get; } - - /// - /// The shortened name of this mod. - /// - public abstract string ShortenedName { get; } - - /// - /// The icon of this mod. - /// - public virtual FontAwesome Icon => FontAwesome.fa_question; - - /// - /// The type of this mod. - /// - public virtual ModType Type => ModType.Special; - - /// - /// The user readable description of this mod. - /// - public virtual string Description => string.Empty; - - /// - /// The score multiplier of this mod. - /// - public abstract double ScoreMultiplier { get; } - - /// - /// Returns true if this mod is implemented (and playable). - /// - public virtual bool HasImplementation => this is IApplicableMod; - - /// - /// Returns if this mod is ranked. - /// - public virtual bool Ranked => false; - - /// - /// The mods this mod cannot be enabled with. - /// - public virtual Type[] IncompatibleMods => new Type[] { }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using System; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// The base class for gameplay modifiers. + /// + public abstract class Mod + { + /// + /// The name of this mod. + /// + public abstract string Name { get; } + + /// + /// The shortened name of this mod. + /// + public abstract string ShortenedName { get; } + + /// + /// The icon of this mod. + /// + public virtual FontAwesome Icon => FontAwesome.fa_question; + + /// + /// The type of this mod. + /// + public virtual ModType Type => ModType.Special; + + /// + /// The user readable description of this mod. + /// + public virtual string Description => string.Empty; + + /// + /// The score multiplier of this mod. + /// + public abstract double ScoreMultiplier { get; } + + /// + /// Returns true if this mod is implemented (and playable). + /// + public virtual bool HasImplementation => this is IApplicableMod; + + /// + /// Returns if this mod is ranked. + /// + public virtual bool Ranked => false; + + /// + /// The mods this mod cannot be enabled with. + /// + public virtual Type[] IncompatibleMods => new Type[] { }; + } +} diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 9f45cada7e..75f37b993d 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Mods -{ - public class ModAutoplay : ModAutoplay, IApplicableToRulesetContainer - where T : HitObject - { - protected virtual Score CreateReplayScore(Beatmap beatmap) => new Score { Replay = new Replay() }; - - public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; - - public virtual void ApplyToRulesetContainer(RulesetContainer rulesetContainer) => rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay); - } - - public abstract class ModAutoplay : Mod, IApplicableFailOverride - { - public override string Name => "Autoplay"; - public override string ShortenedName => "AT"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto; - public override string Description => "Watch a perfect automated play through the song."; - public override double ScoreMultiplier => 0; - public bool AllowFail => false; - public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mods +{ + public class ModAutoplay : ModAutoplay, IApplicableToRulesetContainer + where T : HitObject + { + protected virtual Score CreateReplayScore(Beatmap beatmap) => new Score { Replay = new Replay() }; + + public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; + + public virtual void ApplyToRulesetContainer(RulesetContainer rulesetContainer) => rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay); + } + + public abstract class ModAutoplay : Mod, IApplicableFailOverride + { + public override string Name => "Autoplay"; + public override string ShortenedName => "AT"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto; + public override string Description => "Watch a perfect automated play through the song."; + public override double ScoreMultiplier => 0; + public bool AllowFail => false; + public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; + } +} diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index 015f7381fb..421e3a54e4 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public class ModCinema : ModAutoplay - { - public override string Name => "Cinema"; - public override string ShortenedName => "CN"; - public override bool HasImplementation => false; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema; - public override string Description => "Watch the video without visual distractions."; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public class ModCinema : ModAutoplay + { + public override string Name => "Cinema"; + public override string ShortenedName => "CN"; + public override bool HasImplementation => false; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema; + public override string Description => "Watch the video without visual distractions."; + } +} diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index da4263875b..f11d0c76ed 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Audio; -using osu.Framework.Timing; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModDaycore : ModHalfTime - { - public override string Name => "Daycore"; - public override string ShortenedName => "DC"; - public override FontAwesome Icon => FontAwesome.fa_question; - public override string Description => "Whoaaaaa..."; - - public override void ApplyToClock(IAdjustableClock clock) - { - var pitchAdjust = clock as IHasPitchAdjust; - if (pitchAdjust != null) - pitchAdjust.PitchAdjust = 0.75; - else - base.ApplyToClock(clock); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Audio; +using osu.Framework.Timing; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModDaycore : ModHalfTime + { + public override string Name => "Daycore"; + public override string ShortenedName => "DC"; + public override FontAwesome Icon => FontAwesome.fa_question; + public override string Description => "Whoaaaaa..."; + + public override void ApplyToClock(IAdjustableClock clock) + { + var pitchAdjust = clock as IHasPitchAdjust; + if (pitchAdjust != null) + pitchAdjust.PitchAdjust = 0.75; + else + base.ApplyToClock(clock); + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index 6225a6feee..0d44495fce 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Timing; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModDoubleTime : Mod, IApplicableToClock - { - public override string Name => "Double Time"; - public override string ShortenedName => "DT"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime; - public override ModType Type => ModType.DifficultyIncrease; - public override string Description => "Zoooooooooom..."; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) }; - - public virtual void ApplyToClock(IAdjustableClock clock) - { - clock.Rate = 1.5; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Timing; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModDoubleTime : Mod, IApplicableToClock + { + public override string Name => "Double Time"; + public override string ShortenedName => "DT"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime; + public override ModType Type => ModType.DifficultyIncrease; + public override string Description => "Zoooooooooom..."; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) }; + + public virtual void ApplyToClock(IAdjustableClock clock) + { + clock.Rate = 1.5; + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index 7037edfa31..93abb82e5b 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Beatmaps; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModEasy : Mod, IApplicableToDifficulty - { - public override string Name => "Easy"; - public override string ShortenedName => "EZ"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy; - public override ModType Type => ModType.DifficultyReduction; - public override double ScoreMultiplier => 0.5; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; - - public void ApplyToDifficulty(BeatmapDifficulty difficulty) - { - const float ratio = 0.5f; - difficulty.CircleSize *= ratio; - difficulty.ApproachRate *= ratio; - difficulty.DrainRate *= ratio; - difficulty.OverallDifficulty *= ratio; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModEasy : Mod, IApplicableToDifficulty + { + public override string Name => "Easy"; + public override string ShortenedName => "EZ"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy; + public override ModType Type => ModType.DifficultyReduction; + public override double ScoreMultiplier => 0.5; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + const float ratio = 0.5f; + difficulty.CircleSize *= ratio; + difficulty.ApproachRate *= ratio; + difficulty.DrainRate *= ratio; + difficulty.OverallDifficulty *= ratio; + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 9858e00083..223263195c 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModFlashlight : Mod - { - public override string Name => "Flashlight"; - public override string ShortenedName => "FL"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight; - public override ModType Type => ModType.DifficultyIncrease; - public override string Description => "Restricted view area."; - public override bool Ranked => true; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModFlashlight : Mod + { + public override string Name => "Flashlight"; + public override string ShortenedName => "FL"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight; + 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 883225a66b..3ec4fb9f9f 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Timing; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModHalfTime : Mod, IApplicableToClock - { - public override string Name => "Half Time"; - public override string ShortenedName => "HT"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime; - public override ModType Type => ModType.DifficultyReduction; - public override string Description => "Less zoom..."; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime) }; - - public virtual void ApplyToClock(IAdjustableClock clock) - { - clock.Rate = 0.75; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Timing; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModHalfTime : Mod, IApplicableToClock + { + public override string Name => "Half Time"; + public override string ShortenedName => "HT"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime; + public override ModType Type => ModType.DifficultyReduction; + public override string Description => "Less zoom..."; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime) }; + + public virtual void ApplyToClock(IAdjustableClock clock) + { + clock.Rate = 0.75; + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index c4c0f38faf..090bc737dd 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Beatmaps; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModHardRock : Mod, IApplicableToDifficulty - { - public override string Name => "Hard Rock"; - public override string ShortenedName => "HR"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; - public override ModType Type => ModType.DifficultyIncrease; - public override string Description => "Everything just got a bit harder..."; - public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; - - public void ApplyToDifficulty(BeatmapDifficulty difficulty) - { - const float ratio = 1.4f; - difficulty.CircleSize *= 1.3f; // CS uses a custom 1.3 ratio. - difficulty.ApproachRate = Math.Min(difficulty.ApproachRate * ratio, 10.0f); - difficulty.DrainRate *= ratio; - difficulty.OverallDifficulty *= ratio; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModHardRock : Mod, IApplicableToDifficulty + { + public override string Name => "Hard Rock"; + public override string ShortenedName => "HR"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; + public override ModType Type => ModType.DifficultyIncrease; + public override string Description => "Everything just got a bit harder..."; + public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + const float ratio = 1.4f; + difficulty.CircleSize *= 1.3f; // CS uses a custom 1.3 ratio. + difficulty.ApproachRate = Math.Min(difficulty.ApproachRate * ratio, 10.0f); + difficulty.DrainRate *= ratio; + difficulty.OverallDifficulty *= ratio; + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 303a46f629..b489a665d9 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModHidden : Mod - { - public override string Name => "Hidden"; - public override string ShortenedName => "HD"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; - public override ModType Type => ModType.DifficultyIncrease; - public override bool Ranked => true; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModHidden : Mod + { + public override string Name => "Hidden"; + public override string ShortenedName => "HD"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; + 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 c2925f440f..162d654965 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Audio; -using osu.Framework.Timing; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModNightcore : ModDoubleTime - { - public override string Name => "Nightcore"; - public override string ShortenedName => "NC"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore; - public override string Description => "Uguuuuuuuu..."; - - public override void ApplyToClock(IAdjustableClock clock) - { - var pitchAdjust = clock as IHasPitchAdjust; - if (pitchAdjust != null) - pitchAdjust.PitchAdjust = 1.5; - else - base.ApplyToClock(clock); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Audio; +using osu.Framework.Timing; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModNightcore : ModDoubleTime + { + public override string Name => "Nightcore"; + public override string ShortenedName => "NC"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore; + public override string Description => "Uguuuuuuuu..."; + + public override void ApplyToClock(IAdjustableClock clock) + { + var pitchAdjust = clock as IHasPitchAdjust; + if (pitchAdjust != null) + pitchAdjust.PitchAdjust = 1.5; + else + base.ApplyToClock(clock); + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 8a849825a2..7510f62432 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModNoFail : Mod, IApplicableFailOverride - { - public override string Name => "No Fail"; - public override string ShortenedName => "NF"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail; - public override ModType Type => ModType.DifficultyReduction; - public override string Description => "You can't fail, no matter what."; - public override double ScoreMultiplier => 0.5; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) }; - - /// - /// We never fail, 'yo. - /// - public bool AllowFail => false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModNoFail : Mod, IApplicableFailOverride + { + public override string Name => "No Fail"; + public override string ShortenedName => "NF"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail; + public override ModType Type => ModType.DifficultyReduction; + public override string Description => "You can't fail, no matter what."; + public override double ScoreMultiplier => 0.5; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) }; + + /// + /// We never fail, 'yo. + /// + public bool AllowFail => false; + } +} diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index 116d13bf0a..802890866f 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModPerfect : ModSuddenDeath - { - public override string Name => "Perfect"; - public override string ShortenedName => "PF"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_perfect; - public override string Description => "SS or quit."; - - protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModPerfect : ModSuddenDeath + { + public override string Name => "Perfect"; + public override string ShortenedName => "PF"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_perfect; + public override string Description => "SS or quit."; + + protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1; + } +} diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs index e8328c3ac7..e4137254e6 100644 --- a/osu.Game/Rulesets/Mods/ModRelax.cs +++ b/osu.Game/Rulesets/Mods/ModRelax.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModRelax : Mod - { - public override string Name => "Relax"; - public override string ShortenedName => "RX"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax; - public override double ScoreMultiplier => 0; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModRelax : Mod + { + public override string Name => "Relax"; + public override string ShortenedName => "RX"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax; + public override double ScoreMultiplier => 0; + 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 ef9ff4c69e..48f7d496a5 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor - { - public override string Name => "Sudden Death"; - public override string ShortenedName => "SD"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath; - public override ModType Type => ModType.DifficultyIncrease; - public override string Description => "Miss and fail."; - public override double ScoreMultiplier => 1; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; - - public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) - { - scoreProcessor.FailConditions += FailCondition; - } - - protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor + { + public override string Name => "Sudden Death"; + public override string ShortenedName => "SD"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath; + public override ModType Type => ModType.DifficultyIncrease; + public override string Description => "Miss and fail."; + public override double ScoreMultiplier => 1; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; + + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + scoreProcessor.FailConditions += FailCondition; + } + + protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0; + } +} diff --git a/osu.Game/Rulesets/Mods/ModType.cs b/osu.Game/Rulesets/Mods/ModType.cs index 8aeb880be8..1941724879 100644 --- a/osu.Game/Rulesets/Mods/ModType.cs +++ b/osu.Game/Rulesets/Mods/ModType.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mods -{ - public enum ModType - { - DifficultyReduction, - DifficultyIncrease, - Special, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + public enum ModType + { + DifficultyReduction, + DifficultyIncrease, + Special, + } +} diff --git a/osu.Game/Rulesets/Mods/MultiMod.cs b/osu.Game/Rulesets/Mods/MultiMod.cs index 5548313f8e..3c90a4eedb 100644 --- a/osu.Game/Rulesets/Mods/MultiMod.cs +++ b/osu.Game/Rulesets/Mods/MultiMod.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mods -{ - public class MultiMod : Mod - { - public override string Name => string.Empty; - public override string ShortenedName => string.Empty; - public override string Description => string.Empty; - public override double ScoreMultiplier => 0; - - public Mod[] Mods; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + public class MultiMod : Mod + { + public override string Name => string.Empty; + public override string ShortenedName => string.Empty; + public override string Description => string.Empty; + public override double ScoreMultiplier => 0; + + public Mod[] Mods; + } +} diff --git a/osu.Game/Rulesets/Objects/BezierApproximator.cs b/osu.Game/Rulesets/Objects/BezierApproximator.cs index 6ed83a5f57..6094934510 100644 --- a/osu.Game/Rulesets/Objects/BezierApproximator.cs +++ b/osu.Game/Rulesets/Objects/BezierApproximator.cs @@ -1,150 +1,150 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using OpenTK; - -namespace osu.Game.Rulesets.Objects -{ - public class BezierApproximator - { - private readonly int count; - private readonly List controlPoints; - private readonly Vector2[] subdivisionBuffer1; - private readonly Vector2[] subdivisionBuffer2; - - private const float tolerance = 0.25f; - private const float tolerance_sq = tolerance * tolerance; - - public BezierApproximator(List controlPoints) - { - this.controlPoints = controlPoints; - count = controlPoints.Count; - - subdivisionBuffer1 = new Vector2[count]; - subdivisionBuffer2 = new Vector2[count * 2 - 1]; - } - - /// - /// Make sure the 2nd order derivative (approximated using finite elements) is within tolerable bounds. - /// NOTE: The 2nd order derivative of a 2d curve represents its curvature, so intuitively this function - /// checks (as the name suggests) whether our approximation is _locally_ "flat". More curvy parts - /// need to have a denser approximation to be more "flat". - /// - /// The control points to check for flatness. - /// Whether the control points are flat enough. - private static bool isFlatEnough(Vector2[] controlPoints) - { - for (int i = 1; i < controlPoints.Length - 1; i++) - if ((controlPoints[i - 1] - 2 * controlPoints[i] + controlPoints[i + 1]).LengthSquared > tolerance_sq * 4) - return false; - - return true; - } - - /// - /// Subdivides n control points representing a bezier curve into 2 sets of n control points, each - /// describing a bezier curve equivalent to a half of the original curve. Effectively this splits - /// the original curve into 2 curves which result in the original curve when pieced back together. - /// - /// The control points to split. - /// Output: The control points corresponding to the left half of the curve. - /// Output: The control points corresponding to the right half of the curve. - private void subdivide(Vector2[] controlPoints, Vector2[] l, Vector2[] r) - { - Vector2[] midpoints = subdivisionBuffer1; - - for (int i = 0; i < count; ++i) - midpoints[i] = controlPoints[i]; - - for (int i = 0; i < count; i++) - { - l[i] = midpoints[0]; - r[count - i - 1] = midpoints[count - i - 1]; - - for (int j = 0; j < count - i - 1; j++) - midpoints[j] = (midpoints[j] + midpoints[j + 1]) / 2; - } - } - - /// - /// This uses De Casteljau's algorithm to obtain an optimal - /// piecewise-linear approximation of the bezier curve with the same amount of points as there are control points. - /// - /// The control points describing the bezier curve to be approximated. - /// The points representing the resulting piecewise-linear approximation. - private void approximate(Vector2[] controlPoints, List output) - { - Vector2[] l = subdivisionBuffer2; - Vector2[] r = subdivisionBuffer1; - - subdivide(controlPoints, l, r); - - for (int i = 0; i < count - 1; ++i) - l[count + i] = r[i + 1]; - - output.Add(controlPoints[0]); - for (int i = 1; i < count - 1; ++i) - { - int index = 2 * i; - Vector2 p = 0.25f * (l[index - 1] + 2 * l[index] + l[index + 1]); - output.Add(p); - } - } - - /// - /// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing - /// the control points until their approximation error vanishes below a given threshold. - /// - /// A list of vectors representing the piecewise-linear approximation. - public List CreateBezier() - { - List output = new List(); - - if (count == 0) - return output; - - Stack toFlatten = new Stack(); - Stack freeBuffers = new Stack(); - - // "toFlatten" contains all the curves which are not yet approximated well enough. - // We use a stack to emulate recursion without the risk of running into a stack overflow. - // (More specifically, we iteratively and adaptively refine our curve with a - // Depth-first search - // over the tree resulting from the subdivisions we make.) - toFlatten.Push(controlPoints.ToArray()); - - Vector2[] leftChild = subdivisionBuffer2; - - while (toFlatten.Count > 0) - { - Vector2[] parent = toFlatten.Pop(); - if (isFlatEnough(parent)) - { - // If the control points we currently operate on are sufficiently "flat", we use - // an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation - // of the bezier curve represented by our control points, consisting of the same amount - // of points as there are control points. - approximate(parent, output); - freeBuffers.Push(parent); - continue; - } - - // If we do not yet have a sufficiently "flat" (in other words, detailed) approximation we keep - // subdividing the curve we are currently operating on. - Vector2[] rightChild = freeBuffers.Count > 0 ? freeBuffers.Pop() : new Vector2[count]; - subdivide(parent, leftChild, rightChild); - - // We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration. - for (int i = 0; i < count; ++i) - parent[i] = leftChild[i]; - - toFlatten.Push(rightChild); - toFlatten.Push(parent); - } - - output.Add(controlPoints[count - 1]); - return output; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK; + +namespace osu.Game.Rulesets.Objects +{ + public class BezierApproximator + { + private readonly int count; + private readonly List controlPoints; + private readonly Vector2[] subdivisionBuffer1; + private readonly Vector2[] subdivisionBuffer2; + + private const float tolerance = 0.25f; + private const float tolerance_sq = tolerance * tolerance; + + public BezierApproximator(List controlPoints) + { + this.controlPoints = controlPoints; + count = controlPoints.Count; + + subdivisionBuffer1 = new Vector2[count]; + subdivisionBuffer2 = new Vector2[count * 2 - 1]; + } + + /// + /// Make sure the 2nd order derivative (approximated using finite elements) is within tolerable bounds. + /// NOTE: The 2nd order derivative of a 2d curve represents its curvature, so intuitively this function + /// checks (as the name suggests) whether our approximation is _locally_ "flat". More curvy parts + /// need to have a denser approximation to be more "flat". + /// + /// The control points to check for flatness. + /// Whether the control points are flat enough. + private static bool isFlatEnough(Vector2[] controlPoints) + { + for (int i = 1; i < controlPoints.Length - 1; i++) + if ((controlPoints[i - 1] - 2 * controlPoints[i] + controlPoints[i + 1]).LengthSquared > tolerance_sq * 4) + return false; + + return true; + } + + /// + /// Subdivides n control points representing a bezier curve into 2 sets of n control points, each + /// describing a bezier curve equivalent to a half of the original curve. Effectively this splits + /// the original curve into 2 curves which result in the original curve when pieced back together. + /// + /// The control points to split. + /// Output: The control points corresponding to the left half of the curve. + /// Output: The control points corresponding to the right half of the curve. + private void subdivide(Vector2[] controlPoints, Vector2[] l, Vector2[] r) + { + Vector2[] midpoints = subdivisionBuffer1; + + for (int i = 0; i < count; ++i) + midpoints[i] = controlPoints[i]; + + for (int i = 0; i < count; i++) + { + l[i] = midpoints[0]; + r[count - i - 1] = midpoints[count - i - 1]; + + for (int j = 0; j < count - i - 1; j++) + midpoints[j] = (midpoints[j] + midpoints[j + 1]) / 2; + } + } + + /// + /// This uses De Casteljau's algorithm to obtain an optimal + /// piecewise-linear approximation of the bezier curve with the same amount of points as there are control points. + /// + /// The control points describing the bezier curve to be approximated. + /// The points representing the resulting piecewise-linear approximation. + private void approximate(Vector2[] controlPoints, List output) + { + Vector2[] l = subdivisionBuffer2; + Vector2[] r = subdivisionBuffer1; + + subdivide(controlPoints, l, r); + + for (int i = 0; i < count - 1; ++i) + l[count + i] = r[i + 1]; + + output.Add(controlPoints[0]); + for (int i = 1; i < count - 1; ++i) + { + int index = 2 * i; + Vector2 p = 0.25f * (l[index - 1] + 2 * l[index] + l[index + 1]); + output.Add(p); + } + } + + /// + /// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing + /// the control points until their approximation error vanishes below a given threshold. + /// + /// A list of vectors representing the piecewise-linear approximation. + public List CreateBezier() + { + List output = new List(); + + if (count == 0) + return output; + + Stack toFlatten = new Stack(); + Stack freeBuffers = new Stack(); + + // "toFlatten" contains all the curves which are not yet approximated well enough. + // We use a stack to emulate recursion without the risk of running into a stack overflow. + // (More specifically, we iteratively and adaptively refine our curve with a + // Depth-first search + // over the tree resulting from the subdivisions we make.) + toFlatten.Push(controlPoints.ToArray()); + + Vector2[] leftChild = subdivisionBuffer2; + + while (toFlatten.Count > 0) + { + Vector2[] parent = toFlatten.Pop(); + if (isFlatEnough(parent)) + { + // If the control points we currently operate on are sufficiently "flat", we use + // an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation + // of the bezier curve represented by our control points, consisting of the same amount + // of points as there are control points. + approximate(parent, output); + freeBuffers.Push(parent); + continue; + } + + // If we do not yet have a sufficiently "flat" (in other words, detailed) approximation we keep + // subdividing the curve we are currently operating on. + Vector2[] rightChild = freeBuffers.Count > 0 ? freeBuffers.Pop() : new Vector2[count]; + subdivide(parent, leftChild, rightChild); + + // We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration. + for (int i = 0; i < count; ++i) + parent[i] = leftChild[i]; + + toFlatten.Push(rightChild); + toFlatten.Push(parent); + } + + output.Add(controlPoints[count - 1]); + return output; + } + } +} diff --git a/osu.Game/Rulesets/Objects/CatmullApproximator.cs b/osu.Game/Rulesets/Objects/CatmullApproximator.cs index 364b4e995a..fabb55e8f6 100644 --- a/osu.Game/Rulesets/Objects/CatmullApproximator.cs +++ b/osu.Game/Rulesets/Objects/CatmullApproximator.cs @@ -1,70 +1,70 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using OpenTK; - -namespace osu.Game.Rulesets.Objects -{ - public class CatmullApproximator - { - /// - /// The amount of pieces to calculate for each controlpoint quadruplet. - /// - private const int detail = 50; - - private readonly List controlPoints; - - public CatmullApproximator(List controlPoints) - { - this.controlPoints = controlPoints; - } - - - /// - /// Creates a piecewise-linear approximation of a Catmull-Rom spline. - /// - /// A list of vectors representing the piecewise-linear approximation. - public List CreateCatmull() - { - var result = new List(); - - for (int i = 0; i < controlPoints.Count - 1; i++) - { - var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i]; - var v2 = controlPoints[i]; - var v3 = i < controlPoints.Count - 1 ? controlPoints[i + 1] : v2 + v2 - v1; - var v4 = i < controlPoints.Count - 2 ? controlPoints[i + 2] : v3 + v3 - v2; - - for (int c = 0; c < detail; c++) - { - result.Add(findPoint(ref v1, ref v2, ref v3, ref v4, (float)c / detail)); - result.Add(findPoint(ref v1, ref v2, ref v3, ref v4, (float)(c + 1) / detail)); - } - } - - return result; - } - - /// - /// Finds a point on the spline at the position of a parameter. - /// - /// The first vector. - /// The second vector. - /// The third vector. - /// The fourth vector. - /// The parameter at which to find the point on the spline, in the range [0, 1]. - /// The point on the spline at . - private Vector2 findPoint(ref Vector2 vec1, ref Vector2 vec2, ref Vector2 vec3, ref Vector2 vec4, float t) - { - float t2 = t * t; - float t3 = t * t2; - - Vector2 result; - result.X = 0.5f * (2f * vec2.X + (-vec1.X + vec3.X) * t + (2f * vec1.X - 5f * vec2.X + 4f * vec3.X - vec4.X) * t2 + (-vec1.X + 3f * vec2.X - 3f * vec3.X + vec4.X) * t3); - result.Y = 0.5f * (2f * vec2.Y + (-vec1.Y + vec3.Y) * t + (2f * vec1.Y - 5f * vec2.Y + 4f * vec3.Y - vec4.Y) * t2 + (-vec1.Y + 3f * vec2.Y - 3f * vec3.Y + vec4.Y) * t3); - - return result; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK; + +namespace osu.Game.Rulesets.Objects +{ + public class CatmullApproximator + { + /// + /// The amount of pieces to calculate for each controlpoint quadruplet. + /// + private const int detail = 50; + + private readonly List controlPoints; + + public CatmullApproximator(List controlPoints) + { + this.controlPoints = controlPoints; + } + + + /// + /// Creates a piecewise-linear approximation of a Catmull-Rom spline. + /// + /// A list of vectors representing the piecewise-linear approximation. + public List CreateCatmull() + { + var result = new List(); + + for (int i = 0; i < controlPoints.Count - 1; i++) + { + var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i]; + var v2 = controlPoints[i]; + var v3 = i < controlPoints.Count - 1 ? controlPoints[i + 1] : v2 + v2 - v1; + var v4 = i < controlPoints.Count - 2 ? controlPoints[i + 2] : v3 + v3 - v2; + + for (int c = 0; c < detail; c++) + { + result.Add(findPoint(ref v1, ref v2, ref v3, ref v4, (float)c / detail)); + result.Add(findPoint(ref v1, ref v2, ref v3, ref v4, (float)(c + 1) / detail)); + } + } + + return result; + } + + /// + /// Finds a point on the spline at the position of a parameter. + /// + /// The first vector. + /// The second vector. + /// The third vector. + /// The fourth vector. + /// The parameter at which to find the point on the spline, in the range [0, 1]. + /// The point on the spline at . + private Vector2 findPoint(ref Vector2 vec1, ref Vector2 vec2, ref Vector2 vec3, ref Vector2 vec4, float t) + { + float t2 = t * t; + float t3 = t * t2; + + Vector2 result; + result.X = 0.5f * (2f * vec2.X + (-vec1.X + vec3.X) * t + (2f * vec1.X - 5f * vec2.X + 4f * vec3.X - vec4.X) * t2 + (-vec1.X + 3f * vec2.X - 3f * vec3.X + vec4.X) * t3); + result.Y = 0.5f * (2f * vec2.Y + (-vec1.Y + vec3.Y) * t + (2f * vec1.Y - 5f * vec2.Y + 4f * vec3.Y - vec4.Y) * t2 + (-vec1.Y + 3f * vec2.Y - 3f * vec3.Y + vec4.Y) * t3); + + return result; + } + } +} diff --git a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs index cc319d1a8f..7fb8d8a40e 100644 --- a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs +++ b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs @@ -1,99 +1,99 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.MathUtils; -using OpenTK; - -namespace osu.Game.Rulesets.Objects -{ - public class CircularArcApproximator - { - private readonly Vector2 a; - private readonly Vector2 b; - private readonly Vector2 c; - - private int amountPoints; - - private const float tolerance = 0.1f; - - public CircularArcApproximator(Vector2 a, Vector2 b, Vector2 c) - { - this.a = a; - this.b = b; - this.c = c; - } - - /// - /// Creates a piecewise-linear approximation of a circular arc curve. - /// - /// A list of vectors representing the piecewise-linear approximation. - public List CreateArc() - { - float aSq = (b - c).LengthSquared; - float bSq = (a - c).LengthSquared; - float cSq = (a - b).LengthSquared; - - // If we have a degenerate triangle where a side-length is almost zero, then give up and fall - // back to a more numerically stable method. - if (Precision.AlmostEquals(aSq, 0) || Precision.AlmostEquals(bSq, 0) || Precision.AlmostEquals(cSq, 0)) - return new List(); - - float s = aSq * (bSq + cSq - aSq); - float t = bSq * (aSq + cSq - bSq); - float u = cSq * (aSq + bSq - cSq); - - float sum = s + t + u; - - // If we have a degenerate triangle with an almost-zero size, then give up and fall - // back to a more numerically stable method. - if (Precision.AlmostEquals(sum, 0)) - return new List(); - - Vector2 centre = (s * a + t * b + u * c) / sum; - Vector2 dA = a - centre; - Vector2 dC = c - centre; - - float r = dA.Length; - - double thetaStart = Math.Atan2(dA.Y, dA.X); - double thetaEnd = Math.Atan2(dC.Y, dC.X); - - while (thetaEnd < thetaStart) - thetaEnd += 2 * Math.PI; - - double dir = 1; - double thetaRange = thetaEnd - thetaStart; - - // Decide in which direction to draw the circle, depending on which side of - // AC B lies. - Vector2 orthoAtoC = c - a; - orthoAtoC = new Vector2(orthoAtoC.Y, -orthoAtoC.X); - if (Vector2.Dot(orthoAtoC, b - a) < 0) - { - dir = -dir; - thetaRange = 2 * Math.PI - thetaRange; - } - - // We select the amount of points for the approximation by requiring the discrete curvature - // to be smaller than the provided tolerance. The exact angle required to meet the tolerance - // is: 2 * Math.Acos(1 - TOLERANCE / r) - // The special case is required for extremely short sliders where the radius is smaller than - // the tolerance. This is a pathological rather than a realistic case. - amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r)))); - - List output = new List(amountPoints); - - for (int i = 0; i < amountPoints; ++i) - { - double fract = (double)i / (amountPoints - 1); - double theta = thetaStart + dir * fract * thetaRange; - Vector2 o = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * r; - output.Add(centre + o); - } - - return output; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.MathUtils; +using OpenTK; + +namespace osu.Game.Rulesets.Objects +{ + public class CircularArcApproximator + { + private readonly Vector2 a; + private readonly Vector2 b; + private readonly Vector2 c; + + private int amountPoints; + + private const float tolerance = 0.1f; + + public CircularArcApproximator(Vector2 a, Vector2 b, Vector2 c) + { + this.a = a; + this.b = b; + this.c = c; + } + + /// + /// Creates a piecewise-linear approximation of a circular arc curve. + /// + /// A list of vectors representing the piecewise-linear approximation. + public List CreateArc() + { + float aSq = (b - c).LengthSquared; + float bSq = (a - c).LengthSquared; + float cSq = (a - b).LengthSquared; + + // If we have a degenerate triangle where a side-length is almost zero, then give up and fall + // back to a more numerically stable method. + if (Precision.AlmostEquals(aSq, 0) || Precision.AlmostEquals(bSq, 0) || Precision.AlmostEquals(cSq, 0)) + return new List(); + + float s = aSq * (bSq + cSq - aSq); + float t = bSq * (aSq + cSq - bSq); + float u = cSq * (aSq + bSq - cSq); + + float sum = s + t + u; + + // If we have a degenerate triangle with an almost-zero size, then give up and fall + // back to a more numerically stable method. + if (Precision.AlmostEquals(sum, 0)) + return new List(); + + Vector2 centre = (s * a + t * b + u * c) / sum; + Vector2 dA = a - centre; + Vector2 dC = c - centre; + + float r = dA.Length; + + double thetaStart = Math.Atan2(dA.Y, dA.X); + double thetaEnd = Math.Atan2(dC.Y, dC.X); + + while (thetaEnd < thetaStart) + thetaEnd += 2 * Math.PI; + + double dir = 1; + double thetaRange = thetaEnd - thetaStart; + + // Decide in which direction to draw the circle, depending on which side of + // AC B lies. + Vector2 orthoAtoC = c - a; + orthoAtoC = new Vector2(orthoAtoC.Y, -orthoAtoC.X); + if (Vector2.Dot(orthoAtoC, b - a) < 0) + { + dir = -dir; + thetaRange = 2 * Math.PI - thetaRange; + } + + // We select the amount of points for the approximation by requiring the discrete curvature + // to be smaller than the provided tolerance. The exact angle required to meet the tolerance + // is: 2 * Math.Acos(1 - TOLERANCE / r) + // The special case is required for extremely short sliders where the radius is smaller than + // the tolerance. This is a pathological rather than a realistic case. + amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r)))); + + List output = new List(amountPoints); + + for (int i = 0; i < amountPoints; ++i) + { + double fract = (double)i / (amountPoints - 1); + double theta = thetaStart + dir * fract * thetaRange; + Vector2 o = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * r; + output.Add(centre + o); + } + + return output; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Drawables/ArmedState.cs b/osu.Game/Rulesets/Objects/Drawables/ArmedState.cs index 02b8f2e654..9a72c44d67 100644 --- a/osu.Game/Rulesets/Objects/Drawables/ArmedState.cs +++ b/osu.Game/Rulesets/Objects/Drawables/ArmedState.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Drawables -{ - public enum ArmedState - { - Idle, - Hit, - Miss - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Drawables +{ + public enum ArmedState + { + Idle, + Hit, + Miss + } +} diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index fdfef14a88..88990d435c 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -1,245 +1,245 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Game.Audio; -using osu.Game.Graphics; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Objects.Drawables -{ - public abstract class DrawableHitObject : SkinReloadableDrawable, IHasAccentColour - { - public readonly HitObject HitObject; - - /// - /// The colour used for various elements of this DrawableHitObject. - /// - public virtual Color4 AccentColour { get; set; } = Color4.Gray; - - // Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first - protected virtual string SampleNamespace => null; - - protected SkinnableSound Samples; - - protected virtual IEnumerable GetSamples() => HitObject.Samples; - - private readonly Lazy> nestedHitObjects = new Lazy>(); - public bool HasNestedHitObjects => nestedHitObjects.IsValueCreated; - public IReadOnlyList NestedHitObjects => nestedHitObjects.Value; - - public event Action OnJudgement; - public event Action OnJudgementRemoved; - - public IReadOnlyList Judgements => judgements; - private readonly List judgements = new List(); - - /// - /// Whether a visible judgement should be displayed when this representation is hit. - /// - public virtual bool DisplayJudgement => true; - - /// - /// Whether this and all of its nested s have been hit. - /// - public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && (!HasNestedHitObjects || NestedHitObjects.All(n => n.IsHit)); - - /// - /// Whether this and all of its nested s have been judged. - /// - public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (!HasNestedHitObjects || NestedHitObjects.All(h => h.AllJudged)); - - /// - /// Whether this can be judged. - /// - protected virtual bool ProvidesJudgement => true; - - private bool judgementOccurred; - private bool judgementFinalized => judgements.LastOrDefault()?.Final == true; - - public bool Interactive = true; - public override bool HandleKeyboardInput => Interactive; - public override bool HandleMouseInput => Interactive; - - public override bool RemoveWhenNotAlive => false; - public override bool RemoveCompletedTransforms => false; - protected override bool RequiresChildrenUpdate => true; - - public readonly Bindable State = new Bindable(); - - protected DrawableHitObject(HitObject hitObject) - { - HitObject = hitObject; - } - - [BackgroundDependencyLoader] - private void load() - { - var samples = GetSamples().ToArray(); - - if (samples.Any()) - { - if (HitObject.SampleControlPoint == null) - throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." - + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); - AddInternal(Samples = new SkinnableSound(samples.Select(s => new SampleInfo - { - Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank, - Name = s.Name, - Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume, - Namespace = SampleNamespace - }).ToArray())); - } - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - State.ValueChanged += state => - { - UpdateState(state); - - // apply any custom state overrides - ApplyCustomUpdateState?.Invoke(this, state); - - if (State == ArmedState.Hit) - PlaySamples(); - }; - - State.TriggerChange(); - } - - protected abstract void UpdateState(ArmedState state); - - /// - /// Bind to apply a custom state which can override the default implementation. - /// - public event Action ApplyCustomUpdateState; - - /// - /// Plays all the hitsounds for this . - /// - public void PlaySamples() => Samples?.Play(); - - protected override void Update() - { - base.Update(); - - var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; - - while (judgements.Count > 0) - { - var lastJudgement = judgements[judgements.Count - 1]; - if (lastJudgement.TimeOffset + endTime <= Time.Current) - break; - - judgements.RemoveAt(judgements.Count - 1); - State.Value = ArmedState.Idle; - - OnJudgementRemoved?.Invoke(this, lastJudgement); - } - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - UpdateJudgement(false); - } - - protected virtual void AddNested(DrawableHitObject h) - { - h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j); - h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j); - h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); - - nestedHitObjects.Value.Add(h); - } - - /// - /// Notifies that a new judgement has occurred for this . - /// - /// The . - protected void AddJudgement(Judgement judgement) - { - judgementOccurred = true; - - // Ensure that the judgement is given a valid time offset, because this may not get set by the caller - var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; - judgement.TimeOffset = Time.Current - endTime; - - judgements.Add(judgement); - - switch (judgement.Result) - { - case HitResult.None: - break; - case HitResult.Miss: - State.Value = ArmedState.Miss; - break; - default: - State.Value = ArmedState.Hit; - break; - } - - OnJudgement?.Invoke(this, judgement); - } - - /// - /// Processes this , checking if any judgements have occurred. - /// - /// Whether the user triggered this process. - /// Whether a judgement has occurred from this or any nested s. - protected bool UpdateJudgement(bool userTriggered) - { - judgementOccurred = false; - - if (AllJudged) - return false; - - if (HasNestedHitObjects) - foreach (var d in NestedHitObjects) - judgementOccurred |= d.UpdateJudgement(userTriggered); - - if (!ProvidesJudgement || judgementFinalized || judgementOccurred) - return judgementOccurred; - - var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; - CheckForJudgements(userTriggered, Time.Current - endTime); - - return judgementOccurred; - } - - /// - /// Checks if any judgements have occurred for this . This method must construct - /// all s and notify of them through . - /// - /// Whether the user triggered this check. - /// The offset from the end time at which this check occurred. A > 0 - /// implies that this check occurred after the end time of . - protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) - { - } - } - - public abstract class DrawableHitObject : DrawableHitObject - where TObject : HitObject - { - public new readonly TObject HitObject; - - protected DrawableHitObject(TObject hitObject) - : base(hitObject) - { - HitObject = hitObject; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Audio; +using osu.Game.Graphics; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Objects.Drawables +{ + public abstract class DrawableHitObject : SkinReloadableDrawable, IHasAccentColour + { + public readonly HitObject HitObject; + + /// + /// The colour used for various elements of this DrawableHitObject. + /// + public virtual Color4 AccentColour { get; set; } = Color4.Gray; + + // Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first + protected virtual string SampleNamespace => null; + + protected SkinnableSound Samples; + + protected virtual IEnumerable GetSamples() => HitObject.Samples; + + private readonly Lazy> nestedHitObjects = new Lazy>(); + public bool HasNestedHitObjects => nestedHitObjects.IsValueCreated; + public IReadOnlyList NestedHitObjects => nestedHitObjects.Value; + + public event Action OnJudgement; + public event Action OnJudgementRemoved; + + public IReadOnlyList Judgements => judgements; + private readonly List judgements = new List(); + + /// + /// Whether a visible judgement should be displayed when this representation is hit. + /// + public virtual bool DisplayJudgement => true; + + /// + /// Whether this and all of its nested s have been hit. + /// + public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && (!HasNestedHitObjects || NestedHitObjects.All(n => n.IsHit)); + + /// + /// Whether this and all of its nested s have been judged. + /// + public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (!HasNestedHitObjects || NestedHitObjects.All(h => h.AllJudged)); + + /// + /// Whether this can be judged. + /// + protected virtual bool ProvidesJudgement => true; + + private bool judgementOccurred; + private bool judgementFinalized => judgements.LastOrDefault()?.Final == true; + + public bool Interactive = true; + public override bool HandleKeyboardInput => Interactive; + public override bool HandleMouseInput => Interactive; + + public override bool RemoveWhenNotAlive => false; + public override bool RemoveCompletedTransforms => false; + protected override bool RequiresChildrenUpdate => true; + + public readonly Bindable State = new Bindable(); + + protected DrawableHitObject(HitObject hitObject) + { + HitObject = hitObject; + } + + [BackgroundDependencyLoader] + private void load() + { + var samples = GetSamples().ToArray(); + + if (samples.Any()) + { + if (HitObject.SampleControlPoint == null) + throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." + + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); + AddInternal(Samples = new SkinnableSound(samples.Select(s => new SampleInfo + { + Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank, + Name = s.Name, + Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume, + Namespace = SampleNamespace + }).ToArray())); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + State.ValueChanged += state => + { + UpdateState(state); + + // apply any custom state overrides + ApplyCustomUpdateState?.Invoke(this, state); + + if (State == ArmedState.Hit) + PlaySamples(); + }; + + State.TriggerChange(); + } + + protected abstract void UpdateState(ArmedState state); + + /// + /// Bind to apply a custom state which can override the default implementation. + /// + public event Action ApplyCustomUpdateState; + + /// + /// Plays all the hitsounds for this . + /// + public void PlaySamples() => Samples?.Play(); + + protected override void Update() + { + base.Update(); + + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + + while (judgements.Count > 0) + { + var lastJudgement = judgements[judgements.Count - 1]; + if (lastJudgement.TimeOffset + endTime <= Time.Current) + break; + + judgements.RemoveAt(judgements.Count - 1); + State.Value = ArmedState.Idle; + + OnJudgementRemoved?.Invoke(this, lastJudgement); + } + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + UpdateJudgement(false); + } + + protected virtual void AddNested(DrawableHitObject h) + { + h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j); + h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j); + h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); + + nestedHitObjects.Value.Add(h); + } + + /// + /// Notifies that a new judgement has occurred for this . + /// + /// The . + protected void AddJudgement(Judgement judgement) + { + judgementOccurred = true; + + // Ensure that the judgement is given a valid time offset, because this may not get set by the caller + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + judgement.TimeOffset = Time.Current - endTime; + + judgements.Add(judgement); + + switch (judgement.Result) + { + case HitResult.None: + break; + case HitResult.Miss: + State.Value = ArmedState.Miss; + break; + default: + State.Value = ArmedState.Hit; + break; + } + + OnJudgement?.Invoke(this, judgement); + } + + /// + /// Processes this , checking if any judgements have occurred. + /// + /// Whether the user triggered this process. + /// Whether a judgement has occurred from this or any nested s. + protected bool UpdateJudgement(bool userTriggered) + { + judgementOccurred = false; + + if (AllJudged) + return false; + + if (HasNestedHitObjects) + foreach (var d in NestedHitObjects) + judgementOccurred |= d.UpdateJudgement(userTriggered); + + if (!ProvidesJudgement || judgementFinalized || judgementOccurred) + return judgementOccurred; + + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + CheckForJudgements(userTriggered, Time.Current - endTime); + + return judgementOccurred; + } + + /// + /// Checks if any judgements have occurred for this . This method must construct + /// all s and notify of them through . + /// + /// Whether the user triggered this check. + /// The offset from the end time at which this check occurred. A > 0 + /// implies that this check occurred after the end time of . + protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) + { + } + } + + public abstract class DrawableHitObject : DrawableHitObject + where TObject : HitObject + { + public new readonly TObject HitObject; + + protected DrawableHitObject(TObject hitObject) + : base(hitObject) + { + HitObject = hitObject; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Drawables/IDrawableHitObjectWithProxiedApproach.cs b/osu.Game/Rulesets/Objects/Drawables/IDrawableHitObjectWithProxiedApproach.cs index d10149facd..e10b50e672 100644 --- a/osu.Game/Rulesets/Objects/Drawables/IDrawableHitObjectWithProxiedApproach.cs +++ b/osu.Game/Rulesets/Objects/Drawables/IDrawableHitObjectWithProxiedApproach.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; - -namespace osu.Game.Rulesets.Objects.Drawables -{ - public interface IDrawableHitObjectWithProxiedApproach - { - Drawable ProxiedLayer { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; + +namespace osu.Game.Rulesets.Objects.Drawables +{ + public interface IDrawableHitObjectWithProxiedApproach + { + Drawable ProxiedLayer { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs index 55dd0a16cc..ce365f04bd 100644 --- a/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs @@ -1,31 +1,31 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Framework.Graphics; - -namespace osu.Game.Rulesets.Objects.Drawables -{ - /// - /// An interface that exposes properties required for scrolling hit objects to be properly displayed. - /// - internal interface IScrollingHitObject : IDrawable - { - /// - /// Time offset before the hit object start time at which this becomes visible and the time offset - /// after the hit object's end time after which it expires. - /// - /// - /// This provides only a default life time range, however classes inheriting from should override - /// their life times if more tight control is desired. - /// - /// - BindableDouble LifetimeOffset { get; } - - /// - /// Axes which this will scroll through. - /// This is set by the container which this scrolls through. - /// - Axes ScrollingAxes { set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; + +namespace osu.Game.Rulesets.Objects.Drawables +{ + /// + /// An interface that exposes properties required for scrolling hit objects to be properly displayed. + /// + internal interface IScrollingHitObject : IDrawable + { + /// + /// Time offset before the hit object start time at which this becomes visible and the time offset + /// after the hit object's end time after which it expires. + /// + /// + /// This provides only a default life time range, however classes inheriting from should override + /// their life times if more tight control is desired. + /// + /// + BindableDouble LifetimeOffset { get; } + + /// + /// Axes which this will scroll through. + /// This is set by the container which this scrolls through. + /// + Axes ScrollingAxes { set; } + } +} diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 75cb65eff0..13fa61f536 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -1,102 +1,102 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using Newtonsoft.Json; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Lists; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects -{ - /// - /// A HitObject describes an object in a Beatmap. - /// - /// HitObjects may contain more properties for which you should be checking through the IHas* types. - /// - /// - public class HitObject - { - /// - /// The time at which the HitObject starts. - /// - public virtual double StartTime { get; set; } - - private List samples; - - /// - /// The samples to be played when this hit object is hit. - /// - /// In the case of types, this is the sample of the curve body - /// and can be treated as the default samples for the hit object. - /// - /// - public List Samples - { - get => samples ?? (samples = new List()); - set => samples = value; - } - - [JsonIgnore] - public SampleControlPoint SampleControlPoint; - - /// - /// Whether this is in Kiai time. - /// - [JsonIgnore] - public bool Kiai { get; private set; } - - private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY; - - private HitWindows hitWindows; - - /// - /// The hit windows for this . - /// - public HitWindows HitWindows - { - get => hitWindows ?? (hitWindows = new HitWindows(overallDifficulty)); - protected set => hitWindows = value; - } - - private readonly SortedList nestedHitObjects = new SortedList((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); - - [JsonIgnore] - public IReadOnlyList NestedHitObjects => nestedHitObjects; - - /// - /// Applies default values to this HitObject. - /// - /// The control points. - /// The difficulty settings to use. - public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - ApplyDefaultsToSelf(controlPointInfo, difficulty); - - nestedHitObjects.Clear(); - CreateNestedHitObjects(); - nestedHitObjects.ForEach(h => h.ApplyDefaults(controlPointInfo, difficulty)); - } - - protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - SampleControlPoint samplePoint = controlPointInfo.SamplePointAt(StartTime); - EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime); - - Kiai = effectPoint.KiaiMode; - SampleControlPoint = samplePoint; - - overallDifficulty = difficulty.OverallDifficulty; - hitWindows = null; - } - - protected virtual void CreateNestedHitObjects() - { - } - - protected void AddNested(HitObject hitObject) => nestedHitObjects.Add(hitObject); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using Newtonsoft.Json; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Lists; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects +{ + /// + /// A HitObject describes an object in a Beatmap. + /// + /// HitObjects may contain more properties for which you should be checking through the IHas* types. + /// + /// + public class HitObject + { + /// + /// The time at which the HitObject starts. + /// + public virtual double StartTime { get; set; } + + private List samples; + + /// + /// The samples to be played when this hit object is hit. + /// + /// In the case of types, this is the sample of the curve body + /// and can be treated as the default samples for the hit object. + /// + /// + public List Samples + { + get => samples ?? (samples = new List()); + set => samples = value; + } + + [JsonIgnore] + public SampleControlPoint SampleControlPoint; + + /// + /// Whether this is in Kiai time. + /// + [JsonIgnore] + public bool Kiai { get; private set; } + + private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY; + + private HitWindows hitWindows; + + /// + /// The hit windows for this . + /// + public HitWindows HitWindows + { + get => hitWindows ?? (hitWindows = new HitWindows(overallDifficulty)); + protected set => hitWindows = value; + } + + private readonly SortedList nestedHitObjects = new SortedList((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); + + [JsonIgnore] + public IReadOnlyList NestedHitObjects => nestedHitObjects; + + /// + /// Applies default values to this HitObject. + /// + /// The control points. + /// The difficulty settings to use. + public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + ApplyDefaultsToSelf(controlPointInfo, difficulty); + + nestedHitObjects.Clear(); + CreateNestedHitObjects(); + nestedHitObjects.ForEach(h => h.ApplyDefaults(controlPointInfo, difficulty)); + } + + protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + SampleControlPoint samplePoint = controlPointInfo.SamplePointAt(StartTime); + EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime); + + Kiai = effectPoint.KiaiMode; + SampleControlPoint = samplePoint; + + overallDifficulty = difficulty.OverallDifficulty; + hitWindows = null; + } + + protected virtual void CreateNestedHitObjects() + { + } + + protected void AddNested(HitObject hitObject) => nestedHitObjects.Add(hitObject); + } +} diff --git a/osu.Game/Rulesets/Objects/HitObjectParser.cs b/osu.Game/Rulesets/Objects/HitObjectParser.cs index 5a38daa654..d44da4e16c 100644 --- a/osu.Game/Rulesets/Objects/HitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/HitObjectParser.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects -{ - public abstract class HitObjectParser - { - public abstract HitObject Parse(string text); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects +{ + public abstract class HitObjectParser + { + public abstract HitObject Parse(string text); + } +} diff --git a/osu.Game/Rulesets/Objects/HitWindows.cs b/osu.Game/Rulesets/Objects/HitWindows.cs index ddd9f9b5dc..bf0878a408 100644 --- a/osu.Game/Rulesets/Objects/HitWindows.cs +++ b/osu.Game/Rulesets/Objects/HitWindows.cs @@ -1,173 +1,173 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Objects -{ - public class HitWindows - { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary - { - { HitResult.Perfect, (44.8, 38.8, 27.8) }, - { HitResult.Great, (128, 98, 68 ) }, - { HitResult.Good, (194, 164, 134) }, - { HitResult.Ok, (254, 224, 194) }, - { HitResult.Meh, (302, 272, 242) }, - { HitResult.Miss, (376, 346, 316) }, - }; - - /// - /// Hit window for a result. - /// The user can only achieve receive this result if is true. - /// - public double Perfect { get; protected set; } - - /// - /// Hit window for a result. - /// - public double Great { get; protected set; } - - /// - /// Hit window for a result. - /// - public double Good { get; protected set; } - - /// - /// Hit window for an result. - /// The user can only achieve this result if is true. - /// - public double Ok { get; protected set; } - - /// - /// Hit window for a result. - /// - public double Meh { get; protected set; } - - /// - /// Hit window for a result. - /// - public double Miss { get; protected set; } - - /// - /// Whether it's possible to achieve a result. - /// - public bool AllowsPerfect; - - /// - /// Whether it's possible to achieve a result. - /// - public bool AllowsOk; - - /// - /// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window. - /// - /// The parameter. - public HitWindows(double difficulty) - { - Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]); - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]); - Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); - } - - /// - /// Retrieves the for a time offset. - /// - /// The time offset. - /// The hit result, or if doesn't result in a judgement. - public HitResult ResultFor(double timeOffset) - { - timeOffset = Math.Abs(timeOffset); - - if (AllowsPerfect && timeOffset <= HalfWindowFor(HitResult.Perfect)) - return HitResult.Perfect; - if (timeOffset <= HalfWindowFor(HitResult.Great)) - return HitResult.Great; - if (timeOffset <= HalfWindowFor(HitResult.Good)) - return HitResult.Good; - if (AllowsOk && timeOffset <= HalfWindowFor(HitResult.Ok)) - return HitResult.Ok; - if (timeOffset <= HalfWindowFor(HitResult.Meh)) - return HitResult.Meh; - if (timeOffset <= HalfWindowFor(HitResult.Miss)) - return HitResult.Miss; - - return HitResult.None; - } - - /// - /// Retrieves half the hit window for a . - /// This is useful if the hit window for one half of the hittable range of a is required. - /// - /// The expected . - /// One half of the hit window for . - public double HalfWindowFor(HitResult result) - { - switch (result) - { - case HitResult.Perfect: - return Perfect / 2; - case HitResult.Great: - return Great / 2; - case HitResult.Good: - return Good / 2; - case HitResult.Ok: - return Ok / 2; - case HitResult.Meh: - return Meh / 2; - case HitResult.Miss: - return Miss / 2; - default: - throw new ArgumentException(nameof(result)); - } - } - - /// - /// Given a time offset, whether the can ever be hit in the future with a non- result. - /// This happens if is less than what is required for a result. - /// - /// The time offset. - /// Whether the can be hit at any point in the future from this time offset. - public bool CanBeHit(double timeOffset) => timeOffset <= HalfWindowFor(HitResult.Meh); - - /// - /// Multiplies all hit windows by a value. - /// - /// The hit windows to multiply. - /// The value to multiply each hit window by. - public static HitWindows operator *(HitWindows windows, double value) - { - windows.Perfect *= value; - windows.Great *= value; - windows.Good *= value; - windows.Ok *= value; - windows.Meh *= value; - windows.Miss *= value; - - return windows; - } - - /// - /// Divides all hit windows by a value. - /// - /// The hit windows to divide. - /// The value to divide each hit window by. - public static HitWindows operator /(HitWindows windows, double value) - { - windows.Perfect /= value; - windows.Great /= value; - windows.Good /= value; - windows.Ok /= value; - windows.Meh /= value; - windows.Miss /= value; - - return windows; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Objects +{ + public class HitWindows + { + private static readonly IReadOnlyDictionary base_ranges = new Dictionary + { + { HitResult.Perfect, (44.8, 38.8, 27.8) }, + { HitResult.Great, (128, 98, 68 ) }, + { HitResult.Good, (194, 164, 134) }, + { HitResult.Ok, (254, 224, 194) }, + { HitResult.Meh, (302, 272, 242) }, + { HitResult.Miss, (376, 346, 316) }, + }; + + /// + /// Hit window for a result. + /// The user can only achieve receive this result if is true. + /// + public double Perfect { get; protected set; } + + /// + /// Hit window for a result. + /// + public double Great { get; protected set; } + + /// + /// Hit window for a result. + /// + public double Good { get; protected set; } + + /// + /// Hit window for an result. + /// The user can only achieve this result if is true. + /// + public double Ok { get; protected set; } + + /// + /// Hit window for a result. + /// + public double Meh { get; protected set; } + + /// + /// Hit window for a result. + /// + public double Miss { get; protected set; } + + /// + /// Whether it's possible to achieve a result. + /// + public bool AllowsPerfect; + + /// + /// Whether it's possible to achieve a result. + /// + public bool AllowsOk; + + /// + /// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window. + /// + /// The parameter. + public HitWindows(double difficulty) + { + Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]); + Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); + Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); + Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]); + Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]); + Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); + } + + /// + /// Retrieves the for a time offset. + /// + /// The time offset. + /// The hit result, or if doesn't result in a judgement. + public HitResult ResultFor(double timeOffset) + { + timeOffset = Math.Abs(timeOffset); + + if (AllowsPerfect && timeOffset <= HalfWindowFor(HitResult.Perfect)) + return HitResult.Perfect; + if (timeOffset <= HalfWindowFor(HitResult.Great)) + return HitResult.Great; + if (timeOffset <= HalfWindowFor(HitResult.Good)) + return HitResult.Good; + if (AllowsOk && timeOffset <= HalfWindowFor(HitResult.Ok)) + return HitResult.Ok; + if (timeOffset <= HalfWindowFor(HitResult.Meh)) + return HitResult.Meh; + if (timeOffset <= HalfWindowFor(HitResult.Miss)) + return HitResult.Miss; + + return HitResult.None; + } + + /// + /// Retrieves half the hit window for a . + /// This is useful if the hit window for one half of the hittable range of a is required. + /// + /// The expected . + /// One half of the hit window for . + public double HalfWindowFor(HitResult result) + { + switch (result) + { + case HitResult.Perfect: + return Perfect / 2; + case HitResult.Great: + return Great / 2; + case HitResult.Good: + return Good / 2; + case HitResult.Ok: + return Ok / 2; + case HitResult.Meh: + return Meh / 2; + case HitResult.Miss: + return Miss / 2; + default: + throw new ArgumentException(nameof(result)); + } + } + + /// + /// Given a time offset, whether the can ever be hit in the future with a non- result. + /// This happens if is less than what is required for a result. + /// + /// The time offset. + /// Whether the can be hit at any point in the future from this time offset. + public bool CanBeHit(double timeOffset) => timeOffset <= HalfWindowFor(HitResult.Meh); + + /// + /// Multiplies all hit windows by a value. + /// + /// The hit windows to multiply. + /// The value to multiply each hit window by. + public static HitWindows operator *(HitWindows windows, double value) + { + windows.Perfect *= value; + windows.Great *= value; + windows.Good *= value; + windows.Ok *= value; + windows.Meh *= value; + windows.Miss *= value; + + return windows; + } + + /// + /// Divides all hit windows by a value. + /// + /// The hit windows to divide. + /// The value to divide each hit window by. + public static HitWindows operator /(HitWindows windows, double value) + { + windows.Perfect /= value; + windows.Great /= value; + windows.Good /= value; + windows.Ok /= value; + windows.Meh /= value; + windows.Miss /= value; + + return windows; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs index 366449a910..50035ea116 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Catch -{ - /// - /// Legacy osu!catch Hit-type, used for parsing Beatmaps. - /// - internal sealed class ConvertHit : HitObject, IHasCombo, IHasXPosition - { - public float X { get; set; } - - public bool NewCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Catch +{ + /// + /// Legacy osu!catch Hit-type, used for parsing Beatmaps. + /// + internal sealed class ConvertHit : HitObject, IHasCombo, IHasXPosition + { + public float X { get; set; } + + public bool NewCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index d0e7e09ad2..c7451dc978 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -1,52 +1,52 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Audio; -using osu.Game.Rulesets.Objects.Types; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Objects.Legacy.Catch -{ - /// - /// A HitObjectParser to parse legacy osu!catch Beatmaps. - /// - public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser - { - protected override HitObject CreateHit(Vector2 position, bool newCombo) - { - return new ConvertHit - { - X = position.X, - NewCombo = newCombo, - }; - } - - protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) - { - return new ConvertSlider - { - X = position.X, - NewCombo = newCombo, - ControlPoints = controlPoints, - Distance = length, - CurveType = curveType, - RepeatSamples = repeatSamples, - RepeatCount = repeatCount - }; - } - - protected override HitObject CreateSpinner(Vector2 position, double endTime) - { - return new ConvertSpinner - { - EndTime = endTime - }; - } - - protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) - { - return null; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Audio; +using osu.Game.Rulesets.Objects.Types; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Objects.Legacy.Catch +{ + /// + /// A HitObjectParser to parse legacy osu!catch Beatmaps. + /// + public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser + { + protected override HitObject CreateHit(Vector2 position, bool newCombo) + { + return new ConvertHit + { + X = position.X, + NewCombo = newCombo, + }; + } + + protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + { + return new ConvertSlider + { + X = position.X, + NewCombo = newCombo, + ControlPoints = controlPoints, + Distance = length, + CurveType = curveType, + RepeatSamples = repeatSamples, + RepeatCount = repeatCount + }; + } + + protected override HitObject CreateSpinner(Vector2 position, double endTime) + { + return new ConvertSpinner + { + EndTime = endTime + }; + } + + protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) + { + return null; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs index 6132ea63a6..73e277a125 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Catch -{ - /// - /// Legacy osu!catch Slider-type, used for parsing Beatmaps. - /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo - { - public float X { get; set; } - - public bool NewCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Catch +{ + /// + /// Legacy osu!catch Slider-type, used for parsing Beatmaps. + /// + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo + { + public float X { get; set; } + + public bool NewCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 1df7cce899..9dfe12f25e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Catch -{ - /// - /// Legacy osu!catch Spinner-type, used for parsing Beatmaps. - /// - internal sealed class ConvertSpinner : HitObject, IHasEndTime - { - public double EndTime { get; set; } - - public double Duration => EndTime - StartTime; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Catch +{ + /// + /// Legacy osu!catch Spinner-type, used for parsing Beatmaps. + /// + internal sealed class ConvertSpinner : HitObject, IHasEndTime + { + public double EndTime { get; set; } + + public double Duration => EndTime - StartTime; + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 5084b28cf2..95abc4edb3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -1,315 +1,315 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Rulesets.Objects.Types; -using System; -using System.Collections.Generic; -using System.Globalization; -using osu.Game.Beatmaps.Formats; -using osu.Game.Audio; -using System.Linq; -using osu.Framework.MathUtils; - -namespace osu.Game.Rulesets.Objects.Legacy -{ - /// - /// A HitObjectParser to parse legacy Beatmaps. - /// - public abstract class ConvertHitObjectParser : HitObjectParser - { - public override HitObject Parse(string text) - { - try - { - string[] split = text.Split(','); - - ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax; - bool combo = type.HasFlag(ConvertHitObjectType.NewCombo); - type &= ~ConvertHitObjectType.NewCombo; - - var soundType = (LegacySoundType)int.Parse(split[4]); - var bankInfo = new SampleBankInfo(); - - HitObject result = null; - - if ((type & ConvertHitObjectType.Circle) > 0) - { - result = CreateHit(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo); - - if (split.Length > 5) - readCustomSampleBanks(split[5], bankInfo); - } - else if ((type & ConvertHitObjectType.Slider) > 0) - { - var pos = new Vector2(int.Parse(split[0]), int.Parse(split[1])); - - CurveType curveType = CurveType.Catmull; - double length = 0; - var points = new List { Vector2.Zero }; - - string[] pointsplit = split[5].Split('|'); - foreach (string t in pointsplit) - { - if (t.Length == 1) - { - switch (t) - { - case @"C": - curveType = CurveType.Catmull; - break; - case @"B": - curveType = CurveType.Bezier; - break; - case @"L": - curveType = CurveType.Linear; - break; - case @"P": - curveType = CurveType.PerfectCurve; - break; - } - continue; - } - - string[] temp = t.Split(':'); - points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos); - } - - // osu-stable special-cased colinear perfect curves to a CurveType.Linear - bool isLinear(List p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); - if (points.Count == 3 && curveType == CurveType.PerfectCurve && isLinear(points)) - curveType = CurveType.Linear; - - int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); - - if (repeatCount > 9000) - throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high"); - - // osu-stable treated the first span of the slider as a repeat, but no repeats are happening - repeatCount = Math.Max(0, repeatCount - 1); - - - if (split.Length > 7) - length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); - - if (split.Length > 10) - readCustomSampleBanks(split[10], bankInfo); - - // One node for each repeat + the start and end nodes - int nodes = repeatCount + 2; - - // Populate node sample bank infos with the default hit object sample bank - var nodeBankInfos = new List(); - for (int i = 0; i < nodes; i++) - nodeBankInfos.Add(bankInfo.Clone()); - - // Read any per-node sample banks - if (split.Length > 9 && split[9].Length > 0) - { - string[] sets = split[9].Split('|'); - for (int i = 0; i < nodes; i++) - { - if (i >= sets.Length) - break; - - SampleBankInfo info = nodeBankInfos[i]; - readCustomSampleBanks(sets[i], info); - } - } - - // Populate node sound types with the default hit object sound type - var nodeSoundTypes = new List(); - for (int i = 0; i < nodes; i++) - nodeSoundTypes.Add(soundType); - - // Read any per-node sound types - if (split.Length > 8 && split[8].Length > 0) - { - string[] adds = split[8].Split('|'); - for (int i = 0; i < nodes; i++) - { - if (i >= adds.Length) - break; - - int sound; - int.TryParse(adds[i], out sound); - nodeSoundTypes[i] = (LegacySoundType)sound; - } - } - - // Generate the final per-node samples - var nodeSamples = new List>(nodes); - for (int i = 0; i < nodes; i++) - nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); - - result = CreateSlider(pos, combo, points, length, curveType, repeatCount, nodeSamples); - } - else if ((type & ConvertHitObjectType.Spinner) > 0) - { - result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture)); - - if (split.Length > 6) - readCustomSampleBanks(split[6], bankInfo); - } - else if ((type & ConvertHitObjectType.Hold) > 0) - { - // Note: Hold is generated by BMS converts - - double endTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); - - if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) - { - string[] ss = split[5].Split(':'); - endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture); - readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); - } - - result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime); - } - - if (result == null) - throw new InvalidOperationException($@"Unknown hit object type {type}."); - - result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); - result.Samples = convertSoundType(soundType, bankInfo); - - return result; - } - catch (FormatException) - { - throw new FormatException("One or more hit objects were malformed."); - } - } - - private void readCustomSampleBanks(string str, SampleBankInfo bankInfo) - { - if (string.IsNullOrEmpty(str)) - return; - - string[] split = str.Split(':'); - - var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]); - var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]); - - // Let's not implement this for now, because this doesn't fit nicely into the bank structure - //string sampleFile = split2.Length > 4 ? split2[4] : string.Empty; - - string stringBank = bank.ToString().ToLower(); - if (stringBank == @"none") - stringBank = null; - string stringAddBank = addbank.ToString().ToLower(); - if (stringAddBank == @"none") - stringAddBank = null; - - bankInfo.Normal = stringBank; - bankInfo.Add = stringAddBank; - - if (split.Length > 3) - bankInfo.Volume = int.Parse(split[3]); - } - - /// - /// Creates a legacy Hit-type hit object. - /// - /// The position of the hit object. - /// Whether the hit object creates a new combo. - /// The hit object. - protected abstract HitObject CreateHit(Vector2 position, bool newCombo); - - /// - /// Creats a legacy Slider-type hit object. - /// - /// The position of the hit object. - /// Whether the hit object creates a new combo. - /// The slider control points. - /// The slider length. - /// The slider curve type. - /// The slider repeat count. - /// The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider. - /// The hit object. - protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples); - - /// - /// Creates a legacy Spinner-type hit object. - /// - /// The position of the hit object. - /// The spinner end time. - /// The hit object. - protected abstract HitObject CreateSpinner(Vector2 position, double endTime); - - /// - /// Creates a legacy Hold-type hit object. - /// - /// The position of the hit object. - /// Whether the hit object creates a new combo. - /// The hold end time. - protected abstract HitObject CreateHold(Vector2 position, bool newCombo, double endTime); - - private List convertSoundType(LegacySoundType type, SampleBankInfo bankInfo) - { - var soundTypes = new List - { - new SampleInfo - { - Bank = bankInfo.Normal, - Name = SampleInfo.HIT_NORMAL, - Volume = bankInfo.Volume - } - }; - - if ((type & LegacySoundType.Finish) > 0) - { - soundTypes.Add(new SampleInfo - { - Bank = bankInfo.Add, - Name = SampleInfo.HIT_FINISH, - Volume = bankInfo.Volume - }); - } - - if ((type & LegacySoundType.Whistle) > 0) - { - soundTypes.Add(new SampleInfo - { - Bank = bankInfo.Add, - Name = SampleInfo.HIT_WHISTLE, - Volume = bankInfo.Volume - }); - } - - if ((type & LegacySoundType.Clap) > 0) - { - soundTypes.Add(new SampleInfo - { - Bank = bankInfo.Add, - Name = SampleInfo.HIT_CLAP, - Volume = bankInfo.Volume - }); - } - - return soundTypes; - } - - private class SampleBankInfo - { - public string Normal; - public string Add; - public int Volume; - - public SampleBankInfo Clone() - { - return (SampleBankInfo)MemberwiseClone(); - } - } - - [Flags] - private enum LegacySoundType - { - None = 0, - Normal = 1, - Whistle = 2, - Finish = 4, - Clap = 8 - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Rulesets.Objects.Types; +using System; +using System.Collections.Generic; +using System.Globalization; +using osu.Game.Beatmaps.Formats; +using osu.Game.Audio; +using System.Linq; +using osu.Framework.MathUtils; + +namespace osu.Game.Rulesets.Objects.Legacy +{ + /// + /// A HitObjectParser to parse legacy Beatmaps. + /// + public abstract class ConvertHitObjectParser : HitObjectParser + { + public override HitObject Parse(string text) + { + try + { + string[] split = text.Split(','); + + ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax; + bool combo = type.HasFlag(ConvertHitObjectType.NewCombo); + type &= ~ConvertHitObjectType.NewCombo; + + var soundType = (LegacySoundType)int.Parse(split[4]); + var bankInfo = new SampleBankInfo(); + + HitObject result = null; + + if ((type & ConvertHitObjectType.Circle) > 0) + { + result = CreateHit(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo); + + if (split.Length > 5) + readCustomSampleBanks(split[5], bankInfo); + } + else if ((type & ConvertHitObjectType.Slider) > 0) + { + var pos = new Vector2(int.Parse(split[0]), int.Parse(split[1])); + + CurveType curveType = CurveType.Catmull; + double length = 0; + var points = new List { Vector2.Zero }; + + string[] pointsplit = split[5].Split('|'); + foreach (string t in pointsplit) + { + if (t.Length == 1) + { + switch (t) + { + case @"C": + curveType = CurveType.Catmull; + break; + case @"B": + curveType = CurveType.Bezier; + break; + case @"L": + curveType = CurveType.Linear; + break; + case @"P": + curveType = CurveType.PerfectCurve; + break; + } + continue; + } + + string[] temp = t.Split(':'); + points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos); + } + + // osu-stable special-cased colinear perfect curves to a CurveType.Linear + bool isLinear(List p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); + if (points.Count == 3 && curveType == CurveType.PerfectCurve && isLinear(points)) + curveType = CurveType.Linear; + + int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); + + if (repeatCount > 9000) + throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high"); + + // osu-stable treated the first span of the slider as a repeat, but no repeats are happening + repeatCount = Math.Max(0, repeatCount - 1); + + + if (split.Length > 7) + length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); + + if (split.Length > 10) + readCustomSampleBanks(split[10], bankInfo); + + // One node for each repeat + the start and end nodes + int nodes = repeatCount + 2; + + // Populate node sample bank infos with the default hit object sample bank + var nodeBankInfos = new List(); + for (int i = 0; i < nodes; i++) + nodeBankInfos.Add(bankInfo.Clone()); + + // Read any per-node sample banks + if (split.Length > 9 && split[9].Length > 0) + { + string[] sets = split[9].Split('|'); + for (int i = 0; i < nodes; i++) + { + if (i >= sets.Length) + break; + + SampleBankInfo info = nodeBankInfos[i]; + readCustomSampleBanks(sets[i], info); + } + } + + // Populate node sound types with the default hit object sound type + var nodeSoundTypes = new List(); + for (int i = 0; i < nodes; i++) + nodeSoundTypes.Add(soundType); + + // Read any per-node sound types + if (split.Length > 8 && split[8].Length > 0) + { + string[] adds = split[8].Split('|'); + for (int i = 0; i < nodes; i++) + { + if (i >= adds.Length) + break; + + int sound; + int.TryParse(adds[i], out sound); + nodeSoundTypes[i] = (LegacySoundType)sound; + } + } + + // Generate the final per-node samples + var nodeSamples = new List>(nodes); + for (int i = 0; i < nodes; i++) + nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); + + result = CreateSlider(pos, combo, points, length, curveType, repeatCount, nodeSamples); + } + else if ((type & ConvertHitObjectType.Spinner) > 0) + { + result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture)); + + if (split.Length > 6) + readCustomSampleBanks(split[6], bankInfo); + } + else if ((type & ConvertHitObjectType.Hold) > 0) + { + // Note: Hold is generated by BMS converts + + double endTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); + + if (split.Length > 5 && !string.IsNullOrEmpty(split[5])) + { + string[] ss = split[5].Split(':'); + endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture); + readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); + } + + result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime); + } + + if (result == null) + throw new InvalidOperationException($@"Unknown hit object type {type}."); + + result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); + result.Samples = convertSoundType(soundType, bankInfo); + + return result; + } + catch (FormatException) + { + throw new FormatException("One or more hit objects were malformed."); + } + } + + private void readCustomSampleBanks(string str, SampleBankInfo bankInfo) + { + if (string.IsNullOrEmpty(str)) + return; + + string[] split = str.Split(':'); + + var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]); + var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]); + + // Let's not implement this for now, because this doesn't fit nicely into the bank structure + //string sampleFile = split2.Length > 4 ? split2[4] : string.Empty; + + string stringBank = bank.ToString().ToLower(); + if (stringBank == @"none") + stringBank = null; + string stringAddBank = addbank.ToString().ToLower(); + if (stringAddBank == @"none") + stringAddBank = null; + + bankInfo.Normal = stringBank; + bankInfo.Add = stringAddBank; + + if (split.Length > 3) + bankInfo.Volume = int.Parse(split[3]); + } + + /// + /// Creates a legacy Hit-type hit object. + /// + /// The position of the hit object. + /// Whether the hit object creates a new combo. + /// The hit object. + protected abstract HitObject CreateHit(Vector2 position, bool newCombo); + + /// + /// Creats a legacy Slider-type hit object. + /// + /// The position of the hit object. + /// Whether the hit object creates a new combo. + /// The slider control points. + /// The slider length. + /// The slider curve type. + /// The slider repeat count. + /// The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider. + /// The hit object. + protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples); + + /// + /// Creates a legacy Spinner-type hit object. + /// + /// The position of the hit object. + /// The spinner end time. + /// The hit object. + protected abstract HitObject CreateSpinner(Vector2 position, double endTime); + + /// + /// Creates a legacy Hold-type hit object. + /// + /// The position of the hit object. + /// Whether the hit object creates a new combo. + /// The hold end time. + protected abstract HitObject CreateHold(Vector2 position, bool newCombo, double endTime); + + private List convertSoundType(LegacySoundType type, SampleBankInfo bankInfo) + { + var soundTypes = new List + { + new SampleInfo + { + Bank = bankInfo.Normal, + Name = SampleInfo.HIT_NORMAL, + Volume = bankInfo.Volume + } + }; + + if ((type & LegacySoundType.Finish) > 0) + { + soundTypes.Add(new SampleInfo + { + Bank = bankInfo.Add, + Name = SampleInfo.HIT_FINISH, + Volume = bankInfo.Volume + }); + } + + if ((type & LegacySoundType.Whistle) > 0) + { + soundTypes.Add(new SampleInfo + { + Bank = bankInfo.Add, + Name = SampleInfo.HIT_WHISTLE, + Volume = bankInfo.Volume + }); + } + + if ((type & LegacySoundType.Clap) > 0) + { + soundTypes.Add(new SampleInfo + { + Bank = bankInfo.Add, + Name = SampleInfo.HIT_CLAP, + Volume = bankInfo.Volume + }); + } + + return soundTypes; + } + + private class SampleBankInfo + { + public string Normal; + public string Add; + public int Volume; + + public SampleBankInfo Clone() + { + return (SampleBankInfo)MemberwiseClone(); + } + } + + [Flags] + private enum LegacySoundType + { + None = 0, + Normal = 1, + Whistle = 2, + Finish = 4, + Clap = 8 + } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs index 16ea008216..c0626c3e56 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Rulesets.Objects.Legacy -{ - [Flags] - internal enum ConvertHitObjectType - { - Circle = 1 << 0, - Slider = 1 << 1, - NewCombo = 1 << 2, - Spinner = 1 << 3, - ColourHax = 112, - Hold = 1 << 7 - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Rulesets.Objects.Legacy +{ + [Flags] + internal enum ConvertHitObjectType + { + Circle = 1 << 0, + Slider = 1 << 1, + NewCombo = 1 << 2, + Spinner = 1 << 3, + ColourHax = 112, + Hold = 1 << 7 + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index df7c8b0a83..17848d1e6e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -1,49 +1,49 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using System.Collections.Generic; -using OpenTK; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Rulesets.Objects.Legacy -{ - internal abstract class ConvertSlider : HitObject, IHasCurve - { - /// - /// Scoring distance with a speed-adjusted beat length of 1 second. - /// - private const float base_scoring_distance = 100; - - /// - /// s don't need a curve since they're converted to ruleset-specific hitobjects. - /// - public SliderCurve Curve { get; } = null; - public List ControlPoints { get; set; } - public CurveType CurveType { get; set; } - - public double Distance { get; set; } - - public List> RepeatSamples { get; set; } - public int RepeatCount { get; set; } - - public double EndTime => StartTime + this.SpanCount() * Distance / Velocity; - public double Duration => EndTime - StartTime; - - public double Velocity = 1; - - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) - { - base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - - TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); - - double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; - - Velocity = scoringDistance / timingPoint.BeatLength; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using System.Collections.Generic; +using OpenTK; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Objects.Legacy +{ + internal abstract class ConvertSlider : HitObject, IHasCurve + { + /// + /// Scoring distance with a speed-adjusted beat length of 1 second. + /// + private const float base_scoring_distance = 100; + + /// + /// s don't need a curve since they're converted to ruleset-specific hitobjects. + /// + public SliderCurve Curve { get; } = null; + public List ControlPoints { get; set; } + public CurveType CurveType { get; set; } + + public double Distance { get; set; } + + public List> RepeatSamples { get; set; } + public int RepeatCount { get; set; } + + public double EndTime => StartTime + this.SpanCount() * Distance / Velocity; + public double Duration => EndTime - StartTime; + + public double Velocity = 1; + + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + + TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime); + + double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; + + Velocity = scoringDistance / timingPoint.BeatLength; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs index d756dc71ee..0db5a1dff1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Mania -{ - /// - /// Legacy osu!mania Hit-type, used for parsing Beatmaps. - /// - internal sealed class ConvertHit : HitObject, IHasXPosition, IHasCombo - { - public float X { get; set; } - - public bool NewCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Mania +{ + /// + /// Legacy osu!mania Hit-type, used for parsing Beatmaps. + /// + internal sealed class ConvertHit : HitObject, IHasXPosition, IHasCombo + { + public float X { get; set; } + + public bool NewCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index ad51b636d3..99ba1304e8 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -1,57 +1,57 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Audio; -using osu.Game.Rulesets.Objects.Types; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Objects.Legacy.Mania -{ - /// - /// A HitObjectParser to parse legacy osu!mania Beatmaps. - /// - public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser - { - protected override HitObject CreateHit(Vector2 position, bool newCombo) - { - return new ConvertHit - { - X = position.X, - NewCombo = newCombo, - }; - } - - protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) - { - return new ConvertSlider - { - X = position.X, - NewCombo = newCombo, - ControlPoints = controlPoints, - Distance = length, - CurveType = curveType, - RepeatSamples = repeatSamples, - RepeatCount = repeatCount - }; - } - - protected override HitObject CreateSpinner(Vector2 position, double endTime) - { - return new ConvertSpinner - { - X = position.X, - EndTime = endTime - }; - } - - protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) - { - return new ConvertHold - { - X = position.X, - EndTime = endTime - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Audio; +using osu.Game.Rulesets.Objects.Types; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Objects.Legacy.Mania +{ + /// + /// A HitObjectParser to parse legacy osu!mania Beatmaps. + /// + public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser + { + protected override HitObject CreateHit(Vector2 position, bool newCombo) + { + return new ConvertHit + { + X = position.X, + NewCombo = newCombo, + }; + } + + protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + { + return new ConvertSlider + { + X = position.X, + NewCombo = newCombo, + ControlPoints = controlPoints, + Distance = length, + CurveType = curveType, + RepeatSamples = repeatSamples, + RepeatCount = repeatCount + }; + } + + protected override HitObject CreateSpinner(Vector2 position, double endTime) + { + return new ConvertSpinner + { + X = position.X, + EndTime = endTime + }; + } + + protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) + { + return new ConvertHold + { + X = position.X, + EndTime = endTime + }; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs index 24006a4901..e3b35e2f8e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Mania -{ - internal sealed class ConvertHold : HitObject, IHasXPosition, IHasEndTime - { - public float X { get; set; } - - public double EndTime { get; set; } - - public double Duration => EndTime - StartTime; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Mania +{ + internal sealed class ConvertHold : HitObject, IHasXPosition, IHasEndTime + { + public float X { get; set; } + + public double EndTime { get; set; } + + public double Duration => EndTime - StartTime; + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs index d70c58e262..32fb197c62 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Mania -{ - /// - /// Legacy osu!mania Slider-type, used for parsing Beatmaps. - /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo - { - public float X { get; set; } - - public bool NewCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Mania +{ + /// + /// Legacy osu!mania Slider-type, used for parsing Beatmaps. + /// + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo + { + public float X { get; set; } + + public bool NewCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs index 0def535129..c9b3046698 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Mania -{ - /// - /// Legacy osu!mania Spinner-type, used for parsing Beatmaps. - /// - internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasXPosition - { - public double EndTime { get; set; } - - public double Duration => EndTime - StartTime; - - public float X { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Mania +{ + /// + /// Legacy osu!mania Spinner-type, used for parsing Beatmaps. + /// + internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasXPosition + { + public double EndTime { get; set; } + + public double Duration => EndTime - StartTime; + + public float X { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs index 330b8acfec..f83173f498 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Objects.Legacy.Osu -{ - /// - /// Legacy osu! Hit-type, used for parsing Beatmaps. - /// - internal sealed class ConvertHit : HitObject, IHasPosition, IHasCombo - { - public Vector2 Position { get; set; } - - public float X => Position.X; - - public float Y => Position.Y; - - public bool NewCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Objects.Legacy.Osu +{ + /// + /// Legacy osu! Hit-type, used for parsing Beatmaps. + /// + internal sealed class ConvertHit : HitObject, IHasPosition, IHasCombo + { + public Vector2 Position { get; set; } + + public float X => Position.X; + + public float Y => Position.Y; + + public bool NewCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index d2a0530dd9..801e4ea449 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -1,54 +1,54 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using osu.Game.Rulesets.Objects.Types; -using System.Collections.Generic; -using osu.Game.Audio; - -namespace osu.Game.Rulesets.Objects.Legacy.Osu -{ - /// - /// A HitObjectParser to parse legacy osu! Beatmaps. - /// - public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser - { - protected override HitObject CreateHit(Vector2 position, bool newCombo) - { - return new ConvertHit - { - Position = position, - NewCombo = newCombo, - }; - } - - protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) - { - return new ConvertSlider - { - Position = position, - NewCombo = newCombo, - ControlPoints = controlPoints, - Distance = Math.Max(0, length), - CurveType = curveType, - RepeatSamples = repeatSamples, - RepeatCount = repeatCount - }; - } - - protected override HitObject CreateSpinner(Vector2 position, double endTime) - { - return new ConvertSpinner - { - Position = position, - EndTime = endTime - }; - } - - protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) - { - return null; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using osu.Game.Rulesets.Objects.Types; +using System.Collections.Generic; +using osu.Game.Audio; + +namespace osu.Game.Rulesets.Objects.Legacy.Osu +{ + /// + /// A HitObjectParser to parse legacy osu! Beatmaps. + /// + public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser + { + protected override HitObject CreateHit(Vector2 position, bool newCombo) + { + return new ConvertHit + { + Position = position, + NewCombo = newCombo, + }; + } + + protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + { + return new ConvertSlider + { + Position = position, + NewCombo = newCombo, + ControlPoints = controlPoints, + Distance = Math.Max(0, length), + CurveType = curveType, + RepeatSamples = repeatSamples, + RepeatCount = repeatCount + }; + } + + protected override HitObject CreateSpinner(Vector2 position, double endTime) + { + return new ConvertSpinner + { + Position = position, + EndTime = endTime + }; + } + + protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) + { + return null; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs index 513ea0616b..c6033d482c 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Objects.Legacy.Osu -{ - /// - /// Legacy osu! Slider-type, used for parsing Beatmaps. - /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo - { - public Vector2 Position { get; set; } - - public float X => Position.X; - - public float Y => Position.Y; - - public bool NewCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Objects.Legacy.Osu +{ + /// + /// Legacy osu! Slider-type, used for parsing Beatmaps. + /// + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo + { + public Vector2 Position { get; set; } + + public float X => Position.X; + + public float Y => Position.Y; + + public bool NewCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index 4dbcc6d017..28aac6862e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Objects.Legacy.Osu -{ - /// - /// Legacy osu! Spinner-type, used for parsing Beatmaps. - /// - internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasPosition - { - public double EndTime { get; set; } - - public double Duration => EndTime - StartTime; - - public Vector2 Position { get; set; } - - public float X => Position.X; - - public float Y => Position.Y; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Objects.Legacy.Osu +{ + /// + /// Legacy osu! Spinner-type, used for parsing Beatmaps. + /// + internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasPosition + { + public double EndTime { get; set; } + + public double Duration => EndTime - StartTime; + + public Vector2 Position { get; set; } + + public float X => Position.X; + + public float Y => Position.Y; + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs index 1eafc477fa..72d18664bf 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Taiko -{ - /// - /// Legacy osu!taiko Hit-type, used for parsing Beatmaps. - /// - internal sealed class ConvertHit : HitObject, IHasCombo - { - public bool NewCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Taiko +{ + /// + /// Legacy osu!taiko Hit-type, used for parsing Beatmaps. + /// + internal sealed class ConvertHit : HitObject, IHasCombo + { + public bool NewCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index 1d3bbd0946..03b1a3187a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -1,50 +1,50 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Game.Rulesets.Objects.Types; -using System.Collections.Generic; -using osu.Game.Audio; - -namespace osu.Game.Rulesets.Objects.Legacy.Taiko -{ - /// - /// A HitObjectParser to parse legacy osu!taiko Beatmaps. - /// - public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser - { - protected override HitObject CreateHit(Vector2 position, bool newCombo) - { - return new ConvertHit - { - NewCombo = newCombo, - }; - } - - protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) - { - return new ConvertSlider - { - NewCombo = newCombo, - ControlPoints = controlPoints, - Distance = length, - CurveType = curveType, - RepeatSamples = repeatSamples, - RepeatCount = repeatCount - }; - } - - protected override HitObject CreateSpinner(Vector2 position, double endTime) - { - return new ConvertSpinner - { - EndTime = endTime - }; - } - - protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) - { - return null; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Rulesets.Objects.Types; +using System.Collections.Generic; +using osu.Game.Audio; + +namespace osu.Game.Rulesets.Objects.Legacy.Taiko +{ + /// + /// A HitObjectParser to parse legacy osu!taiko Beatmaps. + /// + public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser + { + protected override HitObject CreateHit(Vector2 position, bool newCombo) + { + return new ConvertHit + { + NewCombo = newCombo, + }; + } + + protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + { + return new ConvertSlider + { + NewCombo = newCombo, + ControlPoints = controlPoints, + Distance = length, + CurveType = curveType, + RepeatSamples = repeatSamples, + RepeatCount = repeatCount + }; + } + + protected override HitObject CreateSpinner(Vector2 position, double endTime) + { + return new ConvertSpinner + { + EndTime = endTime + }; + } + + protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime) + { + return null; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs index a5e22602b0..e810e687bd 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Taiko -{ - /// - /// Legacy osu!taiko Slider-type, used for parsing Beatmaps. - /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasCombo - { - public bool NewCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Taiko +{ + /// + /// Legacy osu!taiko Slider-type, used for parsing Beatmaps. + /// + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasCombo + { + public bool NewCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs index 3047228cdc..193e50aed6 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Rulesets.Objects.Legacy.Taiko -{ - /// - /// Legacy osu!taiko Spinner-type, used for parsing Beatmaps. - /// - internal sealed class ConvertSpinner : HitObject, IHasEndTime - { - public double EndTime { get; set; } - - public double Duration => EndTime - StartTime; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy.Taiko +{ + /// + /// Legacy osu!taiko Spinner-type, used for parsing Beatmaps. + /// + internal sealed class ConvertSpinner : HitObject, IHasEndTime + { + public double EndTime { get; set; } + + public double Duration => EndTime - StartTime; + } +} diff --git a/osu.Game/Rulesets/Objects/SliderCurve.cs b/osu.Game/Rulesets/Objects/SliderCurve.cs index 2b16f463df..86fe74f9af 100644 --- a/osu.Game/Rulesets/Objects/SliderCurve.cs +++ b/osu.Game/Rulesets/Objects/SliderCurve.cs @@ -1,205 +1,205 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Objects -{ - public class SliderCurve - { - public double Distance; - - public List ControlPoints; - - public CurveType CurveType = CurveType.PerfectCurve; - - public Vector2 Offset; - - private readonly List calculatedPath = new List(); - private readonly List cumulativeLength = new List(); - - private List calculateSubpath(List subControlPoints) - { - switch (CurveType) - { - case CurveType.Linear: - return subControlPoints; - case CurveType.PerfectCurve: - //we can only use CircularArc iff we have exactly three control points and no dissection. - if (ControlPoints.Count != 3 || subControlPoints.Count != 3) - break; - - // Here we have exactly 3 control points. Attempt to fit a circular arc. - List subpath = new CircularArcApproximator(subControlPoints[0], subControlPoints[1], subControlPoints[2]).CreateArc(); - - // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. - if (subpath.Count == 0) - break; - - return subpath; - case CurveType.Catmull: - return new CatmullApproximator(subControlPoints).CreateCatmull(); - } - - return new BezierApproximator(subControlPoints).CreateBezier(); - } - - private void calculatePath() - { - calculatedPath.Clear(); - - // Sliders may consist of various subpaths separated by two consecutive vertices - // with the same position. The following loop parses these subpaths and computes - // their shape independently, consecutively appending them to calculatedPath. - List subControlPoints = new List(); - for (int i = 0; i < ControlPoints.Count; ++i) - { - subControlPoints.Add(ControlPoints[i]); - if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1]) - { - List subpath = calculateSubpath(subControlPoints); - foreach (Vector2 t in subpath) - if (calculatedPath.Count == 0 || calculatedPath.Last() != t) - calculatedPath.Add(t); - - subControlPoints.Clear(); - } - } - } - - private void calculateCumulativeLengthAndTrimPath() - { - double l = 0; - - cumulativeLength.Clear(); - cumulativeLength.Add(l); - - for (int i = 0; i < calculatedPath.Count - 1; ++i) - { - Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; - double d = diff.Length; - - // Shorten slider curves that are too long compared to what's - // in the .osu file. - if (Distance - l < d) - { - calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((Distance - l) / d); - calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); - - l = Distance; - cumulativeLength.Add(l); - break; - } - - l += d; - cumulativeLength.Add(l); - } - - //TODO: Figure out if the following code is needed in some cases. Judging by the map - // "Transform" http://osu.ppy.sh/s/484689 it seems like we should _not_ be doing this. - // Lengthen slider curves that are too short compared to what's - // in the .osu file. - /*if (l < Length && calculatedPath.Count > 1) - { - Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; - double d = diff.Length; - - if (d <= 0) - return; - - calculatedPath[calculatedPath.Count - 1] += diff * (float)((Length - l) / d); - cumulativeLength[calculatedPath.Count - 1] = Length; - }*/ - } - - public void Calculate() - { - calculatePath(); - calculateCumulativeLengthAndTrimPath(); - } - - private int indexOfDistance(double d) - { - int i = cumulativeLength.BinarySearch(d); - if (i < 0) i = ~i; - - return i; - } - - private double progressToDistance(double progress) - { - return MathHelper.Clamp(progress, 0, 1) * Distance; - } - - private Vector2 interpolateVertices(int i, double d) - { - if (calculatedPath.Count == 0) - return Vector2.Zero; - - if (i <= 0) - return calculatedPath.First(); - else if (i >= calculatedPath.Count) - return calculatedPath.Last(); - - Vector2 p0 = calculatedPath[i - 1]; - Vector2 p1 = calculatedPath[i]; - - double d0 = cumulativeLength[i - 1]; - double d1 = cumulativeLength[i]; - - // Avoid division by and almost-zero number in case two points are extremely close to each other. - if (Precision.AlmostEquals(d0, d1)) - return p0; - - double w = (d - d0) / (d1 - d0); - return p0 + (p1 - p0) * (float)w; - } - - /// - /// Computes the slider curve until a given progress that ranges from 0 (beginning of the slider) - /// to 1 (end of the slider) and stores the generated path in the given list. - /// - /// The list to be filled with the computed curve. - /// Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). - /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). - public void GetPathToProgress(List path, double p0, double p1) - { - if (calculatedPath.Count == 0 && ControlPoints.Count > 0) - Calculate(); - - double d0 = progressToDistance(p0); - double d1 = progressToDistance(p1); - - path.Clear(); - - int i = 0; - for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) { } - - path.Add(interpolateVertices(i, d0) + Offset); - - for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) - path.Add(calculatedPath[i] + Offset); - - path.Add(interpolateVertices(i, d1) + Offset); - } - - /// - /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the curve) - /// to 1 (end of the curve). - /// - /// Ranges from 0 (beginning of the curve) to 1 (end of the curve). - /// - public Vector2 PositionAt(double progress) - { - if (calculatedPath.Count == 0 && ControlPoints.Count > 0) - Calculate(); - - double d = progressToDistance(progress); - return interpolateVertices(indexOfDistance(d), d) + Offset; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.Objects +{ + public class SliderCurve + { + public double Distance; + + public List ControlPoints; + + public CurveType CurveType = CurveType.PerfectCurve; + + public Vector2 Offset; + + private readonly List calculatedPath = new List(); + private readonly List cumulativeLength = new List(); + + private List calculateSubpath(List subControlPoints) + { + switch (CurveType) + { + case CurveType.Linear: + return subControlPoints; + case CurveType.PerfectCurve: + //we can only use CircularArc iff we have exactly three control points and no dissection. + if (ControlPoints.Count != 3 || subControlPoints.Count != 3) + break; + + // Here we have exactly 3 control points. Attempt to fit a circular arc. + List subpath = new CircularArcApproximator(subControlPoints[0], subControlPoints[1], subControlPoints[2]).CreateArc(); + + // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. + if (subpath.Count == 0) + break; + + return subpath; + case CurveType.Catmull: + return new CatmullApproximator(subControlPoints).CreateCatmull(); + } + + return new BezierApproximator(subControlPoints).CreateBezier(); + } + + private void calculatePath() + { + calculatedPath.Clear(); + + // Sliders may consist of various subpaths separated by two consecutive vertices + // with the same position. The following loop parses these subpaths and computes + // their shape independently, consecutively appending them to calculatedPath. + List subControlPoints = new List(); + for (int i = 0; i < ControlPoints.Count; ++i) + { + subControlPoints.Add(ControlPoints[i]); + if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1]) + { + List subpath = calculateSubpath(subControlPoints); + foreach (Vector2 t in subpath) + if (calculatedPath.Count == 0 || calculatedPath.Last() != t) + calculatedPath.Add(t); + + subControlPoints.Clear(); + } + } + } + + private void calculateCumulativeLengthAndTrimPath() + { + double l = 0; + + cumulativeLength.Clear(); + cumulativeLength.Add(l); + + for (int i = 0; i < calculatedPath.Count - 1; ++i) + { + Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; + double d = diff.Length; + + // Shorten slider curves that are too long compared to what's + // in the .osu file. + if (Distance - l < d) + { + calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((Distance - l) / d); + calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); + + l = Distance; + cumulativeLength.Add(l); + break; + } + + l += d; + cumulativeLength.Add(l); + } + + //TODO: Figure out if the following code is needed in some cases. Judging by the map + // "Transform" http://osu.ppy.sh/s/484689 it seems like we should _not_ be doing this. + // Lengthen slider curves that are too short compared to what's + // in the .osu file. + /*if (l < Length && calculatedPath.Count > 1) + { + Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; + double d = diff.Length; + + if (d <= 0) + return; + + calculatedPath[calculatedPath.Count - 1] += diff * (float)((Length - l) / d); + cumulativeLength[calculatedPath.Count - 1] = Length; + }*/ + } + + public void Calculate() + { + calculatePath(); + calculateCumulativeLengthAndTrimPath(); + } + + private int indexOfDistance(double d) + { + int i = cumulativeLength.BinarySearch(d); + if (i < 0) i = ~i; + + return i; + } + + private double progressToDistance(double progress) + { + return MathHelper.Clamp(progress, 0, 1) * Distance; + } + + private Vector2 interpolateVertices(int i, double d) + { + if (calculatedPath.Count == 0) + return Vector2.Zero; + + if (i <= 0) + return calculatedPath.First(); + else if (i >= calculatedPath.Count) + return calculatedPath.Last(); + + Vector2 p0 = calculatedPath[i - 1]; + Vector2 p1 = calculatedPath[i]; + + double d0 = cumulativeLength[i - 1]; + double d1 = cumulativeLength[i]; + + // Avoid division by and almost-zero number in case two points are extremely close to each other. + if (Precision.AlmostEquals(d0, d1)) + return p0; + + double w = (d - d0) / (d1 - d0); + return p0 + (p1 - p0) * (float)w; + } + + /// + /// Computes the slider curve until a given progress that ranges from 0 (beginning of the slider) + /// to 1 (end of the slider) and stores the generated path in the given list. + /// + /// The list to be filled with the computed curve. + /// Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). + /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). + public void GetPathToProgress(List path, double p0, double p1) + { + if (calculatedPath.Count == 0 && ControlPoints.Count > 0) + Calculate(); + + double d0 = progressToDistance(p0); + double d1 = progressToDistance(p1); + + path.Clear(); + + int i = 0; + for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) { } + + path.Add(interpolateVertices(i, d0) + Offset); + + for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) + path.Add(calculatedPath[i] + Offset); + + path.Add(interpolateVertices(i, d1) + Offset); + } + + /// + /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the curve) + /// to 1 (end of the curve). + /// + /// Ranges from 0 (beginning of the curve) to 1 (end of the curve). + /// + public Vector2 PositionAt(double progress) + { + if (calculatedPath.Count == 0 && ControlPoints.Count > 0) + Calculate(); + + double d = progressToDistance(progress); + return interpolateVertices(indexOfDistance(d), d) + Offset; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/CurveType.cs b/osu.Game/Rulesets/Objects/Types/CurveType.cs index 9b6772539e..1cee6202b6 100644 --- a/osu.Game/Rulesets/Objects/Types/CurveType.cs +++ b/osu.Game/Rulesets/Objects/Types/CurveType.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - public enum CurveType - { - Catmull, - Bezier, - Linear, - PerfectCurve - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + public enum CurveType + { + Catmull, + Bezier, + Linear, + PerfectCurve + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs index a10f5d814d..cb8b6f495a 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that is part of a combo. - /// - public interface IHasCombo - { - /// - /// Whether the HitObject starts a new combo. - /// - bool NewCombo { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that is part of a combo. + /// + public interface IHasCombo + { + /// + /// Whether the HitObject starts a new combo. + /// + bool NewCombo { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs b/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs index 68474a6e2c..c5d0152ae7 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that is part of a combo and has extended information about its position relative to other combo objects. - /// - public interface IHasComboIndex : IHasCombo - { - /// - /// The offset of this hitobject in the current combo. - /// - int IndexInCurrentCombo { get; set; } - - /// - /// The offset of this hitobject in the current combo. - /// - int ComboIndex { get; set; } - - /// - /// Whether this is the last object in the current combo. - /// - bool LastInCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that is part of a combo and has extended information about its position relative to other combo objects. + /// + public interface IHasComboIndex : IHasCombo + { + /// + /// The offset of this hitobject in the current combo. + /// + int IndexInCurrentCombo { get; set; } + + /// + /// The offset of this hitobject in the current combo. + /// + int ComboIndex { get; set; } + + /// + /// Whether this is the last object in the current combo. + /// + bool LastInCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index 1d4f4e0f90..a3dded94ec 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that is part of a combo and has extended information about its position relative to other combo objects. - /// - public interface IHasComboInformation : IHasCombo - { - /// - /// The offset of this hitobject in the current combo. - /// - int IndexInCurrentCombo { get; set; } - - /// - /// The offset of this combo in relation to the beatmap. - /// - int ComboIndex { get; set; } - - /// - /// Whether this is the last object in the current combo. - /// - bool LastInCombo { get; set; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that is part of a combo and has extended information about its position relative to other combo objects. + /// + public interface IHasComboInformation : IHasCombo + { + /// + /// The offset of this hitobject in the current combo. + /// + int IndexInCurrentCombo { get; set; } + + /// + /// The offset of this combo in relation to the beatmap. + /// + int ComboIndex { get; set; } + + /// + /// Whether this is the last object in the current combo. + /// + bool LastInCombo { get; set; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index 251ad3e3cd..54dcb26ae2 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -1,64 +1,64 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using OpenTK; - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that has a curve. - /// - public interface IHasCurve : IHasDistance, IHasRepeats - { - /// - /// The curve. - /// - SliderCurve Curve { get; } - - /// - /// The control points that shape the curve. - /// - List ControlPoints { get; } - - /// - /// The type of curve. - /// - CurveType CurveType { get; } - } - - public static class HasCurveExtensions - { - /// - /// Computes the position on the curve relative to how much of the has been completed. - /// - /// The curve. - /// [0, 1] where 0 is the start time of the and 1 is the end time of the . - /// The position on the curve. - public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) - => obj.Curve.PositionAt(obj.ProgressAt(progress)); - - /// - /// Computes the progress along the curve relative to how much of the has been completed. - /// - /// The curve. - /// [0, 1] where 0 is the start time of the and 1 is the end time of the . - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - public static double ProgressAt(this IHasCurve obj, double progress) - { - double p = progress * obj.SpanCount() % 1; - if (obj.SpanAt(progress) % 2 == 1) - p = 1 - p; - return p; - } - - /// - /// Determines which span of the curve the progress point is on. - /// - /// The curve. - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - /// [0, SpanCount) where 0 is the first run. - public static int SpanAt(this IHasCurve obj, double progress) - => (int)(progress * obj.SpanCount()); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK; + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that has a curve. + /// + public interface IHasCurve : IHasDistance, IHasRepeats + { + /// + /// The curve. + /// + SliderCurve Curve { get; } + + /// + /// The control points that shape the curve. + /// + List ControlPoints { get; } + + /// + /// The type of curve. + /// + CurveType CurveType { get; } + } + + public static class HasCurveExtensions + { + /// + /// Computes the position on the curve relative to how much of the has been completed. + /// + /// The curve. + /// [0, 1] where 0 is the start time of the and 1 is the end time of the . + /// The position on the curve. + public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) + => obj.Curve.PositionAt(obj.ProgressAt(progress)); + + /// + /// Computes the progress along the curve relative to how much of the has been completed. + /// + /// The curve. + /// [0, 1] where 0 is the start time of the and 1 is the end time of the . + /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. + public static double ProgressAt(this IHasCurve obj, double progress) + { + double p = progress * obj.SpanCount() % 1; + if (obj.SpanAt(progress) % 2 == 1) + p = 1 - p; + return p; + } + + /// + /// Determines which span of the curve the progress point is on. + /// + /// The curve. + /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. + /// [0, SpanCount) where 0 is the first run. + public static int SpanAt(this IHasCurve obj, double progress) + => (int)(progress * obj.SpanCount()); + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs index 98ef898f22..535eb21ff4 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that has a positional length. - /// - public interface IHasDistance : IHasEndTime - { - /// - /// The positional length of the HitObject. - /// - double Distance { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that has a positional length. + /// + public interface IHasDistance : IHasEndTime + { + /// + /// The positional length of the HitObject. + /// + double Distance { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs index 1fedb8e061..8383e879a3 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that ends at a different time than its start time. - /// - public interface IHasEndTime - { - /// - /// The time at which the HitObject ends. - /// - double EndTime { get; } - - /// - /// The duration of the HitObject. - /// - double Duration { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that ends at a different time than its start time. + /// + public interface IHasEndTime + { + /// + /// The time at which the HitObject ends. + /// + double EndTime { get; } + + /// + /// The duration of the HitObject. + /// + double Duration { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasHold.cs b/osu.Game/Rulesets/Objects/Types/IHasHold.cs index 1c33187e93..429c582bab 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasHold.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasHold.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A special type of HitObject, mostly used for legacy conversion of "holds". - /// - public interface IHasHold - { - /// - /// The time at which the hold ends. - /// - double EndTime { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A special type of HitObject, mostly used for legacy conversion of "holds". + /// + public interface IHasHold + { + /// + /// The time at which the hold ends. + /// + double EndTime { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasPosition.cs b/osu.Game/Rulesets/Objects/Types/IHasPosition.cs index 0d9cd5ad2e..eabae611d5 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasPosition.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasPosition.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that has a starting position. - /// - public interface IHasPosition : IHasXPosition, IHasYPosition - { - /// - /// The starting position of the HitObject. - /// - Vector2 Position { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that has a starting position. + /// + public interface IHasPosition : IHasXPosition, IHasYPosition + { + /// + /// The starting position of the HitObject. + /// + Vector2 Position { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs index 75dd3776f9..7d918ff160 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Audio; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that spans some length. - /// - public interface IHasRepeats : IHasEndTime - { - /// - /// The amount of times the HitObject repeats. - /// - int RepeatCount { get; } - - /// - /// The samples to be played when each repeat node is hit (0 -> first repeat node, 1 -> second repeat node, etc). - /// - List> RepeatSamples { get; } - } - - public static class HasRepeatsExtensions - { - /// - /// The amount of times the length of this spans. - /// - /// The object that has repeats. - public static int SpanCount(this IHasRepeats obj) => obj.RepeatCount + 1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Audio; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that spans some length. + /// + public interface IHasRepeats : IHasEndTime + { + /// + /// The amount of times the HitObject repeats. + /// + int RepeatCount { get; } + + /// + /// The samples to be played when each repeat node is hit (0 -> first repeat node, 1 -> second repeat node, etc). + /// + List> RepeatSamples { get; } + } + + public static class HasRepeatsExtensions + { + /// + /// The amount of times the length of this spans. + /// + /// The object that has repeats. + public static int SpanCount(this IHasRepeats obj) => obj.RepeatCount + 1; + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasXPosition.cs b/osu.Game/Rulesets/Objects/Types/IHasXPosition.cs index f73f338778..49b1d7cb28 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasXPosition.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasXPosition.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that has a starting X-position. - /// - public interface IHasXPosition - { - /// - /// The starting X-position of this HitObject. - /// - float X { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that has a starting X-position. + /// + public interface IHasXPosition + { + /// + /// The starting X-position of this HitObject. + /// + float X { get; } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasYPosition.cs b/osu.Game/Rulesets/Objects/Types/IHasYPosition.cs index fc2d21481f..8ec7ab39fe 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasYPosition.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasYPosition.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that has a starting Y-position. - /// - public interface IHasYPosition - { - /// - /// The starting Y-position of this HitObject. - /// - float Y { get; } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A HitObject that has a starting Y-position. + /// + public interface IHasYPosition + { + /// + /// The starting Y-position of this HitObject. + /// + float Y { get; } + } +} diff --git a/osu.Game/Rulesets/Replays/AutoGenerator.cs b/osu.Game/Rulesets/Replays/AutoGenerator.cs index 21aa77d4cd..3ac0c3297c 100644 --- a/osu.Game/Rulesets/Replays/AutoGenerator.cs +++ b/osu.Game/Rulesets/Replays/AutoGenerator.cs @@ -1,39 +1,39 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects; -using osu.Game.Beatmaps; - -namespace osu.Game.Rulesets.Replays -{ - public abstract class AutoGenerator : IAutoGenerator - where T : HitObject - { - /// - /// Creates the auto replay and returns it. - /// Every subclass of OsuAutoGeneratorBase should implement this! - /// - public abstract Replay Generate(); - - #region Parameters - - /// - /// The beatmap we're making. - /// - protected Beatmap Beatmap; - - #endregion - - protected AutoGenerator(Beatmap beatmap) - { - Beatmap = beatmap; - } - - #region Constants - - // Shared amongst all modes - protected const double KEY_UP_DELAY = 50; - - #endregion - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Objects; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Replays +{ + public abstract class AutoGenerator : IAutoGenerator + where T : HitObject + { + /// + /// Creates the auto replay and returns it. + /// Every subclass of OsuAutoGeneratorBase should implement this! + /// + public abstract Replay Generate(); + + #region Parameters + + /// + /// The beatmap we're making. + /// + protected Beatmap Beatmap; + + #endregion + + protected AutoGenerator(Beatmap beatmap) + { + Beatmap = beatmap; + } + + #region Constants + + // Shared amongst all modes + protected const double KEY_UP_DELAY = 50; + + #endregion + } +} diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 5ffd67423e..0f5490a182 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -1,126 +1,126 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Input; -using osu.Game.Input.Handlers; -using OpenTK; -using OpenTK.Input; -using KeyboardState = osu.Framework.Input.KeyboardState; -using MouseState = osu.Framework.Input.MouseState; - -namespace osu.Game.Rulesets.Replays -{ - /// - /// The ReplayHandler will take a replay and handle the propagation of updates to the input stack. - /// It handles logic of any frames which *must* be executed. - /// - public abstract class FramedReplayInputHandler : ReplayInputHandler - where TFrame : ReplayFrame - { - private readonly Replay replay; - - protected List Frames => replay.Frames; - - public TFrame CurrentFrame => !HasFrames ? null : (TFrame)Frames[currentFrameIndex]; - public TFrame NextFrame => !HasFrames ? null : (TFrame)Frames[nextFrameIndex]; - - private int currentFrameIndex; - - private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1); - - protected FramedReplayInputHandler(Replay replay) - { - this.replay = replay; - } - - private bool advanceFrame() - { - int newFrame = nextFrameIndex; - - //ensure we aren't at an extent. - if (newFrame == currentFrameIndex) return false; - - currentFrameIndex = newFrame; - return true; - } - - public override List GetPendingStates() => new List(); - - public bool AtLastFrame => currentFrameIndex == Frames.Count - 1; - public bool AtFirstFrame => currentFrameIndex == 0; - - private const double sixty_frame_time = 1000.0 / 60; - - protected double CurrentTime { get; private set; } - private int currentDirection; - - /// - /// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data. - /// Disabling this can make replay playback smoother (useful for autoplay, currently). - /// - public bool FrameAccuratePlayback = true; - - protected bool HasFrames => Frames.Count > 0; - - private bool inImportantSection => - HasFrames && FrameAccuratePlayback && - //a button is in a pressed state - IsImportant(currentDirection > 0 ? CurrentFrame : NextFrame) && - //the next frame is within an allowable time span - Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2; - - protected virtual bool IsImportant(TFrame frame) => false; - - /// - /// Update the current frame based on an incoming time value. - /// There are cases where we return a "must-use" time value that is different from the input. - /// This is to ensure accurate playback of replay data. - /// - /// The time which we should use for finding the current frame. - /// The usable time value. If null, we should not advance time as we do not have enough data. - public override double? SetFrameFromTime(double time) - { - currentDirection = time.CompareTo(CurrentTime); - if (currentDirection == 0) currentDirection = 1; - - if (HasFrames) - { - // check if the next frame is in the "future" for the current playback direction - if (currentDirection != time.CompareTo(NextFrame.Time)) - { - // if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null. - if (inImportantSection) - return null; - } - else if (advanceFrame()) - { - // If going backwards, we need to execute once _before_ the frame time to reverse any judgements - // that would occur as a result of this frame in forward playback - if (currentDirection == -1) - return CurrentTime = CurrentFrame.Time - 1; - return CurrentTime = CurrentFrame.Time; - } - } - - return CurrentTime = time; - } - - protected class ReplayMouseState : MouseState - { - public ReplayMouseState(Vector2 position) - { - Position = position; - } - } - - protected class ReplayKeyboardState : KeyboardState - { - public ReplayKeyboardState(List keys) - { - Keys = keys; - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Input; +using osu.Game.Input.Handlers; +using OpenTK; +using OpenTK.Input; +using KeyboardState = osu.Framework.Input.KeyboardState; +using MouseState = osu.Framework.Input.MouseState; + +namespace osu.Game.Rulesets.Replays +{ + /// + /// The ReplayHandler will take a replay and handle the propagation of updates to the input stack. + /// It handles logic of any frames which *must* be executed. + /// + public abstract class FramedReplayInputHandler : ReplayInputHandler + where TFrame : ReplayFrame + { + private readonly Replay replay; + + protected List Frames => replay.Frames; + + public TFrame CurrentFrame => !HasFrames ? null : (TFrame)Frames[currentFrameIndex]; + public TFrame NextFrame => !HasFrames ? null : (TFrame)Frames[nextFrameIndex]; + + private int currentFrameIndex; + + private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1); + + protected FramedReplayInputHandler(Replay replay) + { + this.replay = replay; + } + + private bool advanceFrame() + { + int newFrame = nextFrameIndex; + + //ensure we aren't at an extent. + if (newFrame == currentFrameIndex) return false; + + currentFrameIndex = newFrame; + return true; + } + + public override List GetPendingStates() => new List(); + + public bool AtLastFrame => currentFrameIndex == Frames.Count - 1; + public bool AtFirstFrame => currentFrameIndex == 0; + + private const double sixty_frame_time = 1000.0 / 60; + + protected double CurrentTime { get; private set; } + private int currentDirection; + + /// + /// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data. + /// Disabling this can make replay playback smoother (useful for autoplay, currently). + /// + public bool FrameAccuratePlayback = true; + + protected bool HasFrames => Frames.Count > 0; + + private bool inImportantSection => + HasFrames && FrameAccuratePlayback && + //a button is in a pressed state + IsImportant(currentDirection > 0 ? CurrentFrame : NextFrame) && + //the next frame is within an allowable time span + Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2; + + protected virtual bool IsImportant(TFrame frame) => false; + + /// + /// Update the current frame based on an incoming time value. + /// There are cases where we return a "must-use" time value that is different from the input. + /// This is to ensure accurate playback of replay data. + /// + /// The time which we should use for finding the current frame. + /// The usable time value. If null, we should not advance time as we do not have enough data. + public override double? SetFrameFromTime(double time) + { + currentDirection = time.CompareTo(CurrentTime); + if (currentDirection == 0) currentDirection = 1; + + if (HasFrames) + { + // check if the next frame is in the "future" for the current playback direction + if (currentDirection != time.CompareTo(NextFrame.Time)) + { + // if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null. + if (inImportantSection) + return null; + } + else if (advanceFrame()) + { + // If going backwards, we need to execute once _before_ the frame time to reverse any judgements + // that would occur as a result of this frame in forward playback + if (currentDirection == -1) + return CurrentTime = CurrentFrame.Time - 1; + return CurrentTime = CurrentFrame.Time; + } + } + + return CurrentTime = time; + } + + protected class ReplayMouseState : MouseState + { + public ReplayMouseState(Vector2 position) + { + Position = position; + } + } + + protected class ReplayKeyboardState : KeyboardState + { + public ReplayKeyboardState(List keys) + { + Keys = keys; + } + } + } +} diff --git a/osu.Game/Rulesets/Replays/IAutoGenerator.cs b/osu.Game/Rulesets/Replays/IAutoGenerator.cs index 7e26fb5758..4ef5f16f39 100644 --- a/osu.Game/Rulesets/Replays/IAutoGenerator.cs +++ b/osu.Game/Rulesets/Replays/IAutoGenerator.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Replays -{ - public interface IAutoGenerator - { - Replay Generate(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Replays +{ + public interface IAutoGenerator + { + Replay Generate(); + } +} diff --git a/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs b/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs index 945cb95e79..d39d765bfe 100644 --- a/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs @@ -1,38 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; - -namespace osu.Game.Rulesets.Replays.Legacy -{ - public class LegacyReplayFrame : ReplayFrame - { - public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0); - - public float? MouseX; - public float? MouseY; - - public bool MouseLeft => MouseLeft1 || MouseLeft2; - public bool MouseRight => MouseRight1 || MouseRight2; - - public bool MouseLeft1 => (ButtonState & ReplayButtonState.Left1) > 0; - public bool MouseRight1 => (ButtonState & ReplayButtonState.Right1) > 0; - public bool MouseLeft2 => (ButtonState & ReplayButtonState.Left2) > 0; - public bool MouseRight2 => (ButtonState & ReplayButtonState.Right2) > 0; - - public ReplayButtonState ButtonState; - - public LegacyReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) - : base(time) - { - MouseX = mouseX; - MouseY = mouseY; - ButtonState = buttonState; - } - - public override string ToString() - { - return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}"; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Rulesets.Replays.Legacy +{ + public class LegacyReplayFrame : ReplayFrame + { + public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0); + + public float? MouseX; + public float? MouseY; + + public bool MouseLeft => MouseLeft1 || MouseLeft2; + public bool MouseRight => MouseRight1 || MouseRight2; + + public bool MouseLeft1 => (ButtonState & ReplayButtonState.Left1) > 0; + public bool MouseRight1 => (ButtonState & ReplayButtonState.Right1) > 0; + public bool MouseLeft2 => (ButtonState & ReplayButtonState.Left2) > 0; + public bool MouseRight2 => (ButtonState & ReplayButtonState.Right2) > 0; + + public ReplayButtonState ButtonState; + + public LegacyReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) + : base(time) + { + MouseX = mouseX; + MouseY = mouseY; + ButtonState = buttonState; + } + + public override string ToString() + { + return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}"; + } + } +} diff --git a/osu.Game/Rulesets/Replays/Legacy/ReplayButtonState.cs b/osu.Game/Rulesets/Replays/Legacy/ReplayButtonState.cs index d0706411d2..ee09414287 100644 --- a/osu.Game/Rulesets/Replays/Legacy/ReplayButtonState.cs +++ b/osu.Game/Rulesets/Replays/Legacy/ReplayButtonState.cs @@ -1,18 +1,18 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; - -namespace osu.Game.Rulesets.Replays.Legacy -{ - [Flags] - public enum ReplayButtonState - { - None = 0, - Left1 = 1, - Right1 = 2, - Left2 = 4, - Right2 = 8, - Smoke = 16 - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Rulesets.Replays.Legacy +{ + [Flags] + public enum ReplayButtonState + { + None = 0, + Left1 = 1, + Right1 = 2, + Left2 = 4, + Right2 = 8, + Smoke = 16 + } +} diff --git a/osu.Game/Rulesets/Replays/Replay.cs b/osu.Game/Rulesets/Replays/Replay.cs index a0ea2c5655..8fbe4c8194 100644 --- a/osu.Game/Rulesets/Replays/Replay.cs +++ b/osu.Game/Rulesets/Replays/Replay.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Users; - -namespace osu.Game.Rulesets.Replays -{ - public class Replay - { - public User User; - public List Frames = new List(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Users; + +namespace osu.Game.Rulesets.Replays +{ + public class Replay + { + public User User; + public List Frames = new List(); + } +} diff --git a/osu.Game/Rulesets/Replays/ReplayFrame.cs b/osu.Game/Rulesets/Replays/ReplayFrame.cs index 61a3646024..fedab8053f 100644 --- a/osu.Game/Rulesets/Replays/ReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/ReplayFrame.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Replays -{ - public class ReplayFrame - { - public double Time; - - public ReplayFrame() - { - } - - public ReplayFrame(double time) - { - Time = time; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Replays +{ + public class ReplayFrame + { + public double Time; + + public ReplayFrame() + { + } + + public ReplayFrame(double time) + { + Time = time; + } + } +} diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs index 892f5ca2fe..adf35ce078 100644 --- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Replays.Legacy; - -namespace osu.Game.Rulesets.Replays.Types -{ - /// - /// A type of which can be converted from a . - /// - public interface IConvertibleReplayFrame - { - /// - /// Populates this using values from a . - /// - /// The to extract values from. - /// The beatmap. - void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Replays.Legacy; + +namespace osu.Game.Rulesets.Replays.Types +{ + /// + /// A type of which can be converted from a . + /// + public interface IConvertibleReplayFrame + { + /// + /// Populates this using values from a . + /// + /// The to extract values from. + /// The beatmap. + void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap); + } +} diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index cba849a491..c2af4d566c 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -1,112 +1,112 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets -{ - public abstract class Ruleset - { - public readonly RulesetInfo RulesetInfo; - - public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; - - public IEnumerable GetAllMods() => Enum.GetValues(typeof(ModType)).Cast() - // Confine all mods of each mod type into a single IEnumerable - .SelectMany(GetModsFor) - // Filter out all null mods - .Where(mod => mod != null) - // Resolve MultiMods as their .Mods property - .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); - - public abstract IEnumerable GetModsFor(ModType type); - - public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay); - - protected Ruleset(RulesetInfo rulesetInfo = null) - { - RulesetInfo = rulesetInfo ?? createRulesetInfo(); - } - - /// - /// Attempt to create a hit renderer for a beatmap - /// - /// The beatmap to create the hit renderer for. - /// Whether the hit renderer should assume the beatmap is for the current ruleset. - /// Unable to successfully load the beatmap to be usable with this ruleset. - /// - public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset); - - public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null); - - public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null; - - public virtual HitObjectComposer CreateHitObjectComposer() => null; - - public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; - - public abstract string Description { get; } - - public virtual SettingsSubsection CreateSettings() => null; - - /// - /// Do not override this unless you are a legacy mode. - /// - public virtual int? LegacyID => null; - - /// - /// A unique short name to reference this ruleset in online requests. - /// - public abstract string ShortName { get; } - - /// - /// A list of available variant ids. - /// - public virtual IEnumerable AvailableVariants => new[] { 0 }; - - /// - /// Get a list of default keys for the specified variant. - /// - /// A variant. - /// A list of valid s. - public virtual IEnumerable GetDefaultKeyBindings(int variant = 0) => new KeyBinding[] { }; - - /// - /// Gets the name for a key binding variant. This is used for display in the settings overlay. - /// - /// The variant. - /// A descriptive name of the variant. - public virtual string GetVariantName(int variant) => string.Empty; - - /// - /// For rulesets which support legacy (osu-stable) replay conversion, this method will create an empty replay frame - /// for conversion use. - /// - /// An empty frame for the current ruleset, or null if unsupported. - public virtual IConvertibleReplayFrame CreateConvertibleReplayFrame() => null; - - /// - /// Create a ruleset info based on this ruleset. - /// - /// A filled . - private RulesetInfo createRulesetInfo() => new RulesetInfo - { - Name = Description, - ShortName = ShortName, - InstantiationInfo = GetType().AssemblyQualifiedName, - ID = LegacyID - }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets +{ + public abstract class Ruleset + { + public readonly RulesetInfo RulesetInfo; + + public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; + + public IEnumerable GetAllMods() => Enum.GetValues(typeof(ModType)).Cast() + // Confine all mods of each mod type into a single IEnumerable + .SelectMany(GetModsFor) + // Filter out all null mods + .Where(mod => mod != null) + // Resolve MultiMods as their .Mods property + .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); + + public abstract IEnumerable GetModsFor(ModType type); + + public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay); + + protected Ruleset(RulesetInfo rulesetInfo = null) + { + RulesetInfo = rulesetInfo ?? createRulesetInfo(); + } + + /// + /// Attempt to create a hit renderer for a beatmap + /// + /// The beatmap to create the hit renderer for. + /// Whether the hit renderer should assume the beatmap is for the current ruleset. + /// Unable to successfully load the beatmap to be usable with this ruleset. + /// + public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset); + + public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null); + + public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null; + + public virtual HitObjectComposer CreateHitObjectComposer() => null; + + public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; + + public abstract string Description { get; } + + public virtual SettingsSubsection CreateSettings() => null; + + /// + /// Do not override this unless you are a legacy mode. + /// + public virtual int? LegacyID => null; + + /// + /// A unique short name to reference this ruleset in online requests. + /// + public abstract string ShortName { get; } + + /// + /// A list of available variant ids. + /// + public virtual IEnumerable AvailableVariants => new[] { 0 }; + + /// + /// Get a list of default keys for the specified variant. + /// + /// A variant. + /// A list of valid s. + public virtual IEnumerable GetDefaultKeyBindings(int variant = 0) => new KeyBinding[] { }; + + /// + /// Gets the name for a key binding variant. This is used for display in the settings overlay. + /// + /// The variant. + /// A descriptive name of the variant. + public virtual string GetVariantName(int variant) => string.Empty; + + /// + /// For rulesets which support legacy (osu-stable) replay conversion, this method will create an empty replay frame + /// for conversion use. + /// + /// An empty frame for the current ruleset, or null if unsupported. + public virtual IConvertibleReplayFrame CreateConvertibleReplayFrame() => null; + + /// + /// Create a ruleset info based on this ruleset. + /// + /// A filled . + private RulesetInfo createRulesetInfo() => new RulesetInfo + { + Name = Description, + ShortName = ShortName, + InstantiationInfo = GetType().AssemblyQualifiedName, + ID = LegacyID + }; + } +} diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 43da3670b0..10463fd961 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.ComponentModel.DataAnnotations.Schema; -using Newtonsoft.Json; - -namespace osu.Game.Rulesets -{ - public class RulesetInfo : IEquatable - { - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - [JsonIgnore] - public int? ID { get; set; } - - public string Name { get; set; } - - public string ShortName { get; set; } - - public string InstantiationInfo { get; set; } - - [JsonIgnore] - public bool Available { get; set; } - - public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); - - public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; + +namespace osu.Game.Rulesets +{ + public class RulesetInfo : IEquatable + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [JsonIgnore] + public int? ID { get; set; } + + public string Name { get; set; } + + public string ShortName { get; set; } + + public string InstantiationInfo { get; set; } + + [JsonIgnore] + public bool Available { get; set; } + + public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); + + public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; + } +} diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index e621c3cf2b..67a9a59d4a 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -1,122 +1,122 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using osu.Game.Database; - -namespace osu.Game.Rulesets -{ - /// - /// Todo: All of this needs to be moved to a RulesetStore. - /// - public class RulesetStore : DatabaseBackedStore - { - private static readonly Dictionary loaded_assemblies = new Dictionary(); - - static RulesetStore() - { - AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; - - foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll")) - loadRulesetFromFile(file); - } - - public RulesetStore(IDatabaseContextFactory factory) - : base(factory) - { - AddMissingRulesets(); - } - - /// - /// Retrieve a ruleset using a known ID. - /// - /// The ruleset's internal ID. - /// A ruleset, if available, else null. - public RulesetInfo GetRuleset(int id) => AvailableRulesets.FirstOrDefault(r => r.ID == id); - - /// - /// Retrieve a ruleset using a known short name. - /// - /// The ruleset's short name. - /// A ruleset, if available, else null. - public RulesetInfo GetRuleset(string shortName) => AvailableRulesets.FirstOrDefault(r => r.ShortName == shortName); - - /// - /// All available rulesets. - /// - public IEnumerable AvailableRulesets; - - private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); - - private const string ruleset_library_prefix = "osu.Game.Rulesets"; - - protected void AddMissingRulesets() - { - using (var usage = ContextFactory.GetForWrite()) - { - var context = usage.Context; - - var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, (RulesetInfo)null)).ToList(); - - //add all legacy modes in correct order - foreach (var r in instances.Where(r => r.LegacyID != null).OrderBy(r => r.LegacyID)) - { - if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == r.RulesetInfo.ID) == null) - context.RulesetInfo.Add(r.RulesetInfo); - } - - context.SaveChanges(); - - //add any other modes - foreach (var r in instances.Where(r => r.LegacyID == null)) - if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) - context.RulesetInfo.Add(r.RulesetInfo); - - context.SaveChanges(); - - //perform a consistency check - foreach (var r in context.RulesetInfo) - { - try - { - var instance = r.CreateInstance(); - - r.Name = instance.Description; - r.ShortName = instance.ShortName; - - r.Available = true; - } - catch - { - r.Available = false; - } - } - - context.SaveChanges(); - - AvailableRulesets = context.RulesetInfo.Where(r => r.Available).ToList(); - } - } - - private static void loadRulesetFromFile(string file) - { - var filename = Path.GetFileNameWithoutExtension(file); - - if (loaded_assemblies.Values.Any(t => t.Namespace == filename)) - return; - - try - { - var assembly = Assembly.LoadFrom(file); - loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsSubclassOf(typeof(Ruleset))); - } - catch (Exception) - { - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using osu.Game.Database; + +namespace osu.Game.Rulesets +{ + /// + /// Todo: All of this needs to be moved to a RulesetStore. + /// + public class RulesetStore : DatabaseBackedStore + { + private static readonly Dictionary loaded_assemblies = new Dictionary(); + + static RulesetStore() + { + AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; + + foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll")) + loadRulesetFromFile(file); + } + + public RulesetStore(IDatabaseContextFactory factory) + : base(factory) + { + AddMissingRulesets(); + } + + /// + /// Retrieve a ruleset using a known ID. + /// + /// The ruleset's internal ID. + /// A ruleset, if available, else null. + public RulesetInfo GetRuleset(int id) => AvailableRulesets.FirstOrDefault(r => r.ID == id); + + /// + /// Retrieve a ruleset using a known short name. + /// + /// The ruleset's short name. + /// A ruleset, if available, else null. + public RulesetInfo GetRuleset(string shortName) => AvailableRulesets.FirstOrDefault(r => r.ShortName == shortName); + + /// + /// All available rulesets. + /// + public IEnumerable AvailableRulesets; + + private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); + + private const string ruleset_library_prefix = "osu.Game.Rulesets"; + + protected void AddMissingRulesets() + { + using (var usage = ContextFactory.GetForWrite()) + { + var context = usage.Context; + + var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, (RulesetInfo)null)).ToList(); + + //add all legacy modes in correct order + foreach (var r in instances.Where(r => r.LegacyID != null).OrderBy(r => r.LegacyID)) + { + if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == r.RulesetInfo.ID) == null) + context.RulesetInfo.Add(r.RulesetInfo); + } + + context.SaveChanges(); + + //add any other modes + foreach (var r in instances.Where(r => r.LegacyID == null)) + if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) + context.RulesetInfo.Add(r.RulesetInfo); + + context.SaveChanges(); + + //perform a consistency check + foreach (var r in context.RulesetInfo) + { + try + { + var instance = r.CreateInstance(); + + r.Name = instance.Description; + r.ShortName = instance.ShortName; + + r.Available = true; + } + catch + { + r.Available = false; + } + } + + context.SaveChanges(); + + AvailableRulesets = context.RulesetInfo.Where(r => r.Available).ToList(); + } + } + + private static void loadRulesetFromFile(string file) + { + var filename = Path.GetFileNameWithoutExtension(file); + + if (loaded_assemblies.Values.Any(t => t.Namespace == filename)) + return; + + try + { + var assembly = Assembly.LoadFrom(file); + loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsSubclassOf(typeof(Ruleset))); + } + catch (Exception) + { + } + } + } +} diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 05f3b82810..98874a4890 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Rulesets.Scoring -{ - public enum HitResult - { - /// - /// Indicates that the object has not been judged yet. - /// - [Description(@"")] - None, - - /// - /// Indicates that the object has been judged as a miss. - /// - [Description(@"Miss")] - Miss, - - [Description(@"Meh")] - Meh, - - /// - /// Optional judgement. - /// - [Description(@"OK")] - Ok, - - [Description(@"Good")] - Good, - - [Description(@"Great")] - Great, - - /// - /// Optional judgement. - /// - [Description(@"Perfect")] - Perfect, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Rulesets.Scoring +{ + public enum HitResult + { + /// + /// Indicates that the object has not been judged yet. + /// + [Description(@"")] + None, + + /// + /// Indicates that the object has been judged as a miss. + /// + [Description(@"Miss")] + Miss, + + [Description(@"Meh")] + Meh, + + /// + /// Optional judgement. + /// + [Description(@"OK")] + Ok, + + [Description(@"Good")] + Good, + + [Description(@"Great")] + Great, + + /// + /// Optional judgement. + /// + [Description(@"Perfect")] + Perfect, + } +} diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index 9ebb62a368..5ee009ba98 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -1,152 +1,152 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using osu.Game.Beatmaps; -using osu.Game.IO.Legacy; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Replays.Legacy; -using osu.Game.Users; -using SharpCompress.Compressors.LZMA; - -namespace osu.Game.Rulesets.Scoring.Legacy -{ - public class LegacyScoreParser - { - private readonly RulesetStore rulesets; - private readonly BeatmapManager beatmaps; - - public LegacyScoreParser(RulesetStore rulesets, BeatmapManager beatmaps) - { - this.rulesets = rulesets; - this.beatmaps = beatmaps; - } - - private Beatmap currentBeatmap; - private Ruleset currentRuleset; - - public Score Parse(Stream stream) - { - Score score; - - using (SerializationReader sr = new SerializationReader(stream)) - { - score = new Score { Ruleset = rulesets.GetRuleset(sr.ReadByte()) }; - currentRuleset = score.Ruleset.CreateInstance(); - - /* score.Pass = true;*/ - var version = sr.ReadInt32(); - - /* score.FileChecksum = */ - var beatmapHash = sr.ReadString(); - score.Beatmap = beatmaps.QueryBeatmap(b => b.MD5Hash == beatmapHash); - currentBeatmap = beatmaps.GetWorkingBeatmap(score.Beatmap).Beatmap; - - /* score.PlayerName = */ - score.User = new User { Username = sr.ReadString() }; - /* var localScoreChecksum = */ - sr.ReadString(); - /* score.Count300 = */ - sr.ReadUInt16(); - /* score.Count100 = */ - sr.ReadUInt16(); - /* score.Count50 = */ - sr.ReadUInt16(); - /* score.CountGeki = */ - sr.ReadUInt16(); - /* score.CountKatu = */ - sr.ReadUInt16(); - /* score.CountMiss = */ - sr.ReadUInt16(); - score.TotalScore = sr.ReadInt32(); - score.MaxCombo = sr.ReadUInt16(); - /* score.Perfect = */ - sr.ReadBoolean(); - /* score.EnabledMods = (Mods)*/ - sr.ReadInt32(); - /* score.HpGraphString = */ - sr.ReadString(); - /* score.Date = */ - sr.ReadDateTime(); - - var compressedReplay = sr.ReadByteArray(); - - if (version >= 20140721) - /*OnlineId =*/ - sr.ReadInt64(); - else if (version >= 20121008) - /*OnlineId =*/ - sr.ReadInt32(); - - using (var replayInStream = new MemoryStream(compressedReplay)) - { - byte[] properties = new byte[5]; - if (replayInStream.Read(properties, 0, 5) != 5) - throw new IOException("input .lzma is too short"); - long outSize = 0; - for (int i = 0; i < 8; i++) - { - int v = replayInStream.ReadByte(); - if (v < 0) - throw new IOException("Can't Read 1"); - outSize |= (long)(byte)v << (8 * i); - } - - long compressedSize = replayInStream.Length - replayInStream.Position; - - using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize)) - using (var reader = new StreamReader(lzma)) - { - score.Replay = new Replay { User = score.User }; - readLegacyReplay(score.Replay, reader); - } - } - } - - return score; - } - - private void readLegacyReplay(Replay replay, StreamReader reader) - { - float lastTime = 0; - - foreach (var l in reader.ReadToEnd().Split(',')) - { - var split = l.Split('|'); - - if (split.Length < 4) - continue; - - if (split[0] == "-12345") - { - // Todo: The seed is provided in split[3], which we'll need to use at some point - continue; - } - - var diff = float.Parse(split[0]); - lastTime += diff; - - // Todo: At some point we probably want to rewind and play back the negative-time frames - // but for now we'll achieve equal playback to stable by skipping negative frames - if (diff < 0) - continue; - - replay.Frames.Add(convertFrame(new LegacyReplayFrame(lastTime, float.Parse(split[1]), float.Parse(split[2]), (ReplayButtonState)int.Parse(split[3])))); - } - } - - private ReplayFrame convertFrame(LegacyReplayFrame legacyFrame) - { - var convertible = currentRuleset.CreateConvertibleReplayFrame(); - if (convertible == null) - throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}"); - convertible.ConvertFrom(legacyFrame, currentBeatmap); - - var frame = (ReplayFrame)convertible; - frame.Time = legacyFrame.Time; - - return frame; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Game.Beatmaps; +using osu.Game.IO.Legacy; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Users; +using SharpCompress.Compressors.LZMA; + +namespace osu.Game.Rulesets.Scoring.Legacy +{ + public class LegacyScoreParser + { + private readonly RulesetStore rulesets; + private readonly BeatmapManager beatmaps; + + public LegacyScoreParser(RulesetStore rulesets, BeatmapManager beatmaps) + { + this.rulesets = rulesets; + this.beatmaps = beatmaps; + } + + private Beatmap currentBeatmap; + private Ruleset currentRuleset; + + public Score Parse(Stream stream) + { + Score score; + + using (SerializationReader sr = new SerializationReader(stream)) + { + score = new Score { Ruleset = rulesets.GetRuleset(sr.ReadByte()) }; + currentRuleset = score.Ruleset.CreateInstance(); + + /* score.Pass = true;*/ + var version = sr.ReadInt32(); + + /* score.FileChecksum = */ + var beatmapHash = sr.ReadString(); + score.Beatmap = beatmaps.QueryBeatmap(b => b.MD5Hash == beatmapHash); + currentBeatmap = beatmaps.GetWorkingBeatmap(score.Beatmap).Beatmap; + + /* score.PlayerName = */ + score.User = new User { Username = sr.ReadString() }; + /* var localScoreChecksum = */ + sr.ReadString(); + /* score.Count300 = */ + sr.ReadUInt16(); + /* score.Count100 = */ + sr.ReadUInt16(); + /* score.Count50 = */ + sr.ReadUInt16(); + /* score.CountGeki = */ + sr.ReadUInt16(); + /* score.CountKatu = */ + sr.ReadUInt16(); + /* score.CountMiss = */ + sr.ReadUInt16(); + score.TotalScore = sr.ReadInt32(); + score.MaxCombo = sr.ReadUInt16(); + /* score.Perfect = */ + sr.ReadBoolean(); + /* score.EnabledMods = (Mods)*/ + sr.ReadInt32(); + /* score.HpGraphString = */ + sr.ReadString(); + /* score.Date = */ + sr.ReadDateTime(); + + var compressedReplay = sr.ReadByteArray(); + + if (version >= 20140721) + /*OnlineId =*/ + sr.ReadInt64(); + else if (version >= 20121008) + /*OnlineId =*/ + sr.ReadInt32(); + + using (var replayInStream = new MemoryStream(compressedReplay)) + { + byte[] properties = new byte[5]; + if (replayInStream.Read(properties, 0, 5) != 5) + throw new IOException("input .lzma is too short"); + long outSize = 0; + for (int i = 0; i < 8; i++) + { + int v = replayInStream.ReadByte(); + if (v < 0) + throw new IOException("Can't Read 1"); + outSize |= (long)(byte)v << (8 * i); + } + + long compressedSize = replayInStream.Length - replayInStream.Position; + + using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize)) + using (var reader = new StreamReader(lzma)) + { + score.Replay = new Replay { User = score.User }; + readLegacyReplay(score.Replay, reader); + } + } + } + + return score; + } + + private void readLegacyReplay(Replay replay, StreamReader reader) + { + float lastTime = 0; + + foreach (var l in reader.ReadToEnd().Split(',')) + { + var split = l.Split('|'); + + if (split.Length < 4) + continue; + + if (split[0] == "-12345") + { + // Todo: The seed is provided in split[3], which we'll need to use at some point + continue; + } + + var diff = float.Parse(split[0]); + lastTime += diff; + + // Todo: At some point we probably want to rewind and play back the negative-time frames + // but for now we'll achieve equal playback to stable by skipping negative frames + if (diff < 0) + continue; + + replay.Frames.Add(convertFrame(new LegacyReplayFrame(lastTime, float.Parse(split[1]), float.Parse(split[2]), (ReplayButtonState)int.Parse(split[3])))); + } + } + + private ReplayFrame convertFrame(LegacyReplayFrame legacyFrame) + { + var convertible = currentRuleset.CreateConvertibleReplayFrame(); + if (convertible == null) + throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}"); + convertible.ConvertFrom(legacyFrame, currentBeatmap); + + var frame = (ReplayFrame)convertible; + frame.Time = legacyFrame.Time; + + return frame; + } + } +} diff --git a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs index c047a421fd..0f115fff6b 100644 --- a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs @@ -1,43 +1,43 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Scoring -{ - public abstract class PerformanceCalculator - { - public abstract double Calculate(Dictionary categoryDifficulty = null); - } - - public abstract class PerformanceCalculator : PerformanceCalculator - where TObject : HitObject - { - private readonly Dictionary attributes = new Dictionary(); - protected IDictionary Attributes => attributes; - - protected readonly Beatmap Beatmap; - protected readonly Score Score; - - protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) - { - Score = score; - - var converter = CreateBeatmapConverter(); - - foreach (var mod in score.Mods.OfType>()) - mod.ApplyToBeatmapConverter(converter); - - Beatmap = converter.Convert(beatmap); - - var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods); - diffCalc.Calculate(attributes); - } - - protected abstract BeatmapConverter CreateBeatmapConverter(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Scoring +{ + public abstract class PerformanceCalculator + { + public abstract double Calculate(Dictionary categoryDifficulty = null); + } + + public abstract class PerformanceCalculator : PerformanceCalculator + where TObject : HitObject + { + private readonly Dictionary attributes = new Dictionary(); + protected IDictionary Attributes => attributes; + + protected readonly Beatmap Beatmap; + protected readonly Score Score; + + protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) + { + Score = score; + + var converter = CreateBeatmapConverter(); + + foreach (var mod in score.Mods.OfType>()) + mod.ApplyToBeatmapConverter(converter); + + Beatmap = converter.Convert(beatmap); + + var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods); + diffCalc.Calculate(attributes); + } + + protected abstract BeatmapConverter CreateBeatmapConverter(); + } +} diff --git a/osu.Game/Rulesets/Scoring/Score.cs b/osu.Game/Rulesets/Scoring/Score.cs index ed79333a16..dfe7ff0195 100644 --- a/osu.Game/Rulesets/Scoring/Score.cs +++ b/osu.Game/Rulesets/Scoring/Score.cs @@ -1,45 +1,45 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Users; -using osu.Game.Rulesets.Replays; - -namespace osu.Game.Rulesets.Scoring -{ - public class Score - { - public ScoreRank Rank { get; set; } - - public double TotalScore { get; set; } - - public double Accuracy { get; set; } - - public double Health { get; set; } = 1; - - public double? PP { get; set; } - - public int MaxCombo { get; set; } - - public int Combo { get; set; } - - public RulesetInfo Ruleset { get; set; } - - public Mod[] Mods { get; set; } = { }; - - public User User; - - public Replay Replay; - - public BeatmapInfo Beatmap; - - public long OnlineScoreID; - - public DateTimeOffset Date; - - public Dictionary Statistics = new Dictionary(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Users; +using osu.Game.Rulesets.Replays; + +namespace osu.Game.Rulesets.Scoring +{ + public class Score + { + public ScoreRank Rank { get; set; } + + public double TotalScore { get; set; } + + public double Accuracy { get; set; } + + public double Health { get; set; } = 1; + + public double? PP { get; set; } + + public int MaxCombo { get; set; } + + public int Combo { get; set; } + + public RulesetInfo Ruleset { get; set; } + + public Mod[] Mods { get; set; } = { }; + + public User User; + + public Replay Replay; + + public BeatmapInfo Beatmap; + + public long OnlineScoreID; + + public DateTimeOffset Date; + + public Dictionary Statistics = new Dictionary(); + } +} diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index e8271059ca..345930ed04 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -1,327 +1,327 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Diagnostics; -using osu.Framework.Configuration; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Scoring -{ - public abstract class ScoreProcessor - { - /// - /// Invoked when the is in a failed state. - /// This may occur regardless of whether an event is invoked. - /// Return true if the fail was permitted. - /// - public event Func Failed; - - /// - /// Invoked when all s have been judged. - /// - public event Action AllJudged; - - /// - /// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the . - /// - public event Action NewJudgement; - - /// - /// Additional conditions on top of that cause a failing state. - /// - public event Func FailConditions; - - /// - /// The current total score. - /// - public readonly BindableDouble TotalScore = new BindableDouble { MinValue = 0 }; - - /// - /// The current accuracy. - /// - public readonly BindableDouble Accuracy = new BindableDouble(1) { MinValue = 0, MaxValue = 1 }; - - /// - /// The current health. - /// - public readonly BindableDouble Health = new BindableDouble { MinValue = 0, MaxValue = 1 }; - - /// - /// The current combo. - /// - public readonly BindableInt Combo = new BindableInt(); - - /// - /// The current rank. - /// - public readonly Bindable Rank = new Bindable(ScoreRank.X); - - /// - /// THe highest combo achieved by this score. - /// - public readonly BindableInt HighestCombo = new BindableInt(); - - /// - /// Whether all s have been processed. - /// - protected virtual bool HasCompleted => false; - - /// - /// Whether this ScoreProcessor has already triggered the failed state. - /// - public virtual bool HasFailed { get; private set; } - - /// - /// The default conditions for failing. - /// - protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue; - - protected ScoreProcessor() - { - Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; - Accuracy.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); }; - } - - private ScoreRank rankFrom(double acc) - { - if (acc == 1) - return ScoreRank.X; - if (acc > 0.95) - return ScoreRank.S; - if (acc > 0.9) - return ScoreRank.A; - if (acc > 0.8) - return ScoreRank.B; - if (acc > 0.7) - return ScoreRank.C; - return ScoreRank.D; - } - - /// - /// Resets this ScoreProcessor to a default state. - /// - /// Whether to store the current state of the for future use. - protected virtual void Reset(bool storeResults) - { - TotalScore.Value = 0; - Accuracy.Value = 1; - Health.Value = 1; - Combo.Value = 0; - Rank.Value = ScoreRank.X; - HighestCombo.Value = 0; - - HasFailed = false; - } - - /// - /// Checks if the score is in a failed state and notifies subscribers. - /// - /// This can only ever notify subscribers once. - /// - /// - protected void UpdateFailed() - { - if (HasFailed) - return; - - if (!DefaultFailCondition && FailConditions?.Invoke(this) != true) - return; - - if (Failed?.Invoke() != false) - HasFailed = true; - } - - /// - /// Notifies subscribers of that a new judgement has occurred. - /// - /// The judgement to notify subscribers of. - protected void NotifyNewJudgement(Judgement judgement) - { - NewJudgement?.Invoke(judgement); - - if (HasCompleted) - AllJudged?.Invoke(); - } - - /// - /// Retrieve a score populated with data for the current play this processor is responsible for. - /// - public virtual void PopulateScore(Score score) - { - score.TotalScore = TotalScore; - score.Combo = Combo; - score.MaxCombo = HighestCombo; - score.Accuracy = Accuracy; - score.Rank = Rank; - score.Date = DateTimeOffset.Now; - score.Health = Health; - } - } - - public class ScoreProcessor : ScoreProcessor - where TObject : HitObject - { - private const double base_portion = 0.3; - private const double combo_portion = 0.7; - private const double max_score = 1000000; - - public readonly Bindable Mode = new Bindable(); - - protected sealed override bool HasCompleted => JudgedHits == MaxHits; - - protected int MaxHits { get; private set; } - protected int JudgedHits { get; private set; } - - private double maxHighestCombo; - - private double maxBaseScore; - private double rollingMaxBaseScore; - private double baseScore; - private double bonusScore; - - protected ScoreProcessor() - { - } - - public ScoreProcessor(RulesetContainer rulesetContainer) - { - Debug.Assert(base_portion + combo_portion == 1.0); - - rulesetContainer.OnJudgement += AddJudgement; - rulesetContainer.OnJudgementRemoved += RemoveJudgement; - - SimulateAutoplay(rulesetContainer.Beatmap); - Reset(true); - - if (maxBaseScore == 0 || maxHighestCombo == 0) - { - Mode.Value = ScoringMode.Exponential; - Mode.Disabled = true; - } - } - - /// - /// Simulates an autoplay of s that will be judged by this - /// by adding s for each in the . - /// - /// This is required for to work, otherwise will be used. - /// - /// - /// The containing the s that will be judged by this . - protected virtual void SimulateAutoplay(Beatmap beatmap) { } - - /// - /// Adds a judgement to this ScoreProcessor. - /// - /// The judgement to add. - protected void AddJudgement(Judgement judgement) - { - OnNewJudgement(judgement); - updateScore(); - - UpdateFailed(); - NotifyNewJudgement(judgement); - } - - protected void RemoveJudgement(Judgement judgement) - { - OnJudgementRemoved(judgement); - updateScore(); - } - - /// - /// Applies a judgement. - /// - /// The judgement to apply/ - protected virtual void OnNewJudgement(Judgement judgement) - { - judgement.ComboAtJudgement = Combo; - judgement.HighestComboAtJudgement = HighestCombo; - - if (judgement.AffectsCombo) - { - switch (judgement.Result) - { - case HitResult.None: - break; - case HitResult.Miss: - Combo.Value = 0; - break; - default: - Combo.Value++; - break; - } - - baseScore += judgement.NumericResult; - rollingMaxBaseScore += judgement.MaxNumericResult; - - JudgedHits++; - } - else if (judgement.IsHit) - bonusScore += judgement.NumericResult; - } - - /// - /// Removes a judgement. This should reverse everything in . - /// - /// The judgement to remove. - protected virtual void OnJudgementRemoved(Judgement judgement) - { - Combo.Value = judgement.ComboAtJudgement; - HighestCombo.Value = judgement.HighestComboAtJudgement; - - if (judgement.AffectsCombo) - { - baseScore -= judgement.NumericResult; - rollingMaxBaseScore -= judgement.MaxNumericResult; - - JudgedHits--; - } - else if (judgement.IsHit) - bonusScore -= judgement.NumericResult; - } - - private void updateScore() - { - if (rollingMaxBaseScore != 0) - Accuracy.Value = baseScore / rollingMaxBaseScore; - - switch (Mode.Value) - { - case ScoringMode.Standardised: - TotalScore.Value = max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore; - break; - case ScoringMode.Exponential: - TotalScore.Value = (baseScore + bonusScore) * Math.Log(HighestCombo + 1, 2); - break; - } - } - - protected override void Reset(bool storeResults) - { - if (storeResults) - { - MaxHits = JudgedHits; - maxHighestCombo = HighestCombo; - maxBaseScore = baseScore; - } - - base.Reset(storeResults); - - JudgedHits = 0; - baseScore = 0; - rollingMaxBaseScore = 0; - bonusScore = 0; - } - } - - public enum ScoringMode - { - Standardised, - Exponential - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Diagnostics; +using osu.Framework.Configuration; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Scoring +{ + public abstract class ScoreProcessor + { + /// + /// Invoked when the is in a failed state. + /// This may occur regardless of whether an event is invoked. + /// Return true if the fail was permitted. + /// + public event Func Failed; + + /// + /// Invoked when all s have been judged. + /// + public event Action AllJudged; + + /// + /// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the . + /// + public event Action NewJudgement; + + /// + /// Additional conditions on top of that cause a failing state. + /// + public event Func FailConditions; + + /// + /// The current total score. + /// + public readonly BindableDouble TotalScore = new BindableDouble { MinValue = 0 }; + + /// + /// The current accuracy. + /// + public readonly BindableDouble Accuracy = new BindableDouble(1) { MinValue = 0, MaxValue = 1 }; + + /// + /// The current health. + /// + public readonly BindableDouble Health = new BindableDouble { MinValue = 0, MaxValue = 1 }; + + /// + /// The current combo. + /// + public readonly BindableInt Combo = new BindableInt(); + + /// + /// The current rank. + /// + public readonly Bindable Rank = new Bindable(ScoreRank.X); + + /// + /// THe highest combo achieved by this score. + /// + public readonly BindableInt HighestCombo = new BindableInt(); + + /// + /// Whether all s have been processed. + /// + protected virtual bool HasCompleted => false; + + /// + /// Whether this ScoreProcessor has already triggered the failed state. + /// + public virtual bool HasFailed { get; private set; } + + /// + /// The default conditions for failing. + /// + protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue; + + protected ScoreProcessor() + { + Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; + Accuracy.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); }; + } + + private ScoreRank rankFrom(double acc) + { + if (acc == 1) + return ScoreRank.X; + if (acc > 0.95) + return ScoreRank.S; + if (acc > 0.9) + return ScoreRank.A; + if (acc > 0.8) + return ScoreRank.B; + if (acc > 0.7) + return ScoreRank.C; + return ScoreRank.D; + } + + /// + /// Resets this ScoreProcessor to a default state. + /// + /// Whether to store the current state of the for future use. + protected virtual void Reset(bool storeResults) + { + TotalScore.Value = 0; + Accuracy.Value = 1; + Health.Value = 1; + Combo.Value = 0; + Rank.Value = ScoreRank.X; + HighestCombo.Value = 0; + + HasFailed = false; + } + + /// + /// Checks if the score is in a failed state and notifies subscribers. + /// + /// This can only ever notify subscribers once. + /// + /// + protected void UpdateFailed() + { + if (HasFailed) + return; + + if (!DefaultFailCondition && FailConditions?.Invoke(this) != true) + return; + + if (Failed?.Invoke() != false) + HasFailed = true; + } + + /// + /// Notifies subscribers of that a new judgement has occurred. + /// + /// The judgement to notify subscribers of. + protected void NotifyNewJudgement(Judgement judgement) + { + NewJudgement?.Invoke(judgement); + + if (HasCompleted) + AllJudged?.Invoke(); + } + + /// + /// Retrieve a score populated with data for the current play this processor is responsible for. + /// + public virtual void PopulateScore(Score score) + { + score.TotalScore = TotalScore; + score.Combo = Combo; + score.MaxCombo = HighestCombo; + score.Accuracy = Accuracy; + score.Rank = Rank; + score.Date = DateTimeOffset.Now; + score.Health = Health; + } + } + + public class ScoreProcessor : ScoreProcessor + where TObject : HitObject + { + private const double base_portion = 0.3; + private const double combo_portion = 0.7; + private const double max_score = 1000000; + + public readonly Bindable Mode = new Bindable(); + + protected sealed override bool HasCompleted => JudgedHits == MaxHits; + + protected int MaxHits { get; private set; } + protected int JudgedHits { get; private set; } + + private double maxHighestCombo; + + private double maxBaseScore; + private double rollingMaxBaseScore; + private double baseScore; + private double bonusScore; + + protected ScoreProcessor() + { + } + + public ScoreProcessor(RulesetContainer rulesetContainer) + { + Debug.Assert(base_portion + combo_portion == 1.0); + + rulesetContainer.OnJudgement += AddJudgement; + rulesetContainer.OnJudgementRemoved += RemoveJudgement; + + SimulateAutoplay(rulesetContainer.Beatmap); + Reset(true); + + if (maxBaseScore == 0 || maxHighestCombo == 0) + { + Mode.Value = ScoringMode.Exponential; + Mode.Disabled = true; + } + } + + /// + /// Simulates an autoplay of s that will be judged by this + /// by adding s for each in the . + /// + /// This is required for to work, otherwise will be used. + /// + /// + /// The containing the s that will be judged by this . + protected virtual void SimulateAutoplay(Beatmap beatmap) { } + + /// + /// Adds a judgement to this ScoreProcessor. + /// + /// The judgement to add. + protected void AddJudgement(Judgement judgement) + { + OnNewJudgement(judgement); + updateScore(); + + UpdateFailed(); + NotifyNewJudgement(judgement); + } + + protected void RemoveJudgement(Judgement judgement) + { + OnJudgementRemoved(judgement); + updateScore(); + } + + /// + /// Applies a judgement. + /// + /// The judgement to apply/ + protected virtual void OnNewJudgement(Judgement judgement) + { + judgement.ComboAtJudgement = Combo; + judgement.HighestComboAtJudgement = HighestCombo; + + if (judgement.AffectsCombo) + { + switch (judgement.Result) + { + case HitResult.None: + break; + case HitResult.Miss: + Combo.Value = 0; + break; + default: + Combo.Value++; + break; + } + + baseScore += judgement.NumericResult; + rollingMaxBaseScore += judgement.MaxNumericResult; + + JudgedHits++; + } + else if (judgement.IsHit) + bonusScore += judgement.NumericResult; + } + + /// + /// Removes a judgement. This should reverse everything in . + /// + /// The judgement to remove. + protected virtual void OnJudgementRemoved(Judgement judgement) + { + Combo.Value = judgement.ComboAtJudgement; + HighestCombo.Value = judgement.HighestComboAtJudgement; + + if (judgement.AffectsCombo) + { + baseScore -= judgement.NumericResult; + rollingMaxBaseScore -= judgement.MaxNumericResult; + + JudgedHits--; + } + else if (judgement.IsHit) + bonusScore -= judgement.NumericResult; + } + + private void updateScore() + { + if (rollingMaxBaseScore != 0) + Accuracy.Value = baseScore / rollingMaxBaseScore; + + switch (Mode.Value) + { + case ScoringMode.Standardised: + TotalScore.Value = max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore; + break; + case ScoringMode.Exponential: + TotalScore.Value = (baseScore + bonusScore) * Math.Log(HighestCombo + 1, 2); + break; + } + } + + protected override void Reset(bool storeResults) + { + if (storeResults) + { + MaxHits = JudgedHits; + maxHighestCombo = HighestCombo; + maxBaseScore = baseScore; + } + + base.Reset(storeResults); + + JudgedHits = 0; + baseScore = 0; + rollingMaxBaseScore = 0; + bonusScore = 0; + } + } + + public enum ScoringMode + { + Standardised, + Exponential + } +} diff --git a/osu.Game/Rulesets/Scoring/ScoreRank.cs b/osu.Game/Rulesets/Scoring/ScoreRank.cs index 5df52ddf95..d4113bb08a 100644 --- a/osu.Game/Rulesets/Scoring/ScoreRank.cs +++ b/osu.Game/Rulesets/Scoring/ScoreRank.cs @@ -1,29 +1,29 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Rulesets.Scoring -{ - public enum ScoreRank - { - [Description(@"F")] - F, - [Description(@"F")] - D, - [Description(@"C")] - C, - [Description(@"B")] - B, - [Description(@"A")] - A, - [Description(@"S")] - S, - [Description(@"SPlus")] - SH, - [Description(@"SS")] - X, - [Description(@"SSPlus")] - XH, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Rulesets.Scoring +{ + public enum ScoreRank + { + [Description(@"F")] + F, + [Description(@"F")] + D, + [Description(@"C")] + C, + [Description(@"B")] + B, + [Description(@"A")] + A, + [Description(@"S")] + S, + [Description(@"SPlus")] + SH, + [Description(@"SS")] + X, + [Description(@"SSPlus")] + XH, + } +} diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index cb2b76cdcf..957fd037e0 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -1,56 +1,56 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using osu.Framework.Platform; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.IPC; -using osu.Game.Rulesets.Scoring.Legacy; - -namespace osu.Game.Rulesets.Scoring -{ - public class ScoreStore : DatabaseBackedStore, ICanAcceptFiles - { - private readonly Storage storage; - - private readonly BeatmapManager beatmaps; - private readonly RulesetStore rulesets; - - private const string replay_folder = @"replays"; - - public event Action ScoreImported; - - // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) - private ScoreIPCChannel ipc; - - public ScoreStore(Storage storage, DatabaseContextFactory factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory) - { - this.storage = storage; - this.beatmaps = beatmaps; - this.rulesets = rulesets; - - if (importHost != null) - ipc = new ScoreIPCChannel(importHost, this); - } - - public string[] HandledExtensions => new[] { ".osr" }; - - public void Import(params string[] paths) - { - foreach (var path in paths) - { - var score = ReadReplayFile(path); - if (score != null) - ScoreImported?.Invoke(score); - } - } - - public Score ReadReplayFile(string replayFilename) - { - using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) - return new LegacyScoreParser(rulesets, beatmaps).Parse(s); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.IPC; +using osu.Game.Rulesets.Scoring.Legacy; + +namespace osu.Game.Rulesets.Scoring +{ + public class ScoreStore : DatabaseBackedStore, ICanAcceptFiles + { + private readonly Storage storage; + + private readonly BeatmapManager beatmaps; + private readonly RulesetStore rulesets; + + private const string replay_folder = @"replays"; + + public event Action ScoreImported; + + // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) + private ScoreIPCChannel ipc; + + public ScoreStore(Storage storage, DatabaseContextFactory factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory) + { + this.storage = storage; + this.beatmaps = beatmaps; + this.rulesets = rulesets; + + if (importHost != null) + ipc = new ScoreIPCChannel(importHost, this); + } + + public string[] HandledExtensions => new[] { ".osr" }; + + public void Import(params string[] paths) + { + foreach (var path in paths) + { + var score = ReadReplayFile(path); + if (score != null) + ScoreImported?.Invoke(score); + } + } + + public Score ReadReplayFile(string replayFilename) + { + using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) + return new LegacyScoreParser(rulesets, beatmaps).Parse(s); + } + } +} diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs index 450b5da7b8..a5bd6bfde7 100644 --- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs +++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs @@ -1,66 +1,66 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.IO.Serialization; - -namespace osu.Game.Rulesets.Timing -{ - /// - /// A control point which adds an aggregated multiplier based on the provided 's BeatLength and 's SpeedMultiplier. - /// - public class MultiplierControlPoint : IJsonSerializable, IComparable - { - /// - /// The time in milliseconds at which this starts. - /// - public double StartTime; - - /// - /// The multiplier which this provides. - /// - public double Multiplier => 1000 / TimingPoint.BeatLength * DifficultyPoint.SpeedMultiplier; - - /// - /// The that provides the timing information for this . - /// - public TimingControlPoint TimingPoint = new TimingControlPoint(); - - /// - /// The that provides additional difficulty information for this . - /// - public DifficultyControlPoint DifficultyPoint = new DifficultyControlPoint(); - - /// - /// Creates a . This is required for JSON serialization - /// - public MultiplierControlPoint() - { - } - - /// - /// Creates a . - /// - /// The start time of this . - public MultiplierControlPoint(double startTime) - { - StartTime = startTime; - } - - /// - /// Creates a by copying another . - /// - /// The start time of this . - /// The to copy. - public MultiplierControlPoint(double startTime, MultiplierControlPoint other) - : this(startTime) - { - TimingPoint = other.TimingPoint; - DifficultyPoint = other.DifficultyPoint; - } - - // ReSharper disable once ImpureMethodCallOnReadonlyValueField - public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO.Serialization; + +namespace osu.Game.Rulesets.Timing +{ + /// + /// A control point which adds an aggregated multiplier based on the provided 's BeatLength and 's SpeedMultiplier. + /// + public class MultiplierControlPoint : IJsonSerializable, IComparable + { + /// + /// The time in milliseconds at which this starts. + /// + public double StartTime; + + /// + /// The multiplier which this provides. + /// + public double Multiplier => 1000 / TimingPoint.BeatLength * DifficultyPoint.SpeedMultiplier; + + /// + /// The that provides the timing information for this . + /// + public TimingControlPoint TimingPoint = new TimingControlPoint(); + + /// + /// The that provides additional difficulty information for this . + /// + public DifficultyControlPoint DifficultyPoint = new DifficultyControlPoint(); + + /// + /// Creates a . This is required for JSON serialization + /// + public MultiplierControlPoint() + { + } + + /// + /// Creates a . + /// + /// The start time of this . + public MultiplierControlPoint(double startTime) + { + StartTime = startTime; + } + + /// + /// Creates a by copying another . + /// + /// The start time of this . + /// The to copy. + public MultiplierControlPoint(double startTime, MultiplierControlPoint other) + : this(startTime) + { + TimingPoint = other.TimingPoint; + DifficultyPoint = other.DifficultyPoint; + } + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime); + } +} diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index ecb10dfba2..1b6841c9bd 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -1,30 +1,30 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.UI -{ - public class HitObjectContainer : CompositeDrawable - { - public virtual IEnumerable Objects => InternalChildren.Cast(); - public virtual IEnumerable AliveObjects => AliveInternalChildren.Cast(); - - public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject); - public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject); - - protected override int Compare(Drawable x, Drawable y) - { - if (!(x is DrawableHitObject xObj) || !(y is DrawableHitObject yObj)) - return base.Compare(x, y); - - // Put earlier hitobjects towards the end of the list, so they handle input first - int i = yObj.HitObject.StartTime.CompareTo(xObj.HitObject.StartTime); - return i == 0 ? CompareReverseChildID(x, y) : i; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.UI +{ + public class HitObjectContainer : CompositeDrawable + { + public virtual IEnumerable Objects => InternalChildren.Cast(); + public virtual IEnumerable AliveObjects => AliveInternalChildren.Cast(); + + public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject); + public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject); + + protected override int Compare(Drawable x, Drawable y) + { + if (!(x is DrawableHitObject xObj) || !(y is DrawableHitObject yObj)) + return base.Compare(x, y); + + // Put earlier hitobjects towards the end of the list, so they handle input first + int i = yObj.HitObject.StartTime.CompareTo(xObj.HitObject.StartTime); + return i == 0 ? CompareReverseChildID(x, y) : i; + } + } +} diff --git a/osu.Game/Rulesets/UI/JudgementContainer.cs b/osu.Game/Rulesets/UI/JudgementContainer.cs index 1291b9fc98..e0d62aba30 100644 --- a/osu.Game/Rulesets/UI/JudgementContainer.cs +++ b/osu.Game/Rulesets/UI/JudgementContainer.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Judgements; - -namespace osu.Game.Rulesets.UI -{ - public class JudgementContainer : Container - where T : DrawableJudgement - { - public override void Add(T judgement) - { - if (judgement == null) throw new ArgumentNullException(nameof(judgement)); - - // remove any existing judgements for the judged object. - // this can be the case when rewinding. - RemoveAll(c => c.JudgedObject == judgement.JudgedObject); - - base.Add(judgement); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; + +namespace osu.Game.Rulesets.UI +{ + public class JudgementContainer : Container + where T : DrawableJudgement + { + public override void Add(T judgement) + { + if (judgement == null) throw new ArgumentNullException(nameof(judgement)); + + // remove any existing judgements for the judged object. + // this can be the case when rewinding. + RemoveAll(c => c.JudgedObject == judgement.JudgedObject); + + base.Add(judgement); + } + } +} diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index f01069118c..cdc1248049 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -1,112 +1,112 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; -using OpenTK; - -namespace osu.Game.Rulesets.UI -{ - public class ModIcon : Container, IHasTooltip - { - private readonly SpriteIcon modIcon; - private readonly SpriteIcon background; - - private const float size = 80; - - public FontAwesome Icon - { - get { return modIcon.Icon; } - set { modIcon.Icon = value; } - } - - private readonly ModType type; - - public virtual string TooltipText { get; } - - public ModIcon(Mod mod) - { - if (mod == null) throw new ArgumentNullException(nameof(mod)); - - type = mod.Type; - - TooltipText = mod.Name; - - Size = new Vector2(size); - - Children = new Drawable[] - { - background = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(size), - Icon = FontAwesome.fa_osu_mod_bg, - Y = -6.5f, - Shadow = true, - }, - modIcon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Colour = OsuColour.Gray(84), - Size = new Vector2(size - 35), - Icon = mod.Icon - }, - }; - } - - private Color4 backgroundColour; - private Color4 highlightedColour; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - switch (type) - { - default: - case ModType.DifficultyIncrease: - backgroundColour = colours.Yellow; - highlightedColour = colours.YellowLight; - break; - case ModType.DifficultyReduction: - backgroundColour = colours.Green; - highlightedColour = colours.GreenLight; - break; - case ModType.Special: - backgroundColour = colours.Blue; - highlightedColour = colours.BlueLight; - break; - } - - applyStyle(); - } - - private bool highlighted; - - public bool Highlighted - { - get - { - return highlighted; - } - - set - { - highlighted = value; - applyStyle(); - } - } - - private void applyStyle() - { - background.Colour = highlighted ? highlightedColour : backgroundColour; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using OpenTK; + +namespace osu.Game.Rulesets.UI +{ + public class ModIcon : Container, IHasTooltip + { + private readonly SpriteIcon modIcon; + private readonly SpriteIcon background; + + private const float size = 80; + + public FontAwesome Icon + { + get { return modIcon.Icon; } + set { modIcon.Icon = value; } + } + + private readonly ModType type; + + public virtual string TooltipText { get; } + + public ModIcon(Mod mod) + { + if (mod == null) throw new ArgumentNullException(nameof(mod)); + + type = mod.Type; + + TooltipText = mod.Name; + + Size = new Vector2(size); + + Children = new Drawable[] + { + background = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(size), + Icon = FontAwesome.fa_osu_mod_bg, + Y = -6.5f, + Shadow = true, + }, + modIcon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Colour = OsuColour.Gray(84), + Size = new Vector2(size - 35), + Icon = mod.Icon + }, + }; + } + + private Color4 backgroundColour; + private Color4 highlightedColour; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + switch (type) + { + default: + case ModType.DifficultyIncrease: + backgroundColour = colours.Yellow; + highlightedColour = colours.YellowLight; + break; + case ModType.DifficultyReduction: + backgroundColour = colours.Green; + highlightedColour = colours.GreenLight; + break; + case ModType.Special: + backgroundColour = colours.Blue; + highlightedColour = colours.BlueLight; + break; + } + + applyStyle(); + } + + private bool highlighted; + + public bool Highlighted + { + get + { + return highlighted; + } + + set + { + highlighted = value; + applyStyle(); + } + } + + private void applyStyle() + { + background.Colour = highlighted ? highlightedColour : backgroundColour; + } + } +} diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index bbf20c2c26..f2c9b49900 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -1,83 +1,83 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Framework.Allocation; - -namespace osu.Game.Rulesets.UI -{ - public abstract class Playfield : ScalableContainer - { - /// - /// The HitObjects contained in this Playfield. - /// - public HitObjectContainer HitObjects { get; private set; } - - /// - /// All the s nested inside this playfield. - /// - public IReadOnlyList NestedPlayfields => nestedPlayfields; - private List nestedPlayfields; - - /// - /// A container for keeping track of DrawableHitObjects. - /// - /// The width to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - /// The height to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - protected Playfield(float? customWidth = null, float? customHeight = null) - : base(customWidth, customHeight) - { - RelativeSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - HitObjects = CreateHitObjectContainer(); - HitObjects.RelativeSizeAxes = Axes.Both; - - Add(HitObjects); - } - - /// - /// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield. - /// - public virtual void PostProcess() => nestedPlayfields?.ForEach(p => p.PostProcess()); - - /// - /// Adds a DrawableHitObject to this Playfield. - /// - /// The DrawableHitObject to add. - public virtual void Add(DrawableHitObject h) => HitObjects.Add(h); - - /// - /// Remove a DrawableHitObject from this Playfield. - /// - /// The DrawableHitObject to remove. - public virtual void Remove(DrawableHitObject h) => HitObjects.Remove(h); - - /// - /// Registers a as a nested . - /// This does not add the to the draw hierarchy. - /// - /// The to add. - protected void AddNested(Playfield otherPlayfield) - { - if (nestedPlayfields == null) - nestedPlayfields = new List(); - - nestedPlayfields.Add(otherPlayfield); - } - - /// - /// Creates the container that will be used to contain the s. - /// - protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Framework.Allocation; + +namespace osu.Game.Rulesets.UI +{ + public abstract class Playfield : ScalableContainer + { + /// + /// The HitObjects contained in this Playfield. + /// + public HitObjectContainer HitObjects { get; private set; } + + /// + /// All the s nested inside this playfield. + /// + public IReadOnlyList NestedPlayfields => nestedPlayfields; + private List nestedPlayfields; + + /// + /// A container for keeping track of DrawableHitObjects. + /// + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + protected Playfield(float? customWidth = null, float? customHeight = null) + : base(customWidth, customHeight) + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + HitObjects = CreateHitObjectContainer(); + HitObjects.RelativeSizeAxes = Axes.Both; + + Add(HitObjects); + } + + /// + /// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield. + /// + public virtual void PostProcess() => nestedPlayfields?.ForEach(p => p.PostProcess()); + + /// + /// Adds a DrawableHitObject to this Playfield. + /// + /// The DrawableHitObject to add. + public virtual void Add(DrawableHitObject h) => HitObjects.Add(h); + + /// + /// Remove a DrawableHitObject from this Playfield. + /// + /// The DrawableHitObject to remove. + public virtual void Remove(DrawableHitObject h) => HitObjects.Remove(h); + + /// + /// Registers a as a nested . + /// This does not add the to the draw hierarchy. + /// + /// The to add. + protected void AddNested(Playfield otherPlayfield) + { + if (nestedPlayfields == null) + nestedPlayfields = new List(); + + nestedPlayfields.Add(otherPlayfield); + } + + /// + /// Creates the container that will be used to contain the s. + /// + protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); + } +} diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 81418fecd4..d1f1807937 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -1,394 +1,394 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using osu.Framework.Configuration; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Input; -using osu.Game.Configuration; -using osu.Game.Input.Handlers; -using osu.Game.Overlays; -using osu.Game.Rulesets.Configuration; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Scoring; -using OpenTK; - -namespace osu.Game.Rulesets.UI -{ - /// - /// Base RulesetContainer. Doesn't hold objects. - /// - /// Should not be derived - derive instead. - /// - /// - public abstract class RulesetContainer : Container - { - /// - /// The selected variant. - /// - public virtual int Variant => 0; - - /// - /// The input manager for this RulesetContainer. - /// - internal IHasReplayHandler ReplayInputManager => KeyBindingInputManager as IHasReplayHandler; - - /// - /// The key conversion input manager for this RulesetContainer. - /// - public PassThroughInputManager KeyBindingInputManager; - - /// - /// Whether a replay is currently loaded. - /// - public readonly BindableBool HasReplayLoaded = new BindableBool(); - - public abstract IEnumerable Objects { get; } - - private readonly Lazy playfield; - /// - /// The playfield. - /// - public Playfield Playfield => playfield.Value; - - /// - /// The cursor provided by this . May be null if no cursor is provided. - /// - public readonly CursorContainer Cursor; - - protected readonly Ruleset Ruleset; - - private IRulesetConfigManager rulesetConfig; - private OnScreenDisplay onScreenDisplay; - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); - - /// - /// A visual representation of a . - /// - /// The ruleset being repesented. - protected RulesetContainer(Ruleset ruleset) - { - Ruleset = ruleset; - playfield = new Lazy(CreatePlayfield); - - Cursor = CreateCursor(); - } - - [BackgroundDependencyLoader(true)] - private void load(OnScreenDisplay onScreenDisplay, SettingsStore settings) - { - this.onScreenDisplay = onScreenDisplay; - - rulesetConfig = CreateConfig(Ruleset, settings); - - if (rulesetConfig != null) - { - dependencies.Cache(rulesetConfig); - onScreenDisplay?.BeginTracking(this, rulesetConfig); - } - } - - public abstract ScoreProcessor CreateScoreProcessor(); - - /// - /// Creates a key conversion input manager. An exception will be thrown if a valid is not returned. - /// - /// The input manager. - public abstract PassThroughInputManager CreateInputManager(); - - protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null; - - public Replay Replay { get; private set; } - - /// - /// Sets a replay to be used, overriding local input. - /// - /// The replay, null for local input. - public virtual void SetReplay(Replay replay) - { - if (ReplayInputManager == null) - throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available"); - - Replay = replay; - ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; - - HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null; - } - - - /// - /// Creates the cursor. May be null if the doesn't provide a custom cursor. - /// - protected virtual CursorContainer CreateCursor() => null; - - protected virtual IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => null; - - /// - /// Creates a Playfield. - /// - /// The Playfield. - protected abstract Playfield CreatePlayfield(); - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (rulesetConfig != null) - { - onScreenDisplay?.StopTracking(this, rulesetConfig); - rulesetConfig = null; - } - } - } - - /// - /// RulesetContainer that applies conversion to Beatmaps. Does not contain a Playfield - /// and does not load drawable hit objects. - /// - /// Should not be derived - derive instead. - /// - /// - /// The type of HitObject contained by this RulesetContainer. - public abstract class RulesetContainer : RulesetContainer - where TObject : HitObject - { - public event Action OnJudgement; - public event Action OnJudgementRemoved; - - /// - /// The Beatmap - /// - public Beatmap Beatmap; - - /// - /// All the converted hit objects contained by this hit renderer. - /// - public override IEnumerable Objects => Beatmap.HitObjects; - - /// - /// The mods which are to be applied. - /// - protected IEnumerable Mods; - - /// - /// The this was created with. - /// - protected readonly WorkingBeatmap WorkingBeatmap; - - /// - /// Whether the specified beatmap is assumed to be specific to the current ruleset. - /// - public readonly bool IsForCurrentRuleset; - - public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this); - - protected override Container Content => content; - private Container content; - - /// - /// Whether to assume the beatmap passed into this is for the current ruleset. - /// Creates a hit renderer for a beatmap. - /// - /// The ruleset being repesented. - /// The beatmap to create the hit renderer for. - /// Whether to assume the beatmap is for the current ruleset. - protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset) - : base(ruleset) - { - Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap."); - - WorkingBeatmap = workingBeatmap; - IsForCurrentRuleset = isForCurrentRuleset; - // ReSharper disable once PossibleNullReferenceException - Mods = workingBeatmap.Mods.Value; - - RelativeSizeAxes = Axes.Both; - - BeatmapConverter converter = CreateBeatmapConverter(); - BeatmapProcessor processor = CreateBeatmapProcessor(); - - // Check if the beatmap can be converted - if (!converter.CanConvert(workingBeatmap.Beatmap)) - throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter})."); - - // Apply conversion adjustments before converting - foreach (var mod in Mods.OfType>()) - mod.ApplyToBeatmapConverter(converter); - - // Convert the beatmap - Beatmap = converter.Convert(workingBeatmap.Beatmap); - - // Apply difficulty adjustments from mods before using Difficulty. - foreach (var mod in Mods.OfType()) - mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty); - - // Post-process the beatmap - processor.PostProcess(Beatmap); - - // Apply defaults - foreach (var h in Beatmap.HitObjects) - h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); - - KeyBindingInputManager = CreateInputManager(); - KeyBindingInputManager.RelativeSizeAxes = Axes.Both; - - // Add mods, should always be the last thing applied to give full control to mods - applyMods(Mods); - } - - [BackgroundDependencyLoader] - private void load() - { - KeyBindingInputManager.Add(content = new Container - { - RelativeSizeAxes = Axes.Both, - }); - - AddInternal(KeyBindingInputManager); - KeyBindingInputManager.Add(Playfield); - - if (Cursor != null) - KeyBindingInputManager.Add(Cursor); - - loadObjects(); - } - - /// - /// Applies the active mods to this RulesetContainer. - /// - /// - private void applyMods(IEnumerable mods) - { - if (mods == null) - return; - - foreach (var mod in mods.OfType>()) - foreach (var obj in Beatmap.HitObjects) - mod.ApplyToHitObject(obj); - - foreach (var mod in mods.OfType>()) - mod.ApplyToRulesetContainer(this); - } - - public override void SetReplay(Replay replay) - { - base.SetReplay(replay); - - if (ReplayInputManager?.ReplayInputHandler != null) - ReplayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace; - } - - /// - /// Creates and adds drawable representations of hit objects to the play field. - /// - private void loadObjects() - { - foreach (TObject h in Beatmap.HitObjects) - { - var drawableObject = GetVisualRepresentation(h); - - if (drawableObject == null) - continue; - - drawableObject.OnJudgement += (d, j) => OnJudgement?.Invoke(j); - drawableObject.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(j); - - Playfield.Add(drawableObject); - } - - Playfield.PostProcess(); - - foreach (var mod in Mods.OfType()) - mod.ApplyToDrawableHitObjects(Playfield.HitObjects.Objects); - } - - protected override void Update() - { - base.Update(); - - Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea; - } - - /// - /// Creates a processor to perform post-processing operations - /// on HitObjects in converted Beatmaps. - /// - /// The Beatmap processor. - protected virtual BeatmapProcessor CreateBeatmapProcessor() => new BeatmapProcessor(); - - /// - /// Computes the size of the in relative coordinate space after aspect adjustments. - /// - /// The aspect-adjusted size. - protected virtual Vector2 GetAspectAdjustedSize() => Vector2.One; - - /// - /// The area of this that is available for the to use. - /// Must be specified in relative coordinate space to this . - /// This affects the final size of the but does not affect the 's scale. - /// - protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default - - /// - /// Creates a converter to convert Beatmap to a specific mode. - /// - /// The Beatmap converter. - protected abstract BeatmapConverter CreateBeatmapConverter(); - - /// - /// Creates a DrawableHitObject from a HitObject. - /// - /// The HitObject to make drawable. - /// The DrawableHitObject. - protected abstract DrawableHitObject GetVisualRepresentation(TObject h); - } - - /// - /// A derivable RulesetContainer that manages the Playfield and HitObjects. - /// - /// The type of Playfield contained by this RulesetContainer. - /// The type of HitObject contained by this RulesetContainer. - public abstract class RulesetContainer : RulesetContainer - where TObject : HitObject - where TPlayfield : Playfield - { - /// - /// The playfield. - /// - protected new TPlayfield Playfield => (TPlayfield)base.Playfield; - - /// - /// Creates a hit renderer for a beatmap. - /// - /// The ruleset being repesented. - /// The beatmap to create the hit renderer for. - /// Whether to assume the beatmap is for the current ruleset. - protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) - { - } - } - - public class BeatmapInvalidForRulesetException : ArgumentException - { - public BeatmapInvalidForRulesetException(string text) - : base(text) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Input; +using osu.Game.Configuration; +using osu.Game.Input.Handlers; +using osu.Game.Overlays; +using osu.Game.Rulesets.Configuration; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using OpenTK; + +namespace osu.Game.Rulesets.UI +{ + /// + /// Base RulesetContainer. Doesn't hold objects. + /// + /// Should not be derived - derive instead. + /// + /// + public abstract class RulesetContainer : Container + { + /// + /// The selected variant. + /// + public virtual int Variant => 0; + + /// + /// The input manager for this RulesetContainer. + /// + internal IHasReplayHandler ReplayInputManager => KeyBindingInputManager as IHasReplayHandler; + + /// + /// The key conversion input manager for this RulesetContainer. + /// + public PassThroughInputManager KeyBindingInputManager; + + /// + /// Whether a replay is currently loaded. + /// + public readonly BindableBool HasReplayLoaded = new BindableBool(); + + public abstract IEnumerable Objects { get; } + + private readonly Lazy playfield; + /// + /// The playfield. + /// + public Playfield Playfield => playfield.Value; + + /// + /// The cursor provided by this . May be null if no cursor is provided. + /// + public readonly CursorContainer Cursor; + + protected readonly Ruleset Ruleset; + + private IRulesetConfigManager rulesetConfig; + private OnScreenDisplay onScreenDisplay; + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + + /// + /// A visual representation of a . + /// + /// The ruleset being repesented. + protected RulesetContainer(Ruleset ruleset) + { + Ruleset = ruleset; + playfield = new Lazy(CreatePlayfield); + + Cursor = CreateCursor(); + } + + [BackgroundDependencyLoader(true)] + private void load(OnScreenDisplay onScreenDisplay, SettingsStore settings) + { + this.onScreenDisplay = onScreenDisplay; + + rulesetConfig = CreateConfig(Ruleset, settings); + + if (rulesetConfig != null) + { + dependencies.Cache(rulesetConfig); + onScreenDisplay?.BeginTracking(this, rulesetConfig); + } + } + + public abstract ScoreProcessor CreateScoreProcessor(); + + /// + /// Creates a key conversion input manager. An exception will be thrown if a valid is not returned. + /// + /// The input manager. + public abstract PassThroughInputManager CreateInputManager(); + + protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null; + + public Replay Replay { get; private set; } + + /// + /// Sets a replay to be used, overriding local input. + /// + /// The replay, null for local input. + public virtual void SetReplay(Replay replay) + { + if (ReplayInputManager == null) + throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available"); + + Replay = replay; + ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; + + HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null; + } + + + /// + /// Creates the cursor. May be null if the doesn't provide a custom cursor. + /// + protected virtual CursorContainer CreateCursor() => null; + + protected virtual IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => null; + + /// + /// Creates a Playfield. + /// + /// The Playfield. + protected abstract Playfield CreatePlayfield(); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (rulesetConfig != null) + { + onScreenDisplay?.StopTracking(this, rulesetConfig); + rulesetConfig = null; + } + } + } + + /// + /// RulesetContainer that applies conversion to Beatmaps. Does not contain a Playfield + /// and does not load drawable hit objects. + /// + /// Should not be derived - derive instead. + /// + /// + /// The type of HitObject contained by this RulesetContainer. + public abstract class RulesetContainer : RulesetContainer + where TObject : HitObject + { + public event Action OnJudgement; + public event Action OnJudgementRemoved; + + /// + /// The Beatmap + /// + public Beatmap Beatmap; + + /// + /// All the converted hit objects contained by this hit renderer. + /// + public override IEnumerable Objects => Beatmap.HitObjects; + + /// + /// The mods which are to be applied. + /// + protected IEnumerable Mods; + + /// + /// The this was created with. + /// + protected readonly WorkingBeatmap WorkingBeatmap; + + /// + /// Whether the specified beatmap is assumed to be specific to the current ruleset. + /// + public readonly bool IsForCurrentRuleset; + + public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this); + + protected override Container Content => content; + private Container content; + + /// + /// Whether to assume the beatmap passed into this is for the current ruleset. + /// Creates a hit renderer for a beatmap. + /// + /// The ruleset being repesented. + /// The beatmap to create the hit renderer for. + /// Whether to assume the beatmap is for the current ruleset. + protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset) + : base(ruleset) + { + Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap."); + + WorkingBeatmap = workingBeatmap; + IsForCurrentRuleset = isForCurrentRuleset; + // ReSharper disable once PossibleNullReferenceException + Mods = workingBeatmap.Mods.Value; + + RelativeSizeAxes = Axes.Both; + + BeatmapConverter converter = CreateBeatmapConverter(); + BeatmapProcessor processor = CreateBeatmapProcessor(); + + // Check if the beatmap can be converted + if (!converter.CanConvert(workingBeatmap.Beatmap)) + throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter})."); + + // Apply conversion adjustments before converting + foreach (var mod in Mods.OfType>()) + mod.ApplyToBeatmapConverter(converter); + + // Convert the beatmap + Beatmap = converter.Convert(workingBeatmap.Beatmap); + + // Apply difficulty adjustments from mods before using Difficulty. + foreach (var mod in Mods.OfType()) + mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty); + + // Post-process the beatmap + processor.PostProcess(Beatmap); + + // Apply defaults + foreach (var h in Beatmap.HitObjects) + h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); + + KeyBindingInputManager = CreateInputManager(); + KeyBindingInputManager.RelativeSizeAxes = Axes.Both; + + // Add mods, should always be the last thing applied to give full control to mods + applyMods(Mods); + } + + [BackgroundDependencyLoader] + private void load() + { + KeyBindingInputManager.Add(content = new Container + { + RelativeSizeAxes = Axes.Both, + }); + + AddInternal(KeyBindingInputManager); + KeyBindingInputManager.Add(Playfield); + + if (Cursor != null) + KeyBindingInputManager.Add(Cursor); + + loadObjects(); + } + + /// + /// Applies the active mods to this RulesetContainer. + /// + /// + private void applyMods(IEnumerable mods) + { + if (mods == null) + return; + + foreach (var mod in mods.OfType>()) + foreach (var obj in Beatmap.HitObjects) + mod.ApplyToHitObject(obj); + + foreach (var mod in mods.OfType>()) + mod.ApplyToRulesetContainer(this); + } + + public override void SetReplay(Replay replay) + { + base.SetReplay(replay); + + if (ReplayInputManager?.ReplayInputHandler != null) + ReplayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace; + } + + /// + /// Creates and adds drawable representations of hit objects to the play field. + /// + private void loadObjects() + { + foreach (TObject h in Beatmap.HitObjects) + { + var drawableObject = GetVisualRepresentation(h); + + if (drawableObject == null) + continue; + + drawableObject.OnJudgement += (d, j) => OnJudgement?.Invoke(j); + drawableObject.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(j); + + Playfield.Add(drawableObject); + } + + Playfield.PostProcess(); + + foreach (var mod in Mods.OfType()) + mod.ApplyToDrawableHitObjects(Playfield.HitObjects.Objects); + } + + protected override void Update() + { + base.Update(); + + Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea; + } + + /// + /// Creates a processor to perform post-processing operations + /// on HitObjects in converted Beatmaps. + /// + /// The Beatmap processor. + protected virtual BeatmapProcessor CreateBeatmapProcessor() => new BeatmapProcessor(); + + /// + /// Computes the size of the in relative coordinate space after aspect adjustments. + /// + /// The aspect-adjusted size. + protected virtual Vector2 GetAspectAdjustedSize() => Vector2.One; + + /// + /// The area of this that is available for the to use. + /// Must be specified in relative coordinate space to this . + /// This affects the final size of the but does not affect the 's scale. + /// + protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default + + /// + /// Creates a converter to convert Beatmap to a specific mode. + /// + /// The Beatmap converter. + protected abstract BeatmapConverter CreateBeatmapConverter(); + + /// + /// Creates a DrawableHitObject from a HitObject. + /// + /// The HitObject to make drawable. + /// The DrawableHitObject. + protected abstract DrawableHitObject GetVisualRepresentation(TObject h); + } + + /// + /// A derivable RulesetContainer that manages the Playfield and HitObjects. + /// + /// The type of Playfield contained by this RulesetContainer. + /// The type of HitObject contained by this RulesetContainer. + public abstract class RulesetContainer : RulesetContainer + where TObject : HitObject + where TPlayfield : Playfield + { + /// + /// The playfield. + /// + protected new TPlayfield Playfield => (TPlayfield)base.Playfield; + + /// + /// Creates a hit renderer for a beatmap. + /// + /// The ruleset being repesented. + /// The beatmap to create the hit renderer for. + /// Whether to assume the beatmap is for the current ruleset. + protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + } + } + + public class BeatmapInvalidForRulesetException : ArgumentException + { + public BeatmapInvalidForRulesetException(string text) + : base(text) + { + } + } +} diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 3f8a17e23d..58a66a5224 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -1,270 +1,270 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.Input.Bindings; -using osu.Framework.Timing; -using osu.Game.Configuration; -using osu.Game.Input.Bindings; -using osu.Game.Input.Handlers; -using osu.Game.Screens.Play; -using OpenTK.Input; - -namespace osu.Game.Rulesets.UI -{ - public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler - where T : struct - { - public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer - { - public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) - : base(ruleset, variant, unique) - { - } - } - - protected readonly KeyBindingContainer KeyBindingContainer; - - protected override Container Content => KeyBindingContainer; - - protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) - { - InternalChild = KeyBindingContainer = new RulesetKeyBindingContainer(ruleset, variant, unique); - } - - #region Action mapping (for replays) - - private List lastPressedActions = new List(); - - protected override void HandleNewState(InputState state) - { - base.HandleNewState(state); - - var replayState = state as ReplayInputHandler.ReplayState; - - if (replayState == null) return; - - // Here we handle states specifically coming from a replay source. - // These have extra action information rather than keyboard keys or mouse buttons. - - List newActions = replayState.PressedActions; - - foreach (var released in lastPressedActions.Except(newActions)) - KeyBindingContainer.TriggerReleased(released); - - foreach (var pressed in newActions.Except(lastPressedActions)) - KeyBindingContainer.TriggerPressed(pressed); - - lastPressedActions = newActions; - } - - #endregion - - #region IHasReplayHandler - - private ReplayInputHandler replayInputHandler; - public ReplayInputHandler ReplayInputHandler - { - get - { - return replayInputHandler; - } - set - { - if (replayInputHandler != null) RemoveHandler(replayInputHandler); - - replayInputHandler = value; - UseParentState = replayInputHandler == null; - - if (replayInputHandler != null) - AddHandler(replayInputHandler); - } - } - - #endregion - - #region Clock control - - private ManualClock clock; - private IFrameBasedClock parentClock; - - protected override void LoadComplete() - { - base.LoadComplete(); - - //our clock will now be our parent's clock, but we want to replace this to allow manual control. - parentClock = Clock; - - ProcessCustomClock = false; - Clock = new FramedClock(clock = new ManualClock - { - CurrentTime = parentClock.CurrentTime, - Rate = parentClock.Rate, - }); - } - - /// - /// Whether we are running up-to-date with our parent clock. - /// If not, we will need to keep processing children until we catch up. - /// - private bool requireMoreUpdateLoops; - - /// - /// Whether we are in a valid state (ie. should we keep processing children frames). - /// This should be set to false when the replay is, for instance, waiting for future frames to arrive. - /// - private bool validState; - - protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState; - - private bool isAttached => replayInputHandler != null && !UseParentState; - - private const int max_catch_up_updates_per_frame = 50; - - public override bool UpdateSubTree() - { - requireMoreUpdateLoops = true; - validState = true; - - int loops = 0; - - while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame) - { - if (!base.UpdateSubTree()) - return false; - - UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat); - - if (isAttached) - { - // When handling replay input, we need to consider the possibility of fast-forwarding, which may cause the clock to be updated - // to a point very far into the future, then playing a frame at that time. In such a case, lifetime MUST be updated before - // input is handled. This is why base.Update is not called from the derived Update when handling replay input, and is instead - // called manually at the correct time here. - base.Update(); - } - } - - return true; - } - - protected override void Update() - { - if (parentClock == null) return; - - clock.Rate = parentClock.Rate; - clock.IsRunning = parentClock.IsRunning; - - if (!isAttached) - { - clock.CurrentTime = parentClock.CurrentTime; - } - else - { - double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime); - - if (newTime == null) - { - // we shouldn't execute for this time value. probably waiting on more replay data. - validState = false; - return; - } - - clock.CurrentTime = newTime.Value; - } - - requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime; - - // The manual clock time has changed in the above code. The framed clock now needs to be updated - // to ensure that the its time is valid for our children before input is processed - Clock.ProcessFrame(); - - if (!isAttached) - { - // For non-replay input handling, this provides equivalent input ordering as if Update was not overridden - base.Update(); - } - } - - #endregion - - #region Setting application (disables etc.) - - private Bindable mouseDisabled; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); - } - - protected override void TransformState(InputState state) - { - base.TransformState(state); - - // we don't want to transform the state if a replay is present (for now, at least). - if (replayInputHandler != null) return; - - var mouse = state.Mouse as Framework.Input.MouseState; - - if (mouse != null) - { - if (mouseDisabled.Value) - { - mouse.SetPressed(MouseButton.Left, false); - mouse.SetPressed(MouseButton.Right, false); - } - } - } - - #endregion - - #region Key Counter Attachment - - public void Attach(KeyCounterCollection keyCounter) - { - var receptor = new ActionReceptor(keyCounter); - Add(receptor); - keyCounter.SetReceptor(receptor); - - keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b))); - } - - public class ActionReceptor : KeyCounterCollection.Receptor, IKeyBindingHandler - { - public ActionReceptor(KeyCounterCollection target) - : base(target) - { - } - - public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action)); - - public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action)); - } - - #endregion - } - - /// - /// Expose the in a capable . - /// - public interface IHasReplayHandler - { - ReplayInputHandler ReplayInputHandler { get; set; } - } - - /// - /// Supports attaching a . - /// Keys will be populated automatically and a receptor will be injected inside. - /// - public interface ICanAttachKeyCounter - { - void Attach(KeyCounterCollection keyCounter); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Timing; +using osu.Game.Configuration; +using osu.Game.Input.Bindings; +using osu.Game.Input.Handlers; +using osu.Game.Screens.Play; +using OpenTK.Input; + +namespace osu.Game.Rulesets.UI +{ + public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler + where T : struct + { + public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer + { + public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) + : base(ruleset, variant, unique) + { + } + } + + protected readonly KeyBindingContainer KeyBindingContainer; + + protected override Container Content => KeyBindingContainer; + + protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) + { + InternalChild = KeyBindingContainer = new RulesetKeyBindingContainer(ruleset, variant, unique); + } + + #region Action mapping (for replays) + + private List lastPressedActions = new List(); + + protected override void HandleNewState(InputState state) + { + base.HandleNewState(state); + + var replayState = state as ReplayInputHandler.ReplayState; + + if (replayState == null) return; + + // Here we handle states specifically coming from a replay source. + // These have extra action information rather than keyboard keys or mouse buttons. + + List newActions = replayState.PressedActions; + + foreach (var released in lastPressedActions.Except(newActions)) + KeyBindingContainer.TriggerReleased(released); + + foreach (var pressed in newActions.Except(lastPressedActions)) + KeyBindingContainer.TriggerPressed(pressed); + + lastPressedActions = newActions; + } + + #endregion + + #region IHasReplayHandler + + private ReplayInputHandler replayInputHandler; + public ReplayInputHandler ReplayInputHandler + { + get + { + return replayInputHandler; + } + set + { + if (replayInputHandler != null) RemoveHandler(replayInputHandler); + + replayInputHandler = value; + UseParentState = replayInputHandler == null; + + if (replayInputHandler != null) + AddHandler(replayInputHandler); + } + } + + #endregion + + #region Clock control + + private ManualClock clock; + private IFrameBasedClock parentClock; + + protected override void LoadComplete() + { + base.LoadComplete(); + + //our clock will now be our parent's clock, but we want to replace this to allow manual control. + parentClock = Clock; + + ProcessCustomClock = false; + Clock = new FramedClock(clock = new ManualClock + { + CurrentTime = parentClock.CurrentTime, + Rate = parentClock.Rate, + }); + } + + /// + /// Whether we are running up-to-date with our parent clock. + /// If not, we will need to keep processing children until we catch up. + /// + private bool requireMoreUpdateLoops; + + /// + /// Whether we are in a valid state (ie. should we keep processing children frames). + /// This should be set to false when the replay is, for instance, waiting for future frames to arrive. + /// + private bool validState; + + protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState; + + private bool isAttached => replayInputHandler != null && !UseParentState; + + private const int max_catch_up_updates_per_frame = 50; + + public override bool UpdateSubTree() + { + requireMoreUpdateLoops = true; + validState = true; + + int loops = 0; + + while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame) + { + if (!base.UpdateSubTree()) + return false; + + UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat); + + if (isAttached) + { + // When handling replay input, we need to consider the possibility of fast-forwarding, which may cause the clock to be updated + // to a point very far into the future, then playing a frame at that time. In such a case, lifetime MUST be updated before + // input is handled. This is why base.Update is not called from the derived Update when handling replay input, and is instead + // called manually at the correct time here. + base.Update(); + } + } + + return true; + } + + protected override void Update() + { + if (parentClock == null) return; + + clock.Rate = parentClock.Rate; + clock.IsRunning = parentClock.IsRunning; + + if (!isAttached) + { + clock.CurrentTime = parentClock.CurrentTime; + } + else + { + double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime); + + if (newTime == null) + { + // we shouldn't execute for this time value. probably waiting on more replay data. + validState = false; + return; + } + + clock.CurrentTime = newTime.Value; + } + + requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime; + + // The manual clock time has changed in the above code. The framed clock now needs to be updated + // to ensure that the its time is valid for our children before input is processed + Clock.ProcessFrame(); + + if (!isAttached) + { + // For non-replay input handling, this provides equivalent input ordering as if Update was not overridden + base.Update(); + } + } + + #endregion + + #region Setting application (disables etc.) + + private Bindable mouseDisabled; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); + } + + protected override void TransformState(InputState state) + { + base.TransformState(state); + + // we don't want to transform the state if a replay is present (for now, at least). + if (replayInputHandler != null) return; + + var mouse = state.Mouse as Framework.Input.MouseState; + + if (mouse != null) + { + if (mouseDisabled.Value) + { + mouse.SetPressed(MouseButton.Left, false); + mouse.SetPressed(MouseButton.Right, false); + } + } + } + + #endregion + + #region Key Counter Attachment + + public void Attach(KeyCounterCollection keyCounter) + { + var receptor = new ActionReceptor(keyCounter); + Add(receptor); + keyCounter.SetReceptor(receptor); + + keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b))); + } + + public class ActionReceptor : KeyCounterCollection.Receptor, IKeyBindingHandler + { + public ActionReceptor(KeyCounterCollection target) + : base(target) + { + } + + public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action)); + + public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action)); + } + + #endregion + } + + /// + /// Expose the in a capable . + /// + public interface IHasReplayHandler + { + ReplayInputHandler ReplayInputHandler { get; set; } + } + + /// + /// Supports attaching a . + /// Keys will be populated automatically and a receptor will be injected inside. + /// + public interface ICanAttachKeyCounter + { + void Attach(KeyCounterCollection keyCounter); + } +} diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs index 04e6db9578..5ee03be7ee 100644 --- a/osu.Game/Rulesets/UI/ScalableContainer.cs +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -1,99 +1,99 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Rulesets.UI -{ - /// - /// A which can have its internal coordinate system scaled to a specific size. - /// - public class ScalableContainer : Container - { - /// - /// A function that converts coordinates from gamefield to screen space. - /// - public Func GamefieldToScreenSpace => scaledContent.GamefieldToScreenSpace; - - /// - /// The scaled content. - /// - private readonly ScaledContainer scaledContent; - protected override Container Content => scaledContent; - - /// - /// A which can have its internal coordinate system scaled to a specific size. - /// - /// The width to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - /// The height to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - public ScalableContainer(float? customWidth = null, float? customHeight = null) - { - AddInternal(scaledContent = new ScaledContainer - { - CustomWidth = customWidth, - CustomHeight = customHeight, - RelativeSizeAxes = Axes.Both, - }); - } - - private class ScaledContainer : Container - { - /// - /// A function that converts coordinates from gamefield to screen space. - /// - public Func GamefieldToScreenSpace => content.ToScreenSpace; - - /// - /// The value to scale the width of the content to match. - /// If null, is used. - /// - public float? CustomWidth; - - /// - /// The value to scale the height of the content to match. - /// if null, is used. - /// - public float? CustomHeight; - - private readonly Container content; - protected override Container Content => content; - - public ScaledContainer() - { - AddInternal(content = new Container { RelativeSizeAxes = Axes.Both }); - } - - protected override void Update() - { - base.Update(); - - content.Scale = sizeScale; - content.Size = Vector2.Divide(Vector2.One, sizeScale); - } - - /// - /// The scale that is required for the size of the content to match and . - /// - private Vector2 sizeScale - { - get - { - if (CustomWidth.HasValue && CustomHeight.HasValue) - return Vector2.Divide(DrawSize, new Vector2(CustomWidth.Value, CustomHeight.Value)); - if (CustomWidth.HasValue) - return new Vector2(DrawSize.X / CustomWidth.Value); - if (CustomHeight.HasValue) - return new Vector2(DrawSize.Y / CustomHeight.Value); - return Vector2.One; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Rulesets.UI +{ + /// + /// A which can have its internal coordinate system scaled to a specific size. + /// + public class ScalableContainer : Container + { + /// + /// A function that converts coordinates from gamefield to screen space. + /// + public Func GamefieldToScreenSpace => scaledContent.GamefieldToScreenSpace; + + /// + /// The scaled content. + /// + private readonly ScaledContainer scaledContent; + protected override Container Content => scaledContent; + + /// + /// A which can have its internal coordinate system scaled to a specific size. + /// + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + public ScalableContainer(float? customWidth = null, float? customHeight = null) + { + AddInternal(scaledContent = new ScaledContainer + { + CustomWidth = customWidth, + CustomHeight = customHeight, + RelativeSizeAxes = Axes.Both, + }); + } + + private class ScaledContainer : Container + { + /// + /// A function that converts coordinates from gamefield to screen space. + /// + public Func GamefieldToScreenSpace => content.ToScreenSpace; + + /// + /// The value to scale the width of the content to match. + /// If null, is used. + /// + public float? CustomWidth; + + /// + /// The value to scale the height of the content to match. + /// if null, is used. + /// + public float? CustomHeight; + + private readonly Container content; + protected override Container Content => content; + + public ScaledContainer() + { + AddInternal(content = new Container { RelativeSizeAxes = Axes.Both }); + } + + protected override void Update() + { + base.Update(); + + content.Scale = sizeScale; + content.Size = Vector2.Divide(Vector2.One, sizeScale); + } + + /// + /// The scale that is required for the size of the content to match and . + /// + private Vector2 sizeScale + { + get + { + if (CustomWidth.HasValue && CustomHeight.HasValue) + return Vector2.Divide(DrawSize, new Vector2(CustomWidth.Value, CustomHeight.Value)); + if (CustomWidth.HasValue) + return new Vector2(DrawSize.X / CustomWidth.Value); + if (CustomHeight.HasValue) + return new Vector2(DrawSize.Y / CustomHeight.Value); + return Vector2.One; + } + } + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs index 372bdb1030..4fe727cb84 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs @@ -1,25 +1,25 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.UI.Scrolling -{ - public enum ScrollingDirection - { - /// - /// Hitobjects will scroll vertically from the bottom of the hitobject container. - /// - Up, - /// - /// Hitobjects will scroll vertically from the top of the hitobject container. - /// - Down, - /// - /// Hitobjects will scroll horizontally from the right of the hitobject container. - /// - Left, - /// - /// Hitobjects will scroll horizontally from the left of the hitobject container. - /// - Right - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.UI.Scrolling +{ + public enum ScrollingDirection + { + /// + /// Hitobjects will scroll vertically from the bottom of the hitobject container. + /// + Up, + /// + /// Hitobjects will scroll vertically from the top of the hitobject container. + /// + Down, + /// + /// Hitobjects will scroll horizontally from the right of the hitobject container. + /// + Left, + /// + /// Hitobjects will scroll horizontally from the left of the hitobject container. + /// + Right + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 530ed653aa..edfea57e94 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -1,116 +1,116 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Caching; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Lists; -using osu.Game.Configuration; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Timing; -using osu.Game.Rulesets.UI.Scrolling.Visualisers; - -namespace osu.Game.Rulesets.UI.Scrolling -{ - public class ScrollingHitObjectContainer : HitObjectContainer - { - /// - /// The duration required to scroll through one length of the before any control point adjustments. - /// - public readonly BindableDouble TimeRange = new BindableDouble - { - MinValue = 0, - MaxValue = double.MaxValue - }; - - /// - /// The control points that adjust the scrolling speed. - /// - protected readonly SortedList ControlPoints = new SortedList(); - - private readonly ScrollingDirection direction; - - private Cached initialStateCache = new Cached(); - - public ScrollingHitObjectContainer(ScrollingDirection direction) - { - this.direction = direction; - - RelativeSizeAxes = Axes.Both; - - TimeRange.ValueChanged += v => initialStateCache.Invalidate(); - } - - private ISpeedChangeVisualiser speedChangeVisualiser; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - switch (config.Get(OsuSetting.SpeedChangeVisualisation)) - { - case SpeedChangeVisualisationMethod.Sequential: - speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints); - break; - case SpeedChangeVisualisationMethod.Overlapping: - speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints); - break; - } - } - - public override void Add(DrawableHitObject hitObject) - { - initialStateCache.Invalidate(); - base.Add(hitObject); - } - - public override bool Remove(DrawableHitObject hitObject) - { - var result = base.Remove(hitObject); - if (result) - initialStateCache.Invalidate(); - return result; - } - - public void AddControlPoint(MultiplierControlPoint controlPoint) - { - ControlPoints.Add(controlPoint); - initialStateCache.Invalidate(); - } - - public bool RemoveControlPoint(MultiplierControlPoint controlPoint) - { - var result = ControlPoints.Remove(controlPoint); - if (result) - initialStateCache.Invalidate(); - return result; - } - - public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) - { - if ((invalidation & (Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo)) > 0) - initialStateCache.Invalidate(); - - return base.Invalidate(invalidation, source, shallPropagate); - } - - protected override void Update() - { - base.Update(); - - if (!initialStateCache.IsValid) - { - speedChangeVisualiser.ComputeInitialStates(Objects, direction, TimeRange, DrawSize); - initialStateCache.Validate(); - } - } - - protected override void UpdateAfterChildrenLife() - { - base.UpdateAfterChildrenLife(); - - // We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions - speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Caching; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Lists; +using osu.Game.Configuration; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Timing; +using osu.Game.Rulesets.UI.Scrolling.Visualisers; + +namespace osu.Game.Rulesets.UI.Scrolling +{ + public class ScrollingHitObjectContainer : HitObjectContainer + { + /// + /// The duration required to scroll through one length of the before any control point adjustments. + /// + public readonly BindableDouble TimeRange = new BindableDouble + { + MinValue = 0, + MaxValue = double.MaxValue + }; + + /// + /// The control points that adjust the scrolling speed. + /// + protected readonly SortedList ControlPoints = new SortedList(); + + private readonly ScrollingDirection direction; + + private Cached initialStateCache = new Cached(); + + public ScrollingHitObjectContainer(ScrollingDirection direction) + { + this.direction = direction; + + RelativeSizeAxes = Axes.Both; + + TimeRange.ValueChanged += v => initialStateCache.Invalidate(); + } + + private ISpeedChangeVisualiser speedChangeVisualiser; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + switch (config.Get(OsuSetting.SpeedChangeVisualisation)) + { + case SpeedChangeVisualisationMethod.Sequential: + speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints); + break; + case SpeedChangeVisualisationMethod.Overlapping: + speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints); + break; + } + } + + public override void Add(DrawableHitObject hitObject) + { + initialStateCache.Invalidate(); + base.Add(hitObject); + } + + public override bool Remove(DrawableHitObject hitObject) + { + var result = base.Remove(hitObject); + if (result) + initialStateCache.Invalidate(); + return result; + } + + public void AddControlPoint(MultiplierControlPoint controlPoint) + { + ControlPoints.Add(controlPoint); + initialStateCache.Invalidate(); + } + + public bool RemoveControlPoint(MultiplierControlPoint controlPoint) + { + var result = ControlPoints.Remove(controlPoint); + if (result) + initialStateCache.Invalidate(); + return result; + } + + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + if ((invalidation & (Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo)) > 0) + initialStateCache.Invalidate(); + + return base.Invalidate(invalidation, source, shallPropagate); + } + + protected override void Update() + { + base.Update(); + + if (!initialStateCache.IsValid) + { + speedChangeVisualiser.ComputeInitialStates(Objects, direction, TimeRange, DrawSize); + initialStateCache.Validate(); + } + } + + protected override void UpdateAfterChildrenLife() + { + base.UpdateAfterChildrenLife(); + + // We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions + speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize); + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 1c1c8f7f61..1e7f561aba 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -1,127 +1,127 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Transforms; -using osu.Framework.Input; -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Objects.Drawables; -using OpenTK.Input; - -namespace osu.Game.Rulesets.UI.Scrolling -{ - /// - /// A type of specialized towards scrolling s. - /// - public abstract class ScrollingPlayfield : Playfield - { - /// - /// The default span of time visible by the length of the scrolling axes. - /// This is clamped between and . - /// - private const double time_span_default = 1500; - /// - /// The minimum span of time that may be visible by the length of the scrolling axes. - /// - private const double time_span_min = 50; - /// - /// The maximum span of time that may be visible by the length of the scrolling axes. - /// - private const double time_span_max = 10000; - /// - /// The step increase/decrease of the span of time visible by the length of the scrolling axes. - /// - private const double time_span_step = 50; - - /// - /// The span of time that is visible by the length of the scrolling axes. - /// For example, only hit objects with start time less than or equal to 1000 will be visible with = 1000. - /// - public readonly BindableDouble VisibleTimeRange = new BindableDouble(time_span_default) - { - Default = time_span_default, - MinValue = time_span_min, - MaxValue = time_span_max - }; - - /// - /// Whether the player can change . - /// - protected virtual bool UserScrollSpeedAdjustment => true; - - /// - /// The container that contains the s. - /// - public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects; - - private readonly ScrollingDirection direction; - - /// - /// Creates a new . - /// - /// The direction in which s in this container should scroll. - /// The width to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - /// The height to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null) - : base(customWidth, customHeight) - { - this.direction = direction; - } - - [BackgroundDependencyLoader] - private void load() - { - HitObjects.TimeRange.BindTo(VisibleTimeRange); - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (!UserScrollSpeedAdjustment) - return false; - - if (state.Keyboard.ControlPressed) - { - switch (args.Key) - { - case Key.Minus: - transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint); - break; - case Key.Plus: - transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint); - break; - } - } - - return false; - } - - private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None) - { - this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing)); - } - - protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction); - - private class TransformVisibleTimeRange : Transform - { - private double valueAt(double time) - { - if (time < StartTime) return StartValue; - if (time >= EndTime) return EndValue; - - return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); - } - - public override string TargetMember => "VisibleTimeRange.Value"; - - protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time); - protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Objects.Drawables; +using OpenTK.Input; + +namespace osu.Game.Rulesets.UI.Scrolling +{ + /// + /// A type of specialized towards scrolling s. + /// + public abstract class ScrollingPlayfield : Playfield + { + /// + /// The default span of time visible by the length of the scrolling axes. + /// This is clamped between and . + /// + private const double time_span_default = 1500; + /// + /// The minimum span of time that may be visible by the length of the scrolling axes. + /// + private const double time_span_min = 50; + /// + /// The maximum span of time that may be visible by the length of the scrolling axes. + /// + private const double time_span_max = 10000; + /// + /// The step increase/decrease of the span of time visible by the length of the scrolling axes. + /// + private const double time_span_step = 50; + + /// + /// The span of time that is visible by the length of the scrolling axes. + /// For example, only hit objects with start time less than or equal to 1000 will be visible with = 1000. + /// + public readonly BindableDouble VisibleTimeRange = new BindableDouble(time_span_default) + { + Default = time_span_default, + MinValue = time_span_min, + MaxValue = time_span_max + }; + + /// + /// Whether the player can change . + /// + protected virtual bool UserScrollSpeedAdjustment => true; + + /// + /// The container that contains the s. + /// + public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects; + + private readonly ScrollingDirection direction; + + /// + /// Creates a new . + /// + /// The direction in which s in this container should scroll. + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null) + : base(customWidth, customHeight) + { + this.direction = direction; + } + + [BackgroundDependencyLoader] + private void load() + { + HitObjects.TimeRange.BindTo(VisibleTimeRange); + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!UserScrollSpeedAdjustment) + return false; + + if (state.Keyboard.ControlPressed) + { + switch (args.Key) + { + case Key.Minus: + transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint); + break; + case Key.Plus: + transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint); + break; + } + } + + return false; + } + + private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None) + { + this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing)); + } + + protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction); + + private class TransformVisibleTimeRange : Transform + { + private double valueAt(double time) + { + if (time < StartTime) return StartValue; + if (time >= EndTime) return EndValue; + + return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); + } + + public override string TargetMember => "VisibleTimeRange.Value"; + + protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time); + protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value; + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs index 5f6b6801ce..79f9eaa9e9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs @@ -1,110 +1,110 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Lists; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.IO.Serialization; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Timing; - -namespace osu.Game.Rulesets.UI.Scrolling -{ - /// - /// A type of that supports a . - /// s inside this will scroll within the playfield. - /// - public abstract class ScrollingRulesetContainer : RulesetContainer - where TObject : HitObject - where TPlayfield : ScrollingPlayfield - { - /// - /// Provides the default s that adjust the scrolling rate of s - /// inside this . - /// - /// - protected readonly SortedList DefaultControlPoints = new SortedList(Comparer.Default); - - protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) - { - } - - [BackgroundDependencyLoader] - private void load() - { - // Calculate default multiplier control points - var lastTimingPoint = new TimingControlPoint(); - var lastDifficultyPoint = new DifficultyControlPoint(); - - // Merge timing + difficulty points - var allPoints = new SortedList(Comparer.Default); - allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints); - allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints); - - // Generate the timing points, making non-timing changes use the previous timing change - var timingChanges = allPoints.Select(c => - { - var timingPoint = c as TimingControlPoint; - var difficultyPoint = c as DifficultyControlPoint; - - if (timingPoint != null) - lastTimingPoint = timingPoint; - - if (difficultyPoint != null) - lastDifficultyPoint = difficultyPoint; - - return new MultiplierControlPoint(c.Time) - { - TimingPoint = lastTimingPoint, - DifficultyPoint = lastDifficultyPoint - }; - }); - - double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - - // Perform some post processing of the timing changes - timingChanges = timingChanges - // Collapse sections after the last hit object - .Where(s => s.StartTime <= lastObjectTime) - // Collapse sections with the same start time - .GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime); - - DefaultControlPoints.AddRange(timingChanges); - - // If we have no control points, add a default one - if (DefaultControlPoints.Count == 0) - DefaultControlPoints.Add(new MultiplierControlPoint()); - - DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield)); - } - - private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield) - { - playfield.HitObjects.AddControlPoint(controlPoint); - playfield.NestedPlayfields?.OfType().ForEach(p => applySpeedAdjustment(controlPoint, p)); - } - - /// - /// Generates a with the default timing change/difficulty change from the beatmap at a time. - /// - /// The time to create the control point at. - /// The default at . - public MultiplierControlPoint CreateControlPointAt(double time) - { - if (DefaultControlPoints.Count == 0) - return new MultiplierControlPoint(time); - - int index = DefaultControlPoints.BinarySearch(new MultiplierControlPoint(time)); - if (index < 0) - return new MultiplierControlPoint(time); - - return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone()); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Lists; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Timing; + +namespace osu.Game.Rulesets.UI.Scrolling +{ + /// + /// A type of that supports a . + /// s inside this will scroll within the playfield. + /// + public abstract class ScrollingRulesetContainer : RulesetContainer + where TObject : HitObject + where TPlayfield : ScrollingPlayfield + { + /// + /// Provides the default s that adjust the scrolling rate of s + /// inside this . + /// + /// + protected readonly SortedList DefaultControlPoints = new SortedList(Comparer.Default); + + protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + } + + [BackgroundDependencyLoader] + private void load() + { + // Calculate default multiplier control points + var lastTimingPoint = new TimingControlPoint(); + var lastDifficultyPoint = new DifficultyControlPoint(); + + // Merge timing + difficulty points + var allPoints = new SortedList(Comparer.Default); + allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints); + allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints); + + // Generate the timing points, making non-timing changes use the previous timing change + var timingChanges = allPoints.Select(c => + { + var timingPoint = c as TimingControlPoint; + var difficultyPoint = c as DifficultyControlPoint; + + if (timingPoint != null) + lastTimingPoint = timingPoint; + + if (difficultyPoint != null) + lastDifficultyPoint = difficultyPoint; + + return new MultiplierControlPoint(c.Time) + { + TimingPoint = lastTimingPoint, + DifficultyPoint = lastDifficultyPoint + }; + }); + + double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; + + // Perform some post processing of the timing changes + timingChanges = timingChanges + // Collapse sections after the last hit object + .Where(s => s.StartTime <= lastObjectTime) + // Collapse sections with the same start time + .GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime); + + DefaultControlPoints.AddRange(timingChanges); + + // If we have no control points, add a default one + if (DefaultControlPoints.Count == 0) + DefaultControlPoints.Add(new MultiplierControlPoint()); + + DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield)); + } + + private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield) + { + playfield.HitObjects.AddControlPoint(controlPoint); + playfield.NestedPlayfields?.OfType().ForEach(p => applySpeedAdjustment(controlPoint, p)); + } + + /// + /// Generates a with the default timing change/difficulty change from the beatmap at a time. + /// + /// The time to create the control point at. + /// The default at . + public MultiplierControlPoint CreateControlPointAt(double time) + { + if (DefaultControlPoints.Count == 0) + return new MultiplierControlPoint(time); + + int index = DefaultControlPoints.BinarySearch(new MultiplierControlPoint(time)); + if (index < 0) + return new MultiplierControlPoint(time); + + return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone()); + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs index 46d71e1602..02791e0517 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; - -namespace osu.Game.Rulesets.UI.Scrolling.Visualisers -{ - public interface ISpeedChangeVisualiser - { - /// - /// Computes the states of s that are constant, such as lifetime and spatial length. - /// This is invoked once whenever or changes. - /// - /// The s whose states should be computed. - /// The scrolling direction. - /// The duration required to scroll through one length of the screen before any control point adjustments. - /// The length of the screen that is scrolled through. - void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length); - - /// - /// Computes the states of s that change depending on , such as position. - /// This is invoked once per frame. - /// - /// The s whose states should be computed. - /// The scrolling direction. - /// The current time. - /// The duration required to scroll through one length of the screen before any control point adjustments. - /// The length of the screen that is scrolled through. - void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; + +namespace osu.Game.Rulesets.UI.Scrolling.Visualisers +{ + public interface ISpeedChangeVisualiser + { + /// + /// Computes the states of s that are constant, such as lifetime and spatial length. + /// This is invoked once whenever or changes. + /// + /// The s whose states should be computed. + /// The scrolling direction. + /// The duration required to scroll through one length of the screen before any control point adjustments. + /// The length of the screen that is scrolled through. + void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length); + + /// + /// Computes the states of s that change depending on , such as position. + /// This is invoked once per frame. + /// + /// The s whose states should be computed. + /// The scrolling direction. + /// The current time. + /// The duration required to scroll through one length of the screen before any control point adjustments. + /// The length of the screen that is scrolled through. + void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length); + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index 48c212efa7..2365582645 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -1,80 +1,80 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Framework.Lists; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Timing; -using OpenTK; - -namespace osu.Game.Rulesets.UI.Scrolling.Visualisers -{ - public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser - { - private readonly SortedList controlPoints; - - public OverlappingSpeedChangeVisualiser(SortedList controlPoints) - { - this.controlPoints = controlPoints; - } - - public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length) - { - foreach (var obj in hitObjects) - { - var controlPoint = controlPointAt(obj.HitObject.StartTime); - obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier; - - if (obj.HasNestedHitObjects) - { - ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); - ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); - } - } - } - - public void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) - { - foreach (var obj in hitObjects) - { - var controlPoint = controlPointAt(obj.HitObject.StartTime); - - var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange; - - switch (direction) - { - case ScrollingDirection.Up: - obj.Y = (float)(position * length.Y); - break; - case ScrollingDirection.Down: - obj.Y = (float)(-position * length.Y); - break; - case ScrollingDirection.Left: - obj.X = (float)(position * length.X); - break; - case ScrollingDirection.Right: - obj.X = (float)(-position * length.X); - break; - } - } - } - - private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); - private MultiplierControlPoint controlPointAt(double time) - { - if (controlPoints.Count == 0) - return new MultiplierControlPoint(double.NegativeInfinity); - - if (time < controlPoints[0].StartTime) - return controlPoints[0]; - - searchPoint.StartTime = time; - int index = controlPoints.BinarySearch(searchPoint); - - if (index < 0) - index = ~index - 1; - - return controlPoints[index]; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Lists; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Timing; +using OpenTK; + +namespace osu.Game.Rulesets.UI.Scrolling.Visualisers +{ + public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser + { + private readonly SortedList controlPoints; + + public OverlappingSpeedChangeVisualiser(SortedList controlPoints) + { + this.controlPoints = controlPoints; + } + + public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length) + { + foreach (var obj in hitObjects) + { + var controlPoint = controlPointAt(obj.HitObject.StartTime); + obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier; + + if (obj.HasNestedHitObjects) + { + ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); + ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + } + } + } + + public void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + { + foreach (var obj in hitObjects) + { + var controlPoint = controlPointAt(obj.HitObject.StartTime); + + var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange; + + switch (direction) + { + case ScrollingDirection.Up: + obj.Y = (float)(position * length.Y); + break; + case ScrollingDirection.Down: + obj.Y = (float)(-position * length.Y); + break; + case ScrollingDirection.Left: + obj.X = (float)(position * length.X); + break; + case ScrollingDirection.Right: + obj.X = (float)(-position * length.X); + break; + } + } + } + + private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); + private MultiplierControlPoint controlPointAt(double time) + { + if (controlPoints.Count == 0) + return new MultiplierControlPoint(double.NegativeInfinity); + + if (time < controlPoints[0].StartTime) + return controlPoints[0]; + + searchPoint.StartTime = time; + int index = controlPoints.BinarySearch(searchPoint); + + if (index < 0) + index = ~index - 1; + + return controlPoints[index]; + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs index 1b7c3714d6..708a2f173b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs @@ -1,103 +1,103 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Timing; -using OpenTK; - -namespace osu.Game.Rulesets.UI.Scrolling.Visualisers -{ - public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser - { - private readonly Dictionary hitObjectPositions = new Dictionary(); - - private readonly IReadOnlyList controlPoints; - - public SequentialSpeedChangeVisualiser(IReadOnlyList controlPoints) - { - this.controlPoints = controlPoints; - } - - public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length) - { - foreach (var obj in hitObjects) - { - var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange); - - obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000; - - if (obj.HitObject is IHasEndTime endTime) - { - var diff = positionAt(endTime.EndTime, timeRange) - startPosition; - - switch (direction) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - obj.Height = (float)(diff * length.Y); - break; - case ScrollingDirection.Left: - case ScrollingDirection.Right: - obj.Width = (float)(diff * length.X); - break; - } - } - - if (obj.HasNestedHitObjects) - { - ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); - ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); - } - } - } - - public void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) - { - var timelinePosition = positionAt(currentTime, timeRange); - - foreach (var obj in hitObjects) - { - var finalPosition = hitObjectPositions[obj] - timelinePosition; - - switch (direction) - { - case ScrollingDirection.Up: - obj.Y = (float)(finalPosition * length.Y); - break; - case ScrollingDirection.Down: - obj.Y = (float)(-finalPosition * length.Y); - break; - case ScrollingDirection.Left: - obj.X = (float)(finalPosition * length.X); - break; - case ScrollingDirection.Right: - obj.X = (float)(-finalPosition * length.X); - break; - } - } - } - - private double positionAt(double time, double timeRange) - { - double length = 0; - for (int i = 0; i < controlPoints.Count; i++) - { - var current = controlPoints[i]; - var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null; - - if (i > 0 && current.StartTime > time) - continue; - - // Duration of the current control point - var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime; - - length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange; - } - - return length; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Timing; +using OpenTK; + +namespace osu.Game.Rulesets.UI.Scrolling.Visualisers +{ + public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser + { + private readonly Dictionary hitObjectPositions = new Dictionary(); + + private readonly IReadOnlyList controlPoints; + + public SequentialSpeedChangeVisualiser(IReadOnlyList controlPoints) + { + this.controlPoints = controlPoints; + } + + public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length) + { + foreach (var obj in hitObjects) + { + var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange); + + obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000; + + if (obj.HitObject is IHasEndTime endTime) + { + var diff = positionAt(endTime.EndTime, timeRange) - startPosition; + + switch (direction) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + obj.Height = (float)(diff * length.Y); + break; + case ScrollingDirection.Left: + case ScrollingDirection.Right: + obj.Width = (float)(diff * length.X); + break; + } + } + + if (obj.HasNestedHitObjects) + { + ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); + ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + } + } + } + + public void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + { + var timelinePosition = positionAt(currentTime, timeRange); + + foreach (var obj in hitObjects) + { + var finalPosition = hitObjectPositions[obj] - timelinePosition; + + switch (direction) + { + case ScrollingDirection.Up: + obj.Y = (float)(finalPosition * length.Y); + break; + case ScrollingDirection.Down: + obj.Y = (float)(-finalPosition * length.Y); + break; + case ScrollingDirection.Left: + obj.X = (float)(finalPosition * length.X); + break; + case ScrollingDirection.Right: + obj.X = (float)(-finalPosition * length.X); + break; + } + } + } + + private double positionAt(double time, double timeRange) + { + double length = 0; + for (int i = 0; i < controlPoints.Count; i++) + { + var current = controlPoints[i]; + var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null; + + if (i > 0 && current.StartTime > time) + continue; + + // Duration of the current control point + var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime; + + length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange; + } + + return length; + } + } +} diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index c5e5883b99..e7d3690f59 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -1,85 +1,85 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Threading; -using osu.Framework.Screens; -using osu.Framework.Graphics; -using osu.Framework.Input; -using OpenTK; - -namespace osu.Game.Screens -{ - public abstract class BackgroundScreen : Screen, IEquatable - { - public virtual bool Equals(BackgroundScreen other) - { - return other?.GetType() == GetType(); - } - - private const float transition_length = 500; - private const float x_movement_amount = 50; - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - //we don't want to handle escape key. - return false; - } - - public override bool Push(Screen screen) - { - // When trying to push a non-loaded screen, load it asynchronously and re-invoke Push - // once it's done. - if (screen.LoadState == LoadState.NotLoaded) - { - LoadComponentAsync(screen, d => Push((BackgroundScreen)d)); - return true; - } - - // Make sure the in-progress loading is complete before pushing the screen. - while (screen.LoadState < LoadState.Ready) - Thread.Sleep(1); - - base.Push(screen); - - return true; - } - - protected override void Update() - { - base.Update(); - Content.Scale = new Vector2(1 + x_movement_amount / DrawSize.X * 2); - } - - protected override void OnEntering(Screen last) - { - Content.FadeOut(); - Content.MoveToX(x_movement_amount); - - Content.FadeIn(transition_length, Easing.InOutQuart); - Content.MoveToX(0, transition_length, Easing.InOutQuart); - - base.OnEntering(last); - } - - protected override void OnSuspending(Screen next) - { - Content.MoveToX(-x_movement_amount, transition_length, Easing.InOutQuart); - base.OnSuspending(next); - } - - protected override bool OnExiting(Screen next) - { - Content.FadeOut(transition_length, Easing.OutExpo); - Content.MoveToX(x_movement_amount, transition_length, Easing.OutExpo); - - return base.OnExiting(next); - } - - protected override void OnResuming(Screen last) - { - Content.MoveToX(0, transition_length, Easing.OutExpo); - base.OnResuming(last); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Threading; +using osu.Framework.Screens; +using osu.Framework.Graphics; +using osu.Framework.Input; +using OpenTK; + +namespace osu.Game.Screens +{ + public abstract class BackgroundScreen : Screen, IEquatable + { + public virtual bool Equals(BackgroundScreen other) + { + return other?.GetType() == GetType(); + } + + private const float transition_length = 500; + private const float x_movement_amount = 50; + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + //we don't want to handle escape key. + return false; + } + + public override bool Push(Screen screen) + { + // When trying to push a non-loaded screen, load it asynchronously and re-invoke Push + // once it's done. + if (screen.LoadState == LoadState.NotLoaded) + { + LoadComponentAsync(screen, d => Push((BackgroundScreen)d)); + return true; + } + + // Make sure the in-progress loading is complete before pushing the screen. + while (screen.LoadState < LoadState.Ready) + Thread.Sleep(1); + + base.Push(screen); + + return true; + } + + protected override void Update() + { + base.Update(); + Content.Scale = new Vector2(1 + x_movement_amount / DrawSize.X * 2); + } + + protected override void OnEntering(Screen last) + { + Content.FadeOut(); + Content.MoveToX(x_movement_amount); + + Content.FadeIn(transition_length, Easing.InOutQuart); + Content.MoveToX(0, transition_length, Easing.InOutQuart); + + base.OnEntering(last); + } + + protected override void OnSuspending(Screen next) + { + Content.MoveToX(-x_movement_amount, transition_length, Easing.InOutQuart); + base.OnSuspending(next); + } + + protected override bool OnExiting(Screen next) + { + Content.FadeOut(transition_length, Easing.OutExpo); + Content.MoveToX(x_movement_amount, transition_length, Easing.OutExpo); + + return base.OnExiting(next); + } + + protected override void OnResuming(Screen last) + { + Content.MoveToX(0, transition_length, Easing.OutExpo); + base.OnResuming(last); + } + } +} diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 1ce84289b2..78561cecbf 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -1,84 +1,84 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Textures; -using osu.Framework.Graphics.Transforms; -using OpenTK; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Backgrounds; - -namespace osu.Game.Screens.Backgrounds -{ - public class BackgroundScreenBeatmap : BackgroundScreen - { - private Background background; - - private WorkingBeatmap beatmap; - private Vector2 blurTarget; - - public WorkingBeatmap Beatmap - { - get { return beatmap; } - set - { - if (beatmap == value && beatmap != null) - return; - - beatmap = value; - - Schedule(() => - { - LoadComponentAsync(new BeatmapBackground(beatmap), b => - { - float newDepth = 0; - if (background != null) - { - newDepth = background.Depth + 1; - background.FinishTransforms(); - background.FadeOut(250); - background.Expire(); - } - - b.Depth = newDepth; - Add(background = b); - background.BlurSigma = blurTarget; - }); - }); - } - } - - public BackgroundScreenBeatmap(WorkingBeatmap beatmap = null) - { - Beatmap = beatmap; - } - - public TransformSequence BlurTo(Vector2 sigma, double duration, Easing easing = Easing.None) - => background?.BlurTo(blurTarget = sigma, duration, easing); - - public override bool Equals(BackgroundScreen other) - { - var otherBeatmapBackground = other as BackgroundScreenBeatmap; - if (otherBeatmapBackground == null) return false; - - return base.Equals(other) && beatmap == otherBeatmapBackground.Beatmap; - } - - private class BeatmapBackground : Background - { - private readonly WorkingBeatmap beatmap; - - public BeatmapBackground(WorkingBeatmap beatmap) - { - this.beatmap = beatmap; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - Sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg1"); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Transforms; +using OpenTK; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Backgrounds; + +namespace osu.Game.Screens.Backgrounds +{ + public class BackgroundScreenBeatmap : BackgroundScreen + { + private Background background; + + private WorkingBeatmap beatmap; + private Vector2 blurTarget; + + public WorkingBeatmap Beatmap + { + get { return beatmap; } + set + { + if (beatmap == value && beatmap != null) + return; + + beatmap = value; + + Schedule(() => + { + LoadComponentAsync(new BeatmapBackground(beatmap), b => + { + float newDepth = 0; + if (background != null) + { + newDepth = background.Depth + 1; + background.FinishTransforms(); + background.FadeOut(250); + background.Expire(); + } + + b.Depth = newDepth; + Add(background = b); + background.BlurSigma = blurTarget; + }); + }); + } + } + + public BackgroundScreenBeatmap(WorkingBeatmap beatmap = null) + { + Beatmap = beatmap; + } + + public TransformSequence BlurTo(Vector2 sigma, double duration, Easing easing = Easing.None) + => background?.BlurTo(blurTarget = sigma, duration, easing); + + public override bool Equals(BackgroundScreen other) + { + var otherBeatmapBackground = other as BackgroundScreenBeatmap; + if (otherBeatmapBackground == null) return false; + + return base.Equals(other) && beatmap == otherBeatmapBackground.Beatmap; + } + + private class BeatmapBackground : Background + { + private readonly WorkingBeatmap beatmap; + + public BeatmapBackground(WorkingBeatmap beatmap) + { + this.beatmap = beatmap; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg1"); + } + } + } +} diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs index 629f5ea3db..041391db1e 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs @@ -1,26 +1,26 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics.Backgrounds; - -namespace osu.Game.Screens.Backgrounds -{ - public class BackgroundScreenCustom : BackgroundScreen - { - private readonly string textureName; - - public BackgroundScreenCustom(string textureName) - { - this.textureName = textureName; - Add(new Background(textureName)); - } - - public override bool Equals(BackgroundScreen other) - { - var backgroundScreenCustom = other as BackgroundScreenCustom; - if (backgroundScreenCustom == null) return false; - - return base.Equals(other) && textureName == backgroundScreenCustom.textureName; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics.Backgrounds; + +namespace osu.Game.Screens.Backgrounds +{ + public class BackgroundScreenCustom : BackgroundScreen + { + private readonly string textureName; + + public BackgroundScreenCustom(string textureName) + { + this.textureName = textureName; + Add(new Background(textureName)); + } + + public override bool Equals(BackgroundScreen other) + { + var backgroundScreenCustom = other as BackgroundScreenCustom; + if (backgroundScreenCustom == null) return false; + + return base.Equals(other) && textureName == backgroundScreenCustom.textureName; + } + } +} diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 24e744d234..38df9b13ef 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Threading; -using osu.Game.Graphics.Backgrounds; - -namespace osu.Game.Screens.Backgrounds -{ - public class BackgroundScreenDefault : BackgroundScreen - { - private int currentDisplay; - private const int background_count = 5; - - private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}"; - - private Background current; - - [BackgroundDependencyLoader] - private void load() - { - display(new Background(backgroundName)); - } - - private void display(Background newBackground) - { - current?.FadeOut(800, Easing.InOutSine); - current?.Expire(); - - Add(current = newBackground); - currentDisplay++; - } - - private ScheduledDelegate nextTask; - - public void Next() - { - nextTask?.Cancel(); - nextTask = Scheduler.AddDelayed(() => - { - LoadComponentAsync(new Background(backgroundName) { Depth = currentDisplay }, display); - }, 100); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Threading; +using osu.Game.Graphics.Backgrounds; + +namespace osu.Game.Screens.Backgrounds +{ + public class BackgroundScreenDefault : BackgroundScreen + { + private int currentDisplay; + private const int background_count = 5; + + private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}"; + + private Background current; + + [BackgroundDependencyLoader] + private void load() + { + display(new Background(backgroundName)); + } + + private void display(Background newBackground) + { + current?.FadeOut(800, Easing.InOutSine); + current?.Expire(); + + Add(current = newBackground); + currentDisplay++; + } + + private ScheduledDelegate nextTask; + + public void Next() + { + nextTask?.Cancel(); + nextTask = Scheduler.AddDelayed(() => + { + LoadComponentAsync(new Background(backgroundName) { Depth = currentDisplay }, display); + }, 100); + } + } +} diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenEmpty.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenEmpty.cs index 758032a711..5e08db8907 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenEmpty.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenEmpty.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Screens.Backgrounds -{ - public class BackgroundScreenEmpty : BackgroundScreen - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Backgrounds +{ + public class BackgroundScreenEmpty : BackgroundScreen + { + } +} diff --git a/osu.Game/Screens/Charts/ChartInfo.cs b/osu.Game/Screens/Charts/ChartInfo.cs index 70ca3b8b67..35021709d6 100644 --- a/osu.Game/Screens/Charts/ChartInfo.cs +++ b/osu.Game/Screens/Charts/ChartInfo.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Screens.Charts -{ - public class ChartInfo : ScreenWhiteBox - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Charts +{ + public class ChartInfo : ScreenWhiteBox + { + } +} diff --git a/osu.Game/Screens/Charts/ChartListing.cs b/osu.Game/Screens/Charts/ChartListing.cs index 376cc76ef5..a618bba8c4 100644 --- a/osu.Game/Screens/Charts/ChartListing.cs +++ b/osu.Game/Screens/Charts/ChartListing.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; - -namespace osu.Game.Screens.Charts -{ - public class ChartListing : ScreenWhiteBox - { - protected override IEnumerable PossibleChildren => new[] { - typeof(ChartInfo) - }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; + +namespace osu.Game.Screens.Charts +{ + public class ChartListing : ScreenWhiteBox + { + protected override IEnumerable PossibleChildren => new[] { + typeof(ChartInfo) + }; + } +} diff --git a/osu.Game/Screens/Direct/OnlineListing.cs b/osu.Game/Screens/Direct/OnlineListing.cs index 38f4d6a771..9d2d09a695 100644 --- a/osu.Game/Screens/Direct/OnlineListing.cs +++ b/osu.Game/Screens/Direct/OnlineListing.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Screens.Direct -{ - public class OnlineListing : ScreenWhiteBox - { - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Direct +{ + public class OnlineListing : ScreenWhiteBox + { + } +} diff --git a/osu.Game/Screens/Edit/Components/BottomBarContainer.cs b/osu.Game/Screens/Edit/Components/BottomBarContainer.cs index 69a1296701..399f9274a6 100644 --- a/osu.Game/Screens/Edit/Components/BottomBarContainer.cs +++ b/osu.Game/Screens/Edit/Components/BottomBarContainer.cs @@ -1,50 +1,50 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Audio.Track; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Graphics; - -namespace osu.Game.Screens.Edit.Components -{ - public class BottomBarContainer : Container - { - private const float corner_radius = 5; - private const float contents_padding = 15; - - public readonly Bindable Beatmap = new Bindable(); - protected Track Track => Beatmap.Value.Track; - - private readonly Drawable background; - private readonly Container content; - - protected override Container Content => content; - - public BottomBarContainer() - { - Masking = true; - CornerRadius = corner_radius; - - InternalChildren = new[] - { - background = new Box { RelativeSizeAxes = Axes.Both }, - content = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = contents_padding }, - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.Gray1; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Edit.Components +{ + public class BottomBarContainer : Container + { + private const float corner_radius = 5; + private const float contents_padding = 15; + + public readonly Bindable Beatmap = new Bindable(); + protected Track Track => Beatmap.Value.Track; + + private readonly Drawable background; + private readonly Container content; + + protected override Container Content => content; + + public BottomBarContainer() + { + Masking = true; + CornerRadius = corner_radius; + + InternalChildren = new[] + { + background = new Box { RelativeSizeAxes = Axes.Both }, + content = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = contents_padding }, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.Gray1; + } + } +} diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index fe2549d300..5f62843faf 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -1,160 +1,160 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Framework.Timing; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Screens.Edit.Components -{ - public class PlaybackControl : BottomBarContainer - { - private IconButton playButton; - - private IAdjustableClock adjustableClock; - - [BackgroundDependencyLoader] - private void load(IAdjustableClock adjustableClock) - { - this.adjustableClock = adjustableClock; - - PlaybackTabControl tabs; - - Children = new Drawable[] - { - playButton = new IconButton - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - Scale = new Vector2(1.4f), - IconScale = new Vector2(1.4f), - Icon = FontAwesome.fa_play_circle_o, - Action = togglePause, - Padding = new MarginPadding { Left = 20 } - }, - new OsuSpriteText - { - Origin = Anchor.BottomLeft, - Text = "Playback Speed", - RelativePositionAxes = Axes.Y, - Y = 0.5f, - Padding = new MarginPadding { Left = 45 } - }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Height = 0.5f, - Padding = new MarginPadding { Left = 45 }, - Child = tabs = new PlaybackTabControl(), - } - }; - - tabs.Current.ValueChanged += newValue => Beatmap.Value.Track.Tempo.Value = newValue; - } - - private void togglePause() - { - if (adjustableClock.IsRunning) - adjustableClock.Stop(); - else - adjustableClock.Start(); - } - - protected override void Update() - { - base.Update(); - - playButton.Icon = adjustableClock.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; - } - - private class PlaybackTabControl : OsuTabControl - { - private static readonly double[] tempo_values = { 0.5, 0.75, 1 }; - - protected override TabItem CreateTabItem(double value) => new PlaybackTabItem(value); - - protected override Dropdown CreateDropdown() => null; - - public PlaybackTabControl() - { - RelativeSizeAxes = Axes.Both; - TabContainer.Spacing = Vector2.Zero; - - tempo_values.ForEach(AddItem); - } - - public class PlaybackTabItem : TabItem - { - private const float fade_duration = 200; - - private readonly OsuSpriteText text; - private readonly OsuSpriteText textBold; - - public PlaybackTabItem(double value) : base(value) - { - RelativeSizeAxes = Axes.Both; - - Width = 1f / tempo_values.Length; - - Children = new Drawable[] - { - text = new OsuSpriteText - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Text = $"{value:0%}", - TextSize = 14, - }, - textBold = new OsuSpriteText - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Text = $"{value:0%}", - TextSize = 14, - Font = @"Exo2.0-Bold", - Alpha = 0, - }, - }; - } - - private Color4 hoveredColour; - private Color4 normalColour; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - text.Colour = normalColour = colours.YellowDarker; - textBold.Colour = hoveredColour = colours.Yellow; - } - - protected override bool OnHover(InputState state) - { - updateState(); - return true; - } - - protected override void OnHoverLost(InputState state) => updateState(); - protected override void OnActivated() => updateState(); - protected override void OnDeactivated() => updateState(); - - private void updateState() - { - text.FadeColour(Active || IsHovered ? hoveredColour : normalColour, fade_duration, Easing.OutQuint); - text.FadeTo(Active ? 0 : 1, fade_duration, Easing.OutQuint); - textBold.FadeTo(Active ? 1 : 0, fade_duration, Easing.OutQuint); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Framework.Timing; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Components +{ + public class PlaybackControl : BottomBarContainer + { + private IconButton playButton; + + private IAdjustableClock adjustableClock; + + [BackgroundDependencyLoader] + private void load(IAdjustableClock adjustableClock) + { + this.adjustableClock = adjustableClock; + + PlaybackTabControl tabs; + + Children = new Drawable[] + { + playButton = new IconButton + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Scale = new Vector2(1.4f), + IconScale = new Vector2(1.4f), + Icon = FontAwesome.fa_play_circle_o, + Action = togglePause, + Padding = new MarginPadding { Left = 20 } + }, + new OsuSpriteText + { + Origin = Anchor.BottomLeft, + Text = "Playback Speed", + RelativePositionAxes = Axes.Y, + Y = 0.5f, + Padding = new MarginPadding { Left = 45 } + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Padding = new MarginPadding { Left = 45 }, + Child = tabs = new PlaybackTabControl(), + } + }; + + tabs.Current.ValueChanged += newValue => Beatmap.Value.Track.Tempo.Value = newValue; + } + + private void togglePause() + { + if (adjustableClock.IsRunning) + adjustableClock.Stop(); + else + adjustableClock.Start(); + } + + protected override void Update() + { + base.Update(); + + playButton.Icon = adjustableClock.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; + } + + private class PlaybackTabControl : OsuTabControl + { + private static readonly double[] tempo_values = { 0.5, 0.75, 1 }; + + protected override TabItem CreateTabItem(double value) => new PlaybackTabItem(value); + + protected override Dropdown CreateDropdown() => null; + + public PlaybackTabControl() + { + RelativeSizeAxes = Axes.Both; + TabContainer.Spacing = Vector2.Zero; + + tempo_values.ForEach(AddItem); + } + + public class PlaybackTabItem : TabItem + { + private const float fade_duration = 200; + + private readonly OsuSpriteText text; + private readonly OsuSpriteText textBold; + + public PlaybackTabItem(double value) : base(value) + { + RelativeSizeAxes = Axes.Both; + + Width = 1f / tempo_values.Length; + + Children = new Drawable[] + { + text = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Text = $"{value:0%}", + TextSize = 14, + }, + textBold = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Text = $"{value:0%}", + TextSize = 14, + Font = @"Exo2.0-Bold", + Alpha = 0, + }, + }; + } + + private Color4 hoveredColour; + private Color4 normalColour; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + text.Colour = normalColour = colours.YellowDarker; + textBold.Colour = hoveredColour = colours.Yellow; + } + + protected override bool OnHover(InputState state) + { + updateState(); + return true; + } + + protected override void OnHoverLost(InputState state) => updateState(); + protected override void OnActivated() => updateState(); + protected override void OnDeactivated() => updateState(); + + private void updateState() + { + text.FadeColour(Active || IsHovered ? hoveredColour : normalColour, fade_duration, Easing.OutQuint); + text.FadeTo(Active ? 0 : 1, fade_duration, Easing.OutQuint); + textBold.FadeTo(Active ? 1 : 0, fade_duration, Easing.OutQuint); + } + } + } + } +} diff --git a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs index 5a3b6c652b..129ea2bf7d 100644 --- a/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs +++ b/osu.Game/Screens/Edit/Components/TimeInfoContainer.cs @@ -1,47 +1,47 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Game.Graphics.Sprites; -using System; -using osu.Framework.Allocation; -using osu.Framework.Timing; - -namespace osu.Game.Screens.Edit.Components -{ - public class TimeInfoContainer : BottomBarContainer - { - private readonly OsuSpriteText trackTimer; - - private IAdjustableClock adjustableClock; - - public TimeInfoContainer() - { - - Children = new Drawable[] - { - trackTimer = new OsuSpriteText - { - Origin = Anchor.BottomLeft, - RelativePositionAxes = Axes.Y, - TextSize = 22, - FixedWidth = true, - Y = 0.5f, - } - }; - } - - [BackgroundDependencyLoader] - private void load(IAdjustableClock adjustableClock) - { - this.adjustableClock = adjustableClock; - } - - protected override void Update() - { - base.Update(); - - trackTimer.Text = TimeSpan.FromMilliseconds(adjustableClock.CurrentTime).ToString(@"mm\:ss\:fff"); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Graphics.Sprites; +using System; +using osu.Framework.Allocation; +using osu.Framework.Timing; + +namespace osu.Game.Screens.Edit.Components +{ + public class TimeInfoContainer : BottomBarContainer + { + private readonly OsuSpriteText trackTimer; + + private IAdjustableClock adjustableClock; + + public TimeInfoContainer() + { + + Children = new Drawable[] + { + trackTimer = new OsuSpriteText + { + Origin = Anchor.BottomLeft, + RelativePositionAxes = Axes.Y, + TextSize = 22, + FixedWidth = true, + Y = 0.5f, + } + }; + } + + [BackgroundDependencyLoader] + private void load(IAdjustableClock adjustableClock) + { + this.adjustableClock = adjustableClock; + } + + protected override void Update() + { + base.Update(); + + trackTimer.Text = TimeSpan.FromMilliseconds(adjustableClock.CurrentTime).ToString(@"mm\:ss\:fff"); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BookmarkPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BookmarkPart.cs index dfb67e4228..3af3fcbdd7 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BookmarkPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BookmarkPart.cs @@ -1,34 +1,34 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; - -namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts -{ - /// - /// The part of the timeline that displays bookmarks. - /// - public class BookmarkPart : TimelinePart - { - protected override void LoadBeatmap(WorkingBeatmap beatmap) - { - base.LoadBeatmap(beatmap); - foreach (int bookmark in beatmap.BeatmapInfo.Bookmarks) - Add(new BookmarkVisualisation(bookmark)); - } - - private class BookmarkVisualisation : PointVisualisation - { - public BookmarkVisualisation(double startTime) - : base(startTime) - { - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) => Colour = colours.Blue; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + /// + /// The part of the timeline that displays bookmarks. + /// + public class BookmarkPart : TimelinePart + { + protected override void LoadBeatmap(WorkingBeatmap beatmap) + { + base.LoadBeatmap(beatmap); + foreach (int bookmark in beatmap.BeatmapInfo.Bookmarks) + Add(new BookmarkVisualisation(bookmark)); + } + + private class BookmarkVisualisation : PointVisualisation + { + public BookmarkVisualisation(double startTime) + : base(startTime) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) => Colour = colours.Blue; + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs index 29cee20a9b..1146037004 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs @@ -1,35 +1,35 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; -using osu.Game.Graphics; -using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; - -namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts -{ - /// - /// The part of the timeline that displays breaks in the song. - /// - public class BreakPart : TimelinePart - { - protected override void LoadBeatmap(WorkingBeatmap beatmap) - { - base.LoadBeatmap(beatmap); - foreach (var breakPeriod in beatmap.Beatmap.Breaks) - Add(new BreakVisualisation(breakPeriod)); - } - - private class BreakVisualisation : DurationVisualisation - { - public BreakVisualisation(BreakPeriod breakPeriod) - : base(breakPeriod.StartTime, breakPeriod.EndTime) - { - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) => Colour = colours.Yellow; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + /// + /// The part of the timeline that displays breaks in the song. + /// + public class BreakPart : TimelinePart + { + protected override void LoadBeatmap(WorkingBeatmap beatmap) + { + base.LoadBeatmap(beatmap); + foreach (var breakPeriod in beatmap.Beatmap.Breaks) + Add(new BreakVisualisation(breakPeriod)); + } + + private class BreakVisualisation : DurationVisualisation + { + public BreakVisualisation(BreakPeriod breakPeriod) + : base(breakPeriod.StartTime, breakPeriod.EndTime) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) => Colour = colours.Yellow; + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs index a8e62d77ad..4bef22463e 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs @@ -1,70 +1,70 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Graphics; -using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; - -namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts -{ - /// - /// The part of the timeline that displays the control points. - /// - public class ControlPointPart : TimelinePart - { - protected override void LoadBeatmap(WorkingBeatmap beatmap) - { - base.LoadBeatmap(beatmap); - - ControlPointInfo cpi = beatmap.Beatmap.ControlPointInfo; - - cpi.TimingPoints.ForEach(addTimingPoint); - - // Consider all non-timing points as the same type - cpi.SamplePoints.Select(c => (ControlPoint)c) - .Concat(cpi.EffectPoints) - .Concat(cpi.DifficultyPoints) - .Distinct() - // Non-timing points should not be added where there are timing points - .Where(c => cpi.TimingPointAt(c.Time).Time != c.Time) - .ForEach(addNonTimingPoint); - } - - private void addTimingPoint(ControlPoint controlPoint) => Add(new TimingPointVisualisation(controlPoint)); - private void addNonTimingPoint(ControlPoint controlPoint) => Add(new NonTimingPointVisualisation(controlPoint)); - - private class TimingPointVisualisation : ControlPointVisualisation - { - public TimingPointVisualisation(ControlPoint controlPoint) - : base(controlPoint) - { - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) => Colour = colours.YellowDark; - } - - private class NonTimingPointVisualisation : ControlPointVisualisation - { - public NonTimingPointVisualisation(ControlPoint controlPoint) - : base(controlPoint) - { - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) => Colour = colours.Green; - } - - private abstract class ControlPointVisualisation : PointVisualisation - { - protected ControlPointVisualisation(ControlPoint controlPoint) - : base(controlPoint.Time) - { - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + /// + /// The part of the timeline that displays the control points. + /// + public class ControlPointPart : TimelinePart + { + protected override void LoadBeatmap(WorkingBeatmap beatmap) + { + base.LoadBeatmap(beatmap); + + ControlPointInfo cpi = beatmap.Beatmap.ControlPointInfo; + + cpi.TimingPoints.ForEach(addTimingPoint); + + // Consider all non-timing points as the same type + cpi.SamplePoints.Select(c => (ControlPoint)c) + .Concat(cpi.EffectPoints) + .Concat(cpi.DifficultyPoints) + .Distinct() + // Non-timing points should not be added where there are timing points + .Where(c => cpi.TimingPointAt(c.Time).Time != c.Time) + .ForEach(addNonTimingPoint); + } + + private void addTimingPoint(ControlPoint controlPoint) => Add(new TimingPointVisualisation(controlPoint)); + private void addNonTimingPoint(ControlPoint controlPoint) => Add(new NonTimingPointVisualisation(controlPoint)); + + private class TimingPointVisualisation : ControlPointVisualisation + { + public TimingPointVisualisation(ControlPoint controlPoint) + : base(controlPoint) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) => Colour = colours.YellowDark; + } + + private class NonTimingPointVisualisation : ControlPointVisualisation + { + public NonTimingPointVisualisation(ControlPoint controlPoint) + : base(controlPoint) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) => Colour = colours.Green; + } + + private abstract class ControlPointVisualisation : PointVisualisation + { + protected ControlPointVisualisation(ControlPoint controlPoint) + : base(controlPoint.Time) + { + } + } + } +} 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 9efe93c5a7..d1fc8be005 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -1,111 +1,111 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Framework.Timing; -using osu.Game.Beatmaps; -using osu.Game.Graphics; - -namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts -{ - /// - /// The part of the timeline that displays the current position of the song. - /// - public class MarkerPart : TimelinePart - { - private readonly Drawable marker; - - private readonly IAdjustableClock adjustableClock; - - public MarkerPart(IAdjustableClock adjustableClock) - { - this.adjustableClock = adjustableClock; - - Add(marker = new MarkerVisualisation()); - } - - protected override bool OnDragStart(InputState state) => true; - protected override bool OnDragEnd(InputState state) => true; - protected override bool OnDrag(InputState state) - { - seekToPosition(state.Mouse.NativeState.Position); - return true; - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - seekToPosition(state.Mouse.NativeState.Position); - return true; - } - - /// - /// Seeks the to the time closest to a position on the screen relative to the . - /// - /// The position in screen coordinates. - private void seekToPosition(Vector2 screenPosition) - { - if (Beatmap.Value == null) - return; - - if (Beatmap.Value.Track.Length == double.PositiveInfinity) return; - - float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); - adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length); - } - - protected override void Update() - { - base.Update(); - marker.X = (float)adjustableClock.CurrentTime; - } - - protected override void LoadBeatmap(WorkingBeatmap beatmap) - { - // block base call so we don't clear our marker (can be reused on beatmap change). - } - - private class MarkerVisualisation : CompositeDrawable - { - public MarkerVisualisation() - { - Anchor = Anchor.CentreLeft; - Origin = Anchor.Centre; - RelativePositionAxes = Axes.X; - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; - InternalChildren = new Drawable[] - { - new Triangle - { - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - Scale = new Vector2(1, -1), - Size = new Vector2(10, 5), - }, - new Triangle - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(10, 5) - }, - new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Width = 2, - EdgeSmoothness = new Vector2(1, 0) - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) => Colour = colours.Red; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + /// + /// The part of the timeline that displays the current position of the song. + /// + public class MarkerPart : TimelinePart + { + private readonly Drawable marker; + + private readonly IAdjustableClock adjustableClock; + + public MarkerPart(IAdjustableClock adjustableClock) + { + this.adjustableClock = adjustableClock; + + Add(marker = new MarkerVisualisation()); + } + + protected override bool OnDragStart(InputState state) => true; + protected override bool OnDragEnd(InputState state) => true; + protected override bool OnDrag(InputState state) + { + seekToPosition(state.Mouse.NativeState.Position); + return true; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + seekToPosition(state.Mouse.NativeState.Position); + return true; + } + + /// + /// Seeks the to the time closest to a position on the screen relative to the . + /// + /// The position in screen coordinates. + private void seekToPosition(Vector2 screenPosition) + { + if (Beatmap.Value == null) + return; + + if (Beatmap.Value.Track.Length == double.PositiveInfinity) return; + + float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); + adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length); + } + + protected override void Update() + { + base.Update(); + marker.X = (float)adjustableClock.CurrentTime; + } + + protected override void LoadBeatmap(WorkingBeatmap beatmap) + { + // block base call so we don't clear our marker (can be reused on beatmap change). + } + + private class MarkerVisualisation : CompositeDrawable + { + public MarkerVisualisation() + { + Anchor = Anchor.CentreLeft; + Origin = Anchor.Centre; + RelativePositionAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; + InternalChildren = new Drawable[] + { + new Triangle + { + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + Scale = new Vector2(1, -1), + Size = new Vector2(10, 5), + }, + new Triangle + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(10, 5) + }, + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Width = 2, + EdgeSmoothness = new Vector2(1, 0) + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) => Colour = colours.Red; + } + } +} 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 c3c8dd9de0..c00e9ac4d5 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -1,54 +1,54 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; - -namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts -{ - /// - /// Represents a part of the summary timeline.. - /// - public abstract class TimelinePart : CompositeDrawable - { - public Bindable Beatmap = new Bindable(); - - private readonly Container timeline; - - protected TimelinePart() - { - AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both }); - - Beatmap.ValueChanged += b => - { - updateRelativeChildSize(); - LoadBeatmap(b); - }; - } - - private void updateRelativeChildSize() - { - // the track may not be loaded completely (only has a length once it is). - if (!Beatmap.Value.Track.IsLoaded) - { - timeline.RelativeChildSize = Vector2.One; - Schedule(updateRelativeChildSize); - return; - } - - // Todo: This should be handled more gracefully - timeline.RelativeChildSize = Beatmap.Value.Track.Length == double.PositiveInfinity ? Vector2.One : new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); - } - - protected void Add(Drawable visualisation) => timeline.Add(visualisation); - - protected virtual void LoadBeatmap(WorkingBeatmap beatmap) - { - timeline.Clear(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + /// + /// Represents a part of the summary timeline.. + /// + public abstract class TimelinePart : CompositeDrawable + { + public Bindable Beatmap = new Bindable(); + + private readonly Container timeline; + + protected TimelinePart() + { + AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both }); + + Beatmap.ValueChanged += b => + { + updateRelativeChildSize(); + LoadBeatmap(b); + }; + } + + private void updateRelativeChildSize() + { + // the track may not be loaded completely (only has a length once it is). + if (!Beatmap.Value.Track.IsLoaded) + { + timeline.RelativeChildSize = Vector2.One; + Schedule(updateRelativeChildSize); + return; + } + + // Todo: This should be handled more gracefully + timeline.RelativeChildSize = Beatmap.Value.Track.Length == double.PositiveInfinity ? Vector2.One : new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); + } + + protected void Add(Drawable visualisation) => timeline.Add(visualisation); + + protected virtual void LoadBeatmap(WorkingBeatmap beatmap) + { + timeline.Clear(); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index 0e80c13257..0301870588 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -1,85 +1,85 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; -using osu.Game.Graphics; -using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; - -namespace osu.Game.Screens.Edit.Components.Timelines.Summary -{ - /// - /// The timeline that sits at the bottom of the editor. - /// - public class SummaryTimeline : BottomBarContainer - { - [BackgroundDependencyLoader] - private void load(OsuColour colours, IAdjustableClock adjustableClock) - { - TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart; - - Children = new Drawable[] - { - markerPart = new MarkerPart(adjustableClock) { RelativeSizeAxes = Axes.Both }, - controlPointPart = new ControlPointPart - { - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.Both, - Height = 0.35f - }, - bookmarkPart = new BookmarkPart - { - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - Height = 0.35f - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray5, - Children = new Drawable[] - { - new Circle - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, - Size = new Vector2(5) - }, - new Box - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - Height = 1, - EdgeSmoothness = new Vector2(0, 1), - }, - new Circle - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreLeft, - Size = new Vector2(5) - }, - } - }, - breakPart = new BreakPart - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Height = 0.25f - } - }; - - markerPart.Beatmap.BindTo(Beatmap); - controlPointPart.Beatmap.BindTo(Beatmap); - bookmarkPart.Beatmap.BindTo(Beatmap); - breakPart.Beatmap.BindTo(Beatmap); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary +{ + /// + /// The timeline that sits at the bottom of the editor. + /// + public class SummaryTimeline : BottomBarContainer + { + [BackgroundDependencyLoader] + private void load(OsuColour colours, IAdjustableClock adjustableClock) + { + TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart; + + Children = new Drawable[] + { + markerPart = new MarkerPart(adjustableClock) { RelativeSizeAxes = Axes.Both }, + controlPointPart = new ControlPointPart + { + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + Height = 0.35f + }, + bookmarkPart = new BookmarkPart + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + Height = 0.35f + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray5, + Children = new Drawable[] + { + new Circle + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreRight, + Size = new Vector2(5) + }, + new Box + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Height = 1, + EdgeSmoothness = new Vector2(0, 1), + }, + new Circle + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreLeft, + Size = new Vector2(5) + }, + } + }, + breakPart = new BreakPart + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Height = 0.25f + } + }; + + markerPart.Beatmap.BindTo(Beatmap); + controlPointPart.Beatmap.BindTo(Beatmap); + bookmarkPart.Beatmap.BindTo(Beatmap); + breakPart.Beatmap.BindTo(Beatmap); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs index f8d3133ae9..0b5c6018e8 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs @@ -1,28 +1,28 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations -{ - /// - /// Represents a spanning point on a timeline part. - /// - public class DurationVisualisation : Container - { - protected DurationVisualisation(double startTime, double endTime) - { - Masking = true; - CornerRadius = 5; - - RelativePositionAxes = Axes.X; - RelativeSizeAxes = Axes.Both; - X = (float)startTime; - Width = (float)(endTime - startTime); - - AddInternal(new Box { RelativeSizeAxes = Axes.Both }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations +{ + /// + /// Represents a spanning point on a timeline part. + /// + public class DurationVisualisation : Container + { + protected DurationVisualisation(double startTime, double endTime) + { + Masking = true; + CornerRadius = 5; + + RelativePositionAxes = Axes.X; + RelativeSizeAxes = Axes.Both; + X = (float)startTime; + Width = (float)(endTime - startTime); + + AddInternal(new Box { RelativeSizeAxes = Axes.Both }); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index 4271375740..003d969ab8 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -1,27 +1,27 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; - -namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations -{ - /// - /// Represents a singular point on a timeline part. - /// - public class PointVisualisation : Box - { - protected PointVisualisation(double startTime) - { - Origin = Anchor.TopCentre; - - RelativeSizeAxes = Axes.Y; - Width = 1; - EdgeSmoothness = new Vector2(1, 0); - - RelativePositionAxes = Axes.X; - X = (float)startTime; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations +{ + /// + /// Represents a singular point on a timeline part. + /// + public class PointVisualisation : Box + { + protected PointVisualisation(double startTime) + { + Origin = Anchor.TopCentre; + + RelativeSizeAxes = Axes.Y; + Width = 1; + EdgeSmoothness = new Vector2(1, 0); + + RelativePositionAxes = Axes.X; + X = (float)startTime; + } + } +} diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index e6edc9a6ff..ea1d85bb5b 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1,217 +1,217 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Screens; -using osu.Game.Screens.Backgrounds; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Screens.Edit.Menus; -using osu.Game.Screens.Edit.Components.Timelines.Summary; -using osu.Framework.Allocation; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Framework.Timing; -using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Edit.Screens; -using osu.Game.Screens.Edit.Screens.Compose; -using osu.Game.Screens.Edit.Screens.Design; -using osu.Game.Screens.Edit.Components; - -namespace osu.Game.Screens.Edit -{ - public class Editor : OsuScreen - { - protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); - - public override bool ShowOverlaysOnEnter => false; - - private Box bottomBackground; - private Container screenContainer; - - private EditorScreen currentScreen; - - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - - private EditorClock clock; - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(parent); - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - // TODO: should probably be done at a RulesetContainer level to share logic with Player. - var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); - clock = new EditorClock(Beatmap.Value.Beatmap.ControlPointInfo, beatDivisor) { IsCoupled = false }; - clock.ChangeSource(sourceClock); - - dependencies.CacheAs(clock); - dependencies.CacheAs(clock); - dependencies.Cache(beatDivisor); - - EditorMenuBar menuBar; - TimeInfoContainer timeInfo; - SummaryTimeline timeline; - PlaybackControl playback; - - Children = new[] - { - new Container - { - Name = "Screen container", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 40, Bottom = 60 }, - Child = screenContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true - } - }, - new Container - { - Name = "Top bar", - RelativeSizeAxes = Axes.X, - Height = 40, - Child = menuBar = new EditorMenuBar - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Items = new[] - { - new MenuItem("File") - { - Items = new[] - { - new EditorMenuItem("Export", MenuItemType.Standard, exportBeatmap), - new EditorMenuItemSpacer(), - new EditorMenuItem("Exit", MenuItemType.Standard, Exit) - } - } - } - } - }, - new Container - { - Name = "Bottom bar", - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 60, - Children = new Drawable[] - { - bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Vertical = 5, Horizontal = 10 }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 220), - new Dimension(), - new Dimension(GridSizeMode.Absolute, 220) - }, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 10 }, - Child = timeInfo = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, - }, - timeline = new SummaryTimeline - { - RelativeSizeAxes = Axes.Both, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 10 }, - Child = playback = new PlaybackControl { RelativeSizeAxes = Axes.Both }, - } - }, - } - }, - } - } - }, - }; - - timeInfo.Beatmap.BindTo(Beatmap); - timeline.Beatmap.BindTo(Beatmap); - playback.Beatmap.BindTo(Beatmap); - menuBar.Mode.ValueChanged += onModeChanged; - - bottomBackground.Colour = colours.Gray2; - } - - private void exportBeatmap() - { - Beatmap.Value.Save(); - } - - private void onModeChanged(EditorScreenMode mode) - { - currentScreen?.Exit(); - - switch (mode) - { - case EditorScreenMode.Compose: - currentScreen = new Compose(); - break; - case EditorScreenMode.Design: - currentScreen = new Design(); - break; - default: - currentScreen = new EditorScreen(); - break; - } - - currentScreen.Beatmap.BindTo(Beatmap); - screenContainer.Add(currentScreen); - } - - protected override bool OnWheel(InputState state) - { - if (state.Mouse.WheelDelta > 0) - clock.SeekBackward(true); - else - clock.SeekForward(true); - return true; - } - - protected override void OnResuming(Screen last) - { - Beatmap.Value.Track?.Stop(); - base.OnResuming(last); - } - - protected override void OnEntering(Screen last) - { - base.OnEntering(last); - Background.FadeColour(Color4.DarkGray, 500); - Beatmap.Value.Track?.Stop(); - } - - protected override bool OnExiting(Screen next) - { - Background.FadeColour(Color4.White, 500); - if (Beatmap.Value.Track != null) - { - Beatmap.Value.Track.Tempo.Value = 1; - Beatmap.Value.Track.Start(); - } - return base.OnExiting(next); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Screens; +using osu.Game.Screens.Backgrounds; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Menus; +using osu.Game.Screens.Edit.Components.Timelines.Summary; +using osu.Framework.Allocation; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Framework.Timing; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Edit.Screens; +using osu.Game.Screens.Edit.Screens.Compose; +using osu.Game.Screens.Edit.Screens.Design; +using osu.Game.Screens.Edit.Components; + +namespace osu.Game.Screens.Edit +{ + public class Editor : OsuScreen + { + protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); + + public override bool ShowOverlaysOnEnter => false; + + private Box bottomBackground; + private Container screenContainer; + + private EditorScreen currentScreen; + + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); + + private EditorClock clock; + + private DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(parent); + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + // TODO: should probably be done at a RulesetContainer level to share logic with Player. + var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); + clock = new EditorClock(Beatmap.Value.Beatmap.ControlPointInfo, beatDivisor) { IsCoupled = false }; + clock.ChangeSource(sourceClock); + + dependencies.CacheAs(clock); + dependencies.CacheAs(clock); + dependencies.Cache(beatDivisor); + + EditorMenuBar menuBar; + TimeInfoContainer timeInfo; + SummaryTimeline timeline; + PlaybackControl playback; + + Children = new[] + { + new Container + { + Name = "Screen container", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 40, Bottom = 60 }, + Child = screenContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true + } + }, + new Container + { + Name = "Top bar", + RelativeSizeAxes = Axes.X, + Height = 40, + Child = menuBar = new EditorMenuBar + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Items = new[] + { + new MenuItem("File") + { + Items = new[] + { + new EditorMenuItem("Export", MenuItemType.Standard, exportBeatmap), + new EditorMenuItemSpacer(), + new EditorMenuItem("Exit", MenuItemType.Standard, Exit) + } + } + } + } + }, + new Container + { + Name = "Bottom bar", + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 60, + Children = new Drawable[] + { + bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Vertical = 5, Horizontal = 10 }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 220), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 220) + }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 10 }, + Child = timeInfo = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, + }, + timeline = new SummaryTimeline + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 10 }, + Child = playback = new PlaybackControl { RelativeSizeAxes = Axes.Both }, + } + }, + } + }, + } + } + }, + }; + + timeInfo.Beatmap.BindTo(Beatmap); + timeline.Beatmap.BindTo(Beatmap); + playback.Beatmap.BindTo(Beatmap); + menuBar.Mode.ValueChanged += onModeChanged; + + bottomBackground.Colour = colours.Gray2; + } + + private void exportBeatmap() + { + Beatmap.Value.Save(); + } + + private void onModeChanged(EditorScreenMode mode) + { + currentScreen?.Exit(); + + switch (mode) + { + case EditorScreenMode.Compose: + currentScreen = new Compose(); + break; + case EditorScreenMode.Design: + currentScreen = new Design(); + break; + default: + currentScreen = new EditorScreen(); + break; + } + + currentScreen.Beatmap.BindTo(Beatmap); + screenContainer.Add(currentScreen); + } + + protected override bool OnWheel(InputState state) + { + if (state.Mouse.WheelDelta > 0) + clock.SeekBackward(true); + else + clock.SeekForward(true); + return true; + } + + protected override void OnResuming(Screen last) + { + Beatmap.Value.Track?.Stop(); + base.OnResuming(last); + } + + protected override void OnEntering(Screen last) + { + base.OnEntering(last); + Background.FadeColour(Color4.DarkGray, 500); + Beatmap.Value.Track?.Stop(); + } + + protected override bool OnExiting(Screen next) + { + Background.FadeColour(Color4.White, 500); + if (Beatmap.Value.Track != null) + { + Beatmap.Value.Track.Tempo.Value = 1; + Beatmap.Value.Track.Start(); + } + return base.OnExiting(next); + } + } +} diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 874fd186f8..67025f0620 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -1,117 +1,117 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.MathUtils; -using osu.Framework.Timing; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Screens.Edit.Screens.Compose; - -namespace osu.Game.Screens.Edit -{ - /// - /// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor. - /// - public class EditorClock : DecoupleableInterpolatingFramedClock - { - public ControlPointInfo ControlPointInfo; - - private readonly BindableBeatDivisor beatDivisor; - - public EditorClock(ControlPointInfo controlPointInfo, BindableBeatDivisor beatDivisor) - { - this.beatDivisor = beatDivisor; - - ControlPointInfo = controlPointInfo; - } - - /// - /// Seek to the closest snappable beat from a time. - /// - /// The raw position which should be seeked around. - /// Whether the seek could be performed. - public bool SeekSnapped(double position) - { - var timingPoint = ControlPointInfo.TimingPointAt(position); - double beatSnapLength = timingPoint.BeatLength / beatDivisor; - - // We will be snapping to beats within the timing point - position -= timingPoint.Time; - - // Determine the index from the current timing point of the closest beat to position - int closestBeat = (int)Math.Round(position / beatSnapLength); - position = timingPoint.Time + closestBeat * beatSnapLength; - - // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to - // the next timing point's start time - var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); - if (position > nextTimingPoint?.Time) - position = nextTimingPoint.Time; - - return Seek(position); - } - - /// - /// Seeks backwards by one beat length. - /// - /// Whether to snap to the closest beat after seeking. - public void SeekBackward(bool snapped = false) => seek(-1, snapped); - - /// - /// Seeks forwards by one beat length. - /// - /// Whether to snap to the closest beat after seeking. - public void SeekForward(bool snapped = false) => seek(1, snapped); - - private void seek(int direction, bool snapped) - { - var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime); - if (direction < 0 && timingPoint.Time == CurrentTime) - { - // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into - int activeIndex = ControlPointInfo.TimingPoints.IndexOf(timingPoint); - while (activeIndex > 0 && CurrentTime == timingPoint.Time) - timingPoint = ControlPointInfo.TimingPoints[--activeIndex]; - } - - double seekAmount = timingPoint.BeatLength / beatDivisor; - double seekTime = CurrentTime + seekAmount * direction; - - if (!snapped || ControlPointInfo.TimingPoints.Count == 0) - { - Seek(seekTime); - return; - } - - // We will be snapping to beats within timingPoint - seekTime -= timingPoint.Time; - - // Determine the index from timingPoint of the closest beat to seekTime, accounting for scrolling direction - int closestBeat; - if (direction > 0) - closestBeat = (int)Math.Floor(seekTime / seekAmount); - else - closestBeat = (int)Math.Ceiling(seekTime / seekAmount); - - seekTime = timingPoint.Time + closestBeat * seekAmount; - - // Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this. - // Instead, we'll go to the next beat in the direction when this is the case - if (Precision.AlmostEquals(CurrentTime, seekTime)) - { - closestBeat += direction > 0 ? 1 : -1; - seekTime = timingPoint.Time + closestBeat * seekAmount; - } - - if (seekTime < timingPoint.Time && timingPoint != ControlPointInfo.TimingPoints.First()) - seekTime = timingPoint.Time; - - var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); - if (seekTime > nextTimingPoint?.Time) - seekTime = nextTimingPoint.Time; - - Seek(seekTime); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Screens.Edit.Screens.Compose; + +namespace osu.Game.Screens.Edit +{ + /// + /// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor. + /// + public class EditorClock : DecoupleableInterpolatingFramedClock + { + public ControlPointInfo ControlPointInfo; + + private readonly BindableBeatDivisor beatDivisor; + + public EditorClock(ControlPointInfo controlPointInfo, BindableBeatDivisor beatDivisor) + { + this.beatDivisor = beatDivisor; + + ControlPointInfo = controlPointInfo; + } + + /// + /// Seek to the closest snappable beat from a time. + /// + /// The raw position which should be seeked around. + /// Whether the seek could be performed. + public bool SeekSnapped(double position) + { + var timingPoint = ControlPointInfo.TimingPointAt(position); + double beatSnapLength = timingPoint.BeatLength / beatDivisor; + + // We will be snapping to beats within the timing point + position -= timingPoint.Time; + + // Determine the index from the current timing point of the closest beat to position + int closestBeat = (int)Math.Round(position / beatSnapLength); + position = timingPoint.Time + closestBeat * beatSnapLength; + + // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to + // the next timing point's start time + var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + if (position > nextTimingPoint?.Time) + position = nextTimingPoint.Time; + + return Seek(position); + } + + /// + /// Seeks backwards by one beat length. + /// + /// Whether to snap to the closest beat after seeking. + public void SeekBackward(bool snapped = false) => seek(-1, snapped); + + /// + /// Seeks forwards by one beat length. + /// + /// Whether to snap to the closest beat after seeking. + public void SeekForward(bool snapped = false) => seek(1, snapped); + + private void seek(int direction, bool snapped) + { + var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime); + if (direction < 0 && timingPoint.Time == CurrentTime) + { + // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into + int activeIndex = ControlPointInfo.TimingPoints.IndexOf(timingPoint); + while (activeIndex > 0 && CurrentTime == timingPoint.Time) + timingPoint = ControlPointInfo.TimingPoints[--activeIndex]; + } + + double seekAmount = timingPoint.BeatLength / beatDivisor; + double seekTime = CurrentTime + seekAmount * direction; + + if (!snapped || ControlPointInfo.TimingPoints.Count == 0) + { + Seek(seekTime); + return; + } + + // We will be snapping to beats within timingPoint + seekTime -= timingPoint.Time; + + // Determine the index from timingPoint of the closest beat to seekTime, accounting for scrolling direction + int closestBeat; + if (direction > 0) + closestBeat = (int)Math.Floor(seekTime / seekAmount); + else + closestBeat = (int)Math.Ceiling(seekTime / seekAmount); + + seekTime = timingPoint.Time + closestBeat * seekAmount; + + // Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this. + // Instead, we'll go to the next beat in the direction when this is the case + if (Precision.AlmostEquals(CurrentTime, seekTime)) + { + closestBeat += direction > 0 ? 1 : -1; + seekTime = timingPoint.Time + closestBeat * seekAmount; + } + + if (seekTime < timingPoint.Time && timingPoint != ControlPointInfo.TimingPoints.First()) + seekTime = timingPoint.Time; + + var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); + if (seekTime > nextTimingPoint?.Time) + seekTime = nextTimingPoint.Time; + + Seek(seekTime); + } + } +} diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs index ab699d1832..cb7c0fa803 100644 --- a/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs @@ -1,190 +1,190 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -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.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using osu.Game.Screens.Edit.Screens; - -namespace osu.Game.Screens.Edit.Menus -{ - public class EditorMenuBar : OsuMenu - { - public readonly Bindable Mode = new Bindable(); - - public EditorMenuBar() - : base(Direction.Horizontal, true) - { - RelativeSizeAxes = Axes.X; - - MaskingContainer.CornerRadius = 0; - ItemsContainer.Padding = new MarginPadding { Left = 100 }; - BackgroundColour = OsuColour.FromHex("111"); - - ScreenSelectionTabControl tabControl; - AddRangeInternal(new Drawable[] - { - tabControl = new ScreenSelectionTabControl - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - X = -15 - } - }); - - Mode.BindTo(tabControl.Current); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Mode.TriggerChange(); - } - - protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu(); - - protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableEditorBarMenuItem(item); - - private class DrawableEditorBarMenuItem : DrawableOsuMenuItem - { - private BackgroundBox background; - - public DrawableEditorBarMenuItem(MenuItem item) - : base(item) - { - Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; - - StateChanged += stateChanged; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - ForegroundColour = colours.BlueLight; - BackgroundColour = Color4.Transparent; - ForegroundColourHover = Color4.White; - BackgroundColourHover = colours.Gray3; - } - - public override void SetFlowDirection(Direction direction) - { - AutoSizeAxes = Axes.Both; - } - - protected override void UpdateBackgroundColour() - { - if (State == MenuItemState.Selected) - Background.FadeColour(BackgroundColourHover); - else - base.UpdateBackgroundColour(); - } - - protected override void UpdateForegroundColour() - { - if (State == MenuItemState.Selected) - Foreground.FadeColour(ForegroundColourHover); - else - base.UpdateForegroundColour(); - } - - private void stateChanged(MenuItemState newState) - { - if (newState == MenuItemState.Selected) - background.Expand(); - else - background.Contract(); - } - - protected override Drawable CreateBackground() => background = new BackgroundBox(); - protected override DrawableOsuMenuItem.TextContainer CreateTextContainer() => new TextContainer(); - - private new class TextContainer : DrawableOsuMenuItem.TextContainer - { - public TextContainer() - { - NormalText.TextSize = BoldText.TextSize = 14; - NormalText.Margin = BoldText.Margin = new MarginPadding { Horizontal = 10, Vertical = MARGIN_VERTICAL }; - } - } - - private class BackgroundBox : CompositeDrawable - { - private readonly Container innerBackground; - - public BackgroundBox() - { - RelativeSizeAxes = Axes.Both; - Masking = true; - InternalChild = innerBackground = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 4, - Child = new Box { RelativeSizeAxes = Axes.Both } - }; - } - - /// - /// Expands the background such that it doesn't show the bottom corners. - /// - public void Expand() => innerBackground.Height = 2; - - /// - /// Contracts the background such that it shows the bottom corners. - /// - public void Contract() => innerBackground.Height = 1; - } - } - - private class SubMenu : OsuMenu - { - public SubMenu() - : base(Direction.Vertical) - { - OriginPosition = new Vector2(5, 1); - ItemsContainer.Padding = new MarginPadding { Top = 5, Bottom = 5 }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray3; - } - - protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu(); - - protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableSubMenuItem(item); - - private class DrawableSubMenuItem : DrawableOsuMenuItem - { - public DrawableSubMenuItem(MenuItem item) - : base(item) - { - } - - protected override bool OnHover(InputState state) - { - if (Item is EditorMenuItemSpacer) - return true; - return base.OnHover(state); - } - - protected override bool OnClick(InputState state) - { - if (Item is EditorMenuItemSpacer) - return true; - return base.OnClick(state); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +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.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using osu.Game.Screens.Edit.Screens; + +namespace osu.Game.Screens.Edit.Menus +{ + public class EditorMenuBar : OsuMenu + { + public readonly Bindable Mode = new Bindable(); + + public EditorMenuBar() + : base(Direction.Horizontal, true) + { + RelativeSizeAxes = Axes.X; + + MaskingContainer.CornerRadius = 0; + ItemsContainer.Padding = new MarginPadding { Left = 100 }; + BackgroundColour = OsuColour.FromHex("111"); + + ScreenSelectionTabControl tabControl; + AddRangeInternal(new Drawable[] + { + tabControl = new ScreenSelectionTabControl + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + X = -15 + } + }); + + Mode.BindTo(tabControl.Current); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Mode.TriggerChange(); + } + + protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu(); + + protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableEditorBarMenuItem(item); + + private class DrawableEditorBarMenuItem : DrawableOsuMenuItem + { + private BackgroundBox background; + + public DrawableEditorBarMenuItem(MenuItem item) + : base(item) + { + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + + StateChanged += stateChanged; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + ForegroundColour = colours.BlueLight; + BackgroundColour = Color4.Transparent; + ForegroundColourHover = Color4.White; + BackgroundColourHover = colours.Gray3; + } + + public override void SetFlowDirection(Direction direction) + { + AutoSizeAxes = Axes.Both; + } + + protected override void UpdateBackgroundColour() + { + if (State == MenuItemState.Selected) + Background.FadeColour(BackgroundColourHover); + else + base.UpdateBackgroundColour(); + } + + protected override void UpdateForegroundColour() + { + if (State == MenuItemState.Selected) + Foreground.FadeColour(ForegroundColourHover); + else + base.UpdateForegroundColour(); + } + + private void stateChanged(MenuItemState newState) + { + if (newState == MenuItemState.Selected) + background.Expand(); + else + background.Contract(); + } + + protected override Drawable CreateBackground() => background = new BackgroundBox(); + protected override DrawableOsuMenuItem.TextContainer CreateTextContainer() => new TextContainer(); + + private new class TextContainer : DrawableOsuMenuItem.TextContainer + { + public TextContainer() + { + NormalText.TextSize = BoldText.TextSize = 14; + NormalText.Margin = BoldText.Margin = new MarginPadding { Horizontal = 10, Vertical = MARGIN_VERTICAL }; + } + } + + private class BackgroundBox : CompositeDrawable + { + private readonly Container innerBackground; + + public BackgroundBox() + { + RelativeSizeAxes = Axes.Both; + Masking = true; + InternalChild = innerBackground = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + Child = new Box { RelativeSizeAxes = Axes.Both } + }; + } + + /// + /// Expands the background such that it doesn't show the bottom corners. + /// + public void Expand() => innerBackground.Height = 2; + + /// + /// Contracts the background such that it shows the bottom corners. + /// + public void Contract() => innerBackground.Height = 1; + } + } + + private class SubMenu : OsuMenu + { + public SubMenu() + : base(Direction.Vertical) + { + OriginPosition = new Vector2(5, 1); + ItemsContainer.Padding = new MarginPadding { Top = 5, Bottom = 5 }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray3; + } + + protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu(); + + protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableSubMenuItem(item); + + private class DrawableSubMenuItem : DrawableOsuMenuItem + { + public DrawableSubMenuItem(MenuItem item) + : base(item) + { + } + + protected override bool OnHover(InputState state) + { + if (Item is EditorMenuItemSpacer) + return true; + return base.OnHover(state); + } + + protected override bool OnClick(InputState state) + { + if (Item is EditorMenuItemSpacer) + return true; + return base.OnClick(state); + } + } + } + } +} diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuItem.cs b/osu.Game/Screens/Edit/Menus/EditorMenuItem.cs index ec936fb023..0ef1ad8c6b 100644 --- a/osu.Game/Screens/Edit/Menus/EditorMenuItem.cs +++ b/osu.Game/Screens/Edit/Menus/EditorMenuItem.cs @@ -1,23 +1,23 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Screens.Edit.Menus -{ - public class EditorMenuItem : OsuMenuItem - { - private const int min_text_length = 40; - - public EditorMenuItem(string text, MenuItemType type = MenuItemType.Standard) - : base(text.PadRight(min_text_length), type) - { - } - - public EditorMenuItem(string text, MenuItemType type, Action action) - : base(text.PadRight(min_text_length), type, action) - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Menus +{ + public class EditorMenuItem : OsuMenuItem + { + private const int min_text_length = 40; + + public EditorMenuItem(string text, MenuItemType type = MenuItemType.Standard) + : base(text.PadRight(min_text_length), type) + { + } + + public EditorMenuItem(string text, MenuItemType type, Action action) + : base(text.PadRight(min_text_length), type, action) + { + } + } +} diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuItemSpacer.cs b/osu.Game/Screens/Edit/Menus/EditorMenuItemSpacer.cs index 1b16b886b0..91b40a2cfb 100644 --- a/osu.Game/Screens/Edit/Menus/EditorMenuItemSpacer.cs +++ b/osu.Game/Screens/Edit/Menus/EditorMenuItemSpacer.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Screens.Edit.Menus -{ - public class EditorMenuItemSpacer : EditorMenuItem - { - public EditorMenuItemSpacer() - : base(" ") - { - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Edit.Menus +{ + public class EditorMenuItemSpacer : EditorMenuItem + { + public EditorMenuItemSpacer() + : base(" ") + { + } + } +} diff --git a/osu.Game/Screens/Edit/Menus/ScreenSelectionTabControl.cs b/osu.Game/Screens/Edit/Menus/ScreenSelectionTabControl.cs index a7569330bd..1471a37a29 100644 --- a/osu.Game/Screens/Edit/Menus/ScreenSelectionTabControl.cs +++ b/osu.Game/Screens/Edit/Menus/ScreenSelectionTabControl.cs @@ -1,75 +1,75 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Edit.Screens; -using OpenTK; - -namespace osu.Game.Screens.Edit.Menus -{ - public class ScreenSelectionTabControl : OsuTabControl - { - public ScreenSelectionTabControl() - { - AutoSizeAxes = Axes.X; - RelativeSizeAxes = Axes.Y; - - TabContainer.RelativeSizeAxes &= ~Axes.X; - TabContainer.AutoSizeAxes = Axes.X; - TabContainer.Padding = new MarginPadding(); - - Add(new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 1, - Colour = Color4.White.Opacity(0.2f), - }); - - Current.Value = EditorScreenMode.Compose; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Yellow; - } - - protected override Dropdown CreateDropdown() => null; - - protected override TabItem CreateTabItem(EditorScreenMode value) => new TabItem(value); - - private class TabItem : OsuTabItem - { - private const float transition_length = 250; - - public TabItem(EditorScreenMode value) - : base(value) - { - Text.Margin = new MarginPadding(); - Text.Anchor = Anchor.CentreLeft; - Text.Origin = Anchor.CentreLeft; - } - - protected override void OnActivated() - { - base.OnActivated(); - Bar.ScaleTo(new Vector2(1, 5), transition_length, Easing.OutQuint); - } - - protected override void OnDeactivated() - { - base.OnDeactivated(); - Bar.ScaleTo(Vector2.One, transition_length, Easing.OutQuint); - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Edit.Screens; +using OpenTK; + +namespace osu.Game.Screens.Edit.Menus +{ + public class ScreenSelectionTabControl : OsuTabControl + { + public ScreenSelectionTabControl() + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + TabContainer.RelativeSizeAxes &= ~Axes.X; + TabContainer.AutoSizeAxes = Axes.X; + TabContainer.Padding = new MarginPadding(); + + Add(new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 1, + Colour = Color4.White.Opacity(0.2f), + }); + + Current.Value = EditorScreenMode.Compose; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Yellow; + } + + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(EditorScreenMode value) => new TabItem(value); + + private class TabItem : OsuTabItem + { + private const float transition_length = 250; + + public TabItem(EditorScreenMode value) + : base(value) + { + Text.Margin = new MarginPadding(); + Text.Anchor = Anchor.CentreLeft; + Text.Origin = Anchor.CentreLeft; + } + + protected override void OnActivated() + { + base.OnActivated(); + Bar.ScaleTo(new Vector2(1, 5), transition_length, Easing.OutQuint); + } + + protected override void OnDeactivated() + { + base.OnDeactivated(); + Bar.ScaleTo(Vector2.One, transition_length, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 0b30aeef8d..21e48d8b0a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -1,397 +1,397 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Input; - -namespace osu.Game.Screens.Edit.Screens.Compose -{ - public class BeatDivisorControl : CompositeDrawable - { - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - - public BeatDivisorControl(BindableBeatDivisor beatDivisor) - { - this.beatDivisor.BindTo(beatDivisor); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Masking = true; - CornerRadius = 5; - - InternalChildren = new Drawable[] - { - new Box - { - Name = "Gray Background", - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray4 - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - Name = "Black Background", - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black - }, - new TickSliderBar(beatDivisor, BindableBeatDivisor.VALID_DIVISORS) - { - RelativeSizeAxes = Axes.Both, - } - } - } - }, - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray4 - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 5 }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new DivisorButton - { - Icon = FontAwesome.fa_chevron_left, - Action = beatDivisor.Previous - }, - new DivisorText(beatDivisor), - new DivisorButton - { - Icon = FontAwesome.fa_chevron_right, - Action = beatDivisor.Next - } - }, - }, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 20), - new Dimension(), - new Dimension(GridSizeMode.Absolute, 20) - } - } - } - } - } - }, - new Drawable[] - { - new TextFlowContainer(s => s.TextSize = 14) - { - Padding = new MarginPadding { Horizontal = 15 }, - Text = "beat snap divisor", - RelativeSizeAxes = Axes.X, - TextAnchor = Anchor.TopCentre - }, - } - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 30), - new Dimension(GridSizeMode.Absolute, 25), - } - } - }; - } - - private class DivisorText : SpriteText - { - private readonly Bindable beatDivisor = new Bindable(); - - public DivisorText(BindableBeatDivisor beatDivisor) - { - this.beatDivisor.BindTo(beatDivisor); - - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = colours.BlueLighter; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - beatDivisor.ValueChanged += v => updateText(); - updateText(); - } - - private void updateText() => Text = $"1/{beatDivisor.Value}"; - } - - private class DivisorButton : IconButton - { - public DivisorButton() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - // Small offset to look a bit better centered along with the divisor text - Y = 1; - - ButtonSize = new Vector2(20); - IconScale = new Vector2(0.6f); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - IconColour = Color4.Black; - HoverColour = colours.Gray7; - FlashColour = colours.Gray9; - } - } - - private class TickSliderBar : SliderBar - { - private Marker marker; - - private readonly BindableBeatDivisor beatDivisor; - private readonly int[] availableDivisors; - - public TickSliderBar(BindableBeatDivisor beatDivisor, params int[] divisors) - { - CurrentNumber.BindTo(this.beatDivisor = beatDivisor); - availableDivisors = divisors; - - Padding = new MarginPadding { Horizontal = 5 }; - } - - [BackgroundDependencyLoader] - private void load() - { - foreach (var t in availableDivisors) - { - AddInternal(new Tick(t) - { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopCentre, - RelativePositionAxes = Axes.X, - X = getMappedPosition(t) - }); - } - - AddInternal(marker = new Marker()); - - CurrentNumber.ValueChanged += v => - { - marker.MoveToX(getMappedPosition(v), 100, Easing.OutQuint); - marker.Flash(); - }; - } - - protected override void UpdateValue(float value) - { - } - - public override bool HandleKeyboardInput => IsHovered && !CurrentNumber.Disabled; - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - switch (args.Key) - { - case Key.Right: - beatDivisor.Next(); - OnUserChange(); - return true; - case Key.Left: - beatDivisor.Previous(); - OnUserChange(); - return true; - default: - return false; - } - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - marker.Active = true; - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - marker.Active = false; - return base.OnMouseUp(state, args); - } - - protected override bool OnClick(InputState state) - { - handleMouseInput(state); - return true; - } - - protected override bool OnDrag(InputState state) - { - handleMouseInput(state); - return true; - } - - private void handleMouseInput(InputState state) - { - // copied from SliderBar so we can do custom spacing logic. - var xPosition = (ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding) / UsableWidth; - - CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First(); - OnUserChange(); - } - - private float getMappedPosition(float divisor) => (float)Math.Pow((divisor - 1) / (availableDivisors.Last() - 1), 0.90f); - - private class Tick : CompositeDrawable - { - private readonly int divisor; - - public Tick(int divisor) - { - this.divisor = divisor; - Size = new Vector2(2.5f, 10); - - InternalChild = new Box { RelativeSizeAxes = Axes.Both }; - - CornerRadius = 0.5f; - Masking = true; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = getColourForDivisor(divisor, colours); - } - - private ColourInfo getColourForDivisor(int divisor, OsuColour colours) - { - switch (divisor) - { - case 2: - return colours.BlueLight; - case 4: - return colours.Blue; - case 8: - return colours.BlueDarker; - case 16: - return colours.PurpleDark; - case 3: - return colours.YellowLight; - case 6: - return colours.Yellow; - case 12: - return colours.YellowDarker; - default: - return Color4.White; - } - } - } - - private class Marker : CompositeDrawable - { - private Color4 defaultColour; - - private const float size = 7; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = defaultColour = colours.Gray4; - Anchor = Anchor.TopLeft; - Origin = Anchor.TopCentre; - - Width = size; - RelativeSizeAxes = Axes.Y; - RelativePositionAxes = Axes.X; - - InternalChildren = new Drawable[] - { - new Box - { - Width = 2, - RelativeSizeAxes = Axes.Y, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.2f), Color4.White), - Blending = BlendingMode.Additive, - }, - new EquilateralTriangle - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Height = size, - EdgeSmoothness = new Vector2(1), - Colour = Color4.White, - } - }; - } - - private bool active; - - public bool Active - { - get => active; - set - { - this.FadeColour(value ? Color4.White : defaultColour, 500, Easing.OutQuint); - active = value; - } - } - - public void Flash() - { - bool wasActive = active; - - Active = true; - - if (wasActive) return; - - using (BeginDelayedSequence(50)) - Active = false; - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; + +namespace osu.Game.Screens.Edit.Screens.Compose +{ + public class BeatDivisorControl : CompositeDrawable + { + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); + + public BeatDivisorControl(BindableBeatDivisor beatDivisor) + { + this.beatDivisor.BindTo(beatDivisor); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Masking = true; + CornerRadius = 5; + + InternalChildren = new Drawable[] + { + new Box + { + Name = "Gray Background", + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray4 + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Name = "Black Background", + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + new TickSliderBar(beatDivisor, BindableBeatDivisor.VALID_DIVISORS) + { + RelativeSizeAxes = Axes.Both, + } + } + } + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray4 + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 5 }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new DivisorButton + { + Icon = FontAwesome.fa_chevron_left, + Action = beatDivisor.Previous + }, + new DivisorText(beatDivisor), + new DivisorButton + { + Icon = FontAwesome.fa_chevron_right, + Action = beatDivisor.Next + } + }, + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 20) + } + } + } + } + } + }, + new Drawable[] + { + new TextFlowContainer(s => s.TextSize = 14) + { + Padding = new MarginPadding { Horizontal = 15 }, + Text = "beat snap divisor", + RelativeSizeAxes = Axes.X, + TextAnchor = Anchor.TopCentre + }, + } + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 30), + new Dimension(GridSizeMode.Absolute, 25), + } + } + }; + } + + private class DivisorText : SpriteText + { + private readonly Bindable beatDivisor = new Bindable(); + + public DivisorText(BindableBeatDivisor beatDivisor) + { + this.beatDivisor.BindTo(beatDivisor); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.BlueLighter; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + beatDivisor.ValueChanged += v => updateText(); + updateText(); + } + + private void updateText() => Text = $"1/{beatDivisor.Value}"; + } + + private class DivisorButton : IconButton + { + public DivisorButton() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + // Small offset to look a bit better centered along with the divisor text + Y = 1; + + ButtonSize = new Vector2(20); + IconScale = new Vector2(0.6f); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IconColour = Color4.Black; + HoverColour = colours.Gray7; + FlashColour = colours.Gray9; + } + } + + private class TickSliderBar : SliderBar + { + private Marker marker; + + private readonly BindableBeatDivisor beatDivisor; + private readonly int[] availableDivisors; + + public TickSliderBar(BindableBeatDivisor beatDivisor, params int[] divisors) + { + CurrentNumber.BindTo(this.beatDivisor = beatDivisor); + availableDivisors = divisors; + + Padding = new MarginPadding { Horizontal = 5 }; + } + + [BackgroundDependencyLoader] + private void load() + { + foreach (var t in availableDivisors) + { + AddInternal(new Tick(t) + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopCentre, + RelativePositionAxes = Axes.X, + X = getMappedPosition(t) + }); + } + + AddInternal(marker = new Marker()); + + CurrentNumber.ValueChanged += v => + { + marker.MoveToX(getMappedPosition(v), 100, Easing.OutQuint); + marker.Flash(); + }; + } + + protected override void UpdateValue(float value) + { + } + + public override bool HandleKeyboardInput => IsHovered && !CurrentNumber.Disabled; + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + switch (args.Key) + { + case Key.Right: + beatDivisor.Next(); + OnUserChange(); + return true; + case Key.Left: + beatDivisor.Previous(); + OnUserChange(); + return true; + default: + return false; + } + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + marker.Active = true; + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + marker.Active = false; + return base.OnMouseUp(state, args); + } + + protected override bool OnClick(InputState state) + { + handleMouseInput(state); + return true; + } + + protected override bool OnDrag(InputState state) + { + handleMouseInput(state); + return true; + } + + private void handleMouseInput(InputState state) + { + // copied from SliderBar so we can do custom spacing logic. + var xPosition = (ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding) / UsableWidth; + + CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First(); + OnUserChange(); + } + + private float getMappedPosition(float divisor) => (float)Math.Pow((divisor - 1) / (availableDivisors.Last() - 1), 0.90f); + + private class Tick : CompositeDrawable + { + private readonly int divisor; + + public Tick(int divisor) + { + this.divisor = divisor; + Size = new Vector2(2.5f, 10); + + InternalChild = new Box { RelativeSizeAxes = Axes.Both }; + + CornerRadius = 0.5f; + Masking = true; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = getColourForDivisor(divisor, colours); + } + + private ColourInfo getColourForDivisor(int divisor, OsuColour colours) + { + switch (divisor) + { + case 2: + return colours.BlueLight; + case 4: + return colours.Blue; + case 8: + return colours.BlueDarker; + case 16: + return colours.PurpleDark; + case 3: + return colours.YellowLight; + case 6: + return colours.Yellow; + case 12: + return colours.YellowDarker; + default: + return Color4.White; + } + } + } + + private class Marker : CompositeDrawable + { + private Color4 defaultColour; + + private const float size = 7; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = defaultColour = colours.Gray4; + Anchor = Anchor.TopLeft; + Origin = Anchor.TopCentre; + + Width = size; + RelativeSizeAxes = Axes.Y; + RelativePositionAxes = Axes.X; + + InternalChildren = new Drawable[] + { + new Box + { + Width = 2, + RelativeSizeAxes = Axes.Y, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.2f), Color4.White), + Blending = BlendingMode.Additive, + }, + new EquilateralTriangle + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Height = size, + EdgeSmoothness = new Vector2(1), + Colour = Color4.White, + } + }; + } + + private bool active; + + public bool Active + { + get => active; + set + { + this.FadeColour(value ? Color4.White : defaultColour, 500, Easing.OutQuint); + active = value; + } + } + + public void Flash() + { + bool wasActive = active; + + Active = true; + + if (wasActive) return; + + using (BeginDelayedSequence(50)) + Active = false; + } + } + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs index 8eb3f1347e..b7dce8c96e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs @@ -1,39 +1,39 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Linq; -using osu.Framework.Configuration; - -namespace osu.Game.Screens.Edit.Screens.Compose -{ - public class BindableBeatDivisor : BindableNumber - { - public static readonly int[] VALID_DIVISORS = { 1, 2, 3, 4, 6, 8, 12, 16 }; - - public BindableBeatDivisor(int value = 1) - : base(value) - { - } - - public void Next() => Value = VALID_DIVISORS[Math.Min(VALID_DIVISORS.Length - 1, Array.IndexOf(VALID_DIVISORS, Value) + 1)]; - - public void Previous() => Value = VALID_DIVISORS[Math.Max(0, Array.IndexOf(VALID_DIVISORS, Value) - 1)]; - - public override int Value - { - get { return base.Value; } - set - { - if (!VALID_DIVISORS.Contains(value)) - throw new ArgumentOutOfRangeException($"Provided divisor is not in {nameof(VALID_DIVISORS)}"); - - base.Value = value; - } - } - - protected override int DefaultMinValue => VALID_DIVISORS.First(); - protected override int DefaultMaxValue => VALID_DIVISORS.Last(); - protected override int DefaultPrecision => 1; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Configuration; + +namespace osu.Game.Screens.Edit.Screens.Compose +{ + public class BindableBeatDivisor : BindableNumber + { + public static readonly int[] VALID_DIVISORS = { 1, 2, 3, 4, 6, 8, 12, 16 }; + + public BindableBeatDivisor(int value = 1) + : base(value) + { + } + + public void Next() => Value = VALID_DIVISORS[Math.Min(VALID_DIVISORS.Length - 1, Array.IndexOf(VALID_DIVISORS, Value) + 1)]; + + public void Previous() => Value = VALID_DIVISORS[Math.Max(0, Array.IndexOf(VALID_DIVISORS, Value) - 1)]; + + public override int Value + { + get { return base.Value; } + set + { + if (!VALID_DIVISORS.Contains(value)) + throw new ArgumentOutOfRangeException($"Provided divisor is not in {nameof(VALID_DIVISORS)}"); + + base.Value = value; + } + } + + protected override int DefaultMinValue => VALID_DIVISORS.First(); + protected override int DefaultMaxValue => VALID_DIVISORS.Last(); + protected override int DefaultPrecision => 1; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index bd672451c0..fea4883144 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -1,118 +1,118 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using JetBrains.Annotations; -using osu.Framework.Allocation; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Logging; -using osu.Game.Screens.Edit.Screens.Compose.Timeline; - -namespace osu.Game.Screens.Edit.Screens.Compose -{ - public class Compose : EditorScreen - { - private const float vertical_margins = 10; - private const float horizontal_margins = 20; - - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - - private Container composerContainer; - - [BackgroundDependencyLoader(true)] - private void load([CanBeNull] BindableBeatDivisor beatDivisor) - { - if (beatDivisor != null) - this.beatDivisor.BindTo(beatDivisor); - - ScrollableTimeline timeline; - Children = new Drawable[] - { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new Container - { - Name = "Timeline", - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f) - }, - new Container - { - Name = "Timeline content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 5 }, - Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } - }, - new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } - }, - }, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 90), - } - }, - } - } - } - }, - new Drawable[] - { - composerContainer = new Container - { - Name = "Composer content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, - } - } - }, - RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, 110) } - }, - }; - - timeline.Beatmap.BindTo(Beatmap); - - var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance(); - if (ruleset == null) - { - Logger.Log("Beatmap doesn't have a ruleset assigned."); - // ExitRequested?.Invoke(); - return; - } - - var composer = ruleset.CreateHitObjectComposer(); - if (composer == null) - { - Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); - // ExitRequested?.Invoke(); - return; - } - - composerContainer.Child = composer; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using JetBrains.Annotations; +using osu.Framework.Allocation; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; +using osu.Game.Screens.Edit.Screens.Compose.Timeline; + +namespace osu.Game.Screens.Edit.Screens.Compose +{ + public class Compose : EditorScreen + { + private const float vertical_margins = 10; + private const float horizontal_margins = 20; + + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); + + private Container composerContainer; + + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] BindableBeatDivisor beatDivisor) + { + if (beatDivisor != null) + this.beatDivisor.BindTo(beatDivisor); + + ScrollableTimeline timeline; + Children = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + Name = "Timeline", + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f) + }, + new Container + { + Name = "Timeline content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 5 }, + Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } + }, + new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } + }, + }, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 90), + } + }, + } + } + } + }, + new Drawable[] + { + composerContainer = new Container + { + Name = "Composer content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + } + } + }, + RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, 110) } + }, + }; + + timeline.Beatmap.BindTo(Beatmap); + + var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance(); + if (ruleset == null) + { + Logger.Log("Beatmap doesn't have a ruleset assigned."); + // ExitRequested?.Invoke(); + return; + } + + var composer = ruleset.CreateHitObjectComposer(); + if (composer == null) + { + Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); + // ExitRequested?.Invoke(); + return; + } + + composerContainer.Child = composer; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs index 49cf078d36..c46f9a1b7f 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/BorderLayer.cs @@ -1,38 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using OpenTK.Graphics; - -namespace osu.Game.Screens.Edit.Screens.Compose.Layers -{ - public class BorderLayer : Container - { - protected override Container Content => content; - private readonly Container content; - - public BorderLayer() - { - InternalChildren = new Drawable[] - { - new Container - { - Name = "Border", - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - }, - content = new Container { RelativeSizeAxes = Axes.Both } - }; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + public class BorderLayer : Container + { + protected override Container Content => content; + private readonly Container content; + + public BorderLayer() + { + InternalChildren = new Drawable[] + { + new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + }, + content = new Container { RelativeSizeAxes = Axes.Both } + }; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs index 51bb61b607..d8200d6c37 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs @@ -1,92 +1,92 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -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; -using osu.Game.Rulesets.Edit; -using OpenTK.Graphics; - -namespace osu.Game.Screens.Edit.Screens.Compose.Layers -{ - /// - /// A layer that handles and displays drag selection for a collection of s. - /// - public class DragLayer : CompositeDrawable - { - private readonly Action performSelection; - - /// - /// Invoked when the drag selection has finished. - /// - public event Action DragEnd; - - private Drawable box; - - /// - /// Creates a new . - /// - /// The selectable s. - public DragLayer(Action performSelection) - { - this.performSelection = performSelection; - - RelativeSizeAxes = Axes.Both; - AlwaysPresent = true; - Alpha = 0; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = box = new Container - { - Masking = true, - BorderColour = Color4.White, - BorderThickness = MaskSelection.BORDER_RADIUS, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.1f - } - }; - } - - protected override bool OnDragStart(InputState state) - { - this.FadeIn(250, Easing.OutQuint); - return true; - } - - protected override bool OnDrag(InputState state) - { - var dragPosition = state.Mouse.NativeState.Position; - var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition; - - var dragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y); - - // We use AABBFloat instead of RectangleF since it handles negative sizes for us - var dragRectangle = dragQuad.AABBFloat; - - var topLeft = ToLocalSpace(dragRectangle.TopLeft); - var bottomRight = ToLocalSpace(dragRectangle.BottomRight); - - box.Position = topLeft; - box.Size = bottomRight - topLeft; - - performSelection?.Invoke(dragRectangle); - return true; - } - - protected override bool OnDragEnd(InputState state) - { - this.FadeOut(250, Easing.OutQuint); - DragEnd?.Invoke(); - return true; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +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; +using osu.Game.Rulesets.Edit; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + /// + /// A layer that handles and displays drag selection for a collection of s. + /// + public class DragLayer : CompositeDrawable + { + private readonly Action performSelection; + + /// + /// Invoked when the drag selection has finished. + /// + public event Action DragEnd; + + private Drawable box; + + /// + /// Creates a new . + /// + /// The selectable s. + public DragLayer(Action performSelection) + { + this.performSelection = performSelection; + + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + Alpha = 0; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = box = new Container + { + Masking = true, + BorderColour = Color4.White, + BorderThickness = MaskSelection.BORDER_RADIUS, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f + } + }; + } + + protected override bool OnDragStart(InputState state) + { + this.FadeIn(250, Easing.OutQuint); + return true; + } + + protected override bool OnDrag(InputState state) + { + var dragPosition = state.Mouse.NativeState.Position; + var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition; + + var dragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y); + + // We use AABBFloat instead of RectangleF since it handles negative sizes for us + var dragRectangle = dragQuad.AABBFloat; + + var topLeft = ToLocalSpace(dragRectangle.TopLeft); + var bottomRight = ToLocalSpace(dragRectangle.BottomRight); + + box.Position = topLeft; + box.Size = bottomRight - topLeft; + + performSelection?.Invoke(dragRectangle); + return true; + } + + protected override bool OnDragEnd(InputState state) + { + this.FadeOut(250, Easing.OutQuint); + DragEnd?.Invoke(); + return true; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index 423cf0ed29..ede98d986a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -1,93 +1,93 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Screens.Edit.Screens.Compose.Layers -{ - public class HitObjectMaskLayer : CompositeDrawable - { - private readonly Playfield playfield; - private readonly HitObjectComposer composer; - - private MaskContainer maskContainer; - - public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer) - { - // we need the playfield as HitObjects may not be initialised until its BDL. - this.playfield = playfield; - - this.composer = composer; - - RelativeSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - maskContainer = new MaskContainer(); - - var maskSelection = composer.CreateMaskSelection(); - - maskContainer.MaskSelected += maskSelection.HandleSelected; - maskContainer.MaskDeselected += maskSelection.HandleDeselected; - maskContainer.MaskSelectionRequested += maskSelection.HandleSelectionRequested; - maskContainer.MaskDragRequested += maskSelection.HandleDrag; - - maskSelection.DeselectAll = maskContainer.DeselectAll; - - var dragLayer = new DragLayer(maskContainer.Select); - dragLayer.DragEnd += () => maskSelection.UpdateVisibility(); - - InternalChildren = new Drawable[] - { - dragLayer, - maskSelection, - maskContainer, - dragLayer.CreateProxy() - }; - - foreach (var obj in playfield.HitObjects.Objects) - addMask(obj); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - maskContainer.DeselectAll(); - return true; - } - - /// - /// Adds a mask for a which adds movement support. - /// - /// The to create a mask for. - private void addMask(DrawableHitObject hitObject) - { - var mask = composer.CreateMaskFor(hitObject); - if (mask == null) - return; - - maskContainer.Add(mask); - } - - /// - /// Removes the mask for a . - /// - /// The to remove the mask for. - private void removeMask(DrawableHitObject hitObject) - { - var mask = maskContainer.FirstOrDefault(h => h.HitObject == hitObject); - if (mask == null) - return; - - maskContainer.Remove(mask); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + public class HitObjectMaskLayer : CompositeDrawable + { + private readonly Playfield playfield; + private readonly HitObjectComposer composer; + + private MaskContainer maskContainer; + + public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer) + { + // we need the playfield as HitObjects may not be initialised until its BDL. + this.playfield = playfield; + + this.composer = composer; + + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + maskContainer = new MaskContainer(); + + var maskSelection = composer.CreateMaskSelection(); + + maskContainer.MaskSelected += maskSelection.HandleSelected; + maskContainer.MaskDeselected += maskSelection.HandleDeselected; + maskContainer.MaskSelectionRequested += maskSelection.HandleSelectionRequested; + maskContainer.MaskDragRequested += maskSelection.HandleDrag; + + maskSelection.DeselectAll = maskContainer.DeselectAll; + + var dragLayer = new DragLayer(maskContainer.Select); + dragLayer.DragEnd += () => maskSelection.UpdateVisibility(); + + InternalChildren = new Drawable[] + { + dragLayer, + maskSelection, + maskContainer, + dragLayer.CreateProxy() + }; + + foreach (var obj in playfield.HitObjects.Objects) + addMask(obj); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + maskContainer.DeselectAll(); + return true; + } + + /// + /// Adds a mask for a which adds movement support. + /// + /// The to create a mask for. + private void addMask(DrawableHitObject hitObject) + { + var mask = composer.CreateMaskFor(hitObject); + if (mask == null) + return; + + maskContainer.Add(mask); + } + + /// + /// Removes the mask for a . + /// + /// The to remove the mask for. + private void removeMask(DrawableHitObject hitObject) + { + var mask = maskContainer.FirstOrDefault(h => h.HitObject == hitObject); + if (mask == null) + return; + + maskContainer.Remove(mask); + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index b631628c9e..6d75b8dc15 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -1,123 +1,123 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Rulesets.Edit; -using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; - -namespace osu.Game.Screens.Edit.Screens.Compose.Layers -{ - public class MaskContainer : Container - { - /// - /// Invoked when any is selected. - /// - public event Action MaskSelected; - - /// - /// Invoked when any is deselected. - /// - public event Action MaskDeselected; - - /// - /// Invoked when any requests selection. - /// - public event Action MaskSelectionRequested; - - /// - /// Invoked when any requests drag. - /// - public event Action MaskDragRequested; - - private IEnumerable aliveMasks => AliveInternalChildren.Cast(); - - public MaskContainer() - { - RelativeSizeAxes = Axes.Both; - } - - public override void Add(HitObjectMask drawable) - { - base.Add(drawable); - - drawable.Selected += onMaskSelected; - drawable.Deselected += onMaskDeselected; - drawable.SelectionRequested += onSelectionRequested; - drawable.DragRequested += onDragRequested; - } - - public override bool Remove(HitObjectMask drawable) - { - var result = base.Remove(drawable); - - if (result) - { - drawable.Selected -= onMaskSelected; - drawable.Deselected -= onMaskDeselected; - drawable.SelectionRequested -= onSelectionRequested; - drawable.DragRequested -= onDragRequested; - } - - return result; - } - - /// - /// Select all masks in a given rectangle selection area. - /// - /// The rectangle to perform a selection on in screen-space coordinates. - public void Select(RectangleF rect) - { - foreach (var mask in aliveMasks.ToList()) - { - if (mask.IsPresent && rect.Contains(mask.SelectionPoint)) - mask.Select(); - else - mask.Deselect(); - } - } - - /// - /// Deselects all selected s. - /// - public void DeselectAll() => aliveMasks.ToList().ForEach(m => m.Deselect()); - - private void onMaskSelected(HitObjectMask mask) - { - MaskSelected?.Invoke(mask); - ChangeChildDepth(mask, 1); - } - - private void onMaskDeselected(HitObjectMask mask) - { - MaskDeselected?.Invoke(mask); - ChangeChildDepth(mask, 0); - } - - private void onSelectionRequested(HitObjectMask mask, InputState state) => MaskSelectionRequested?.Invoke(mask, state); - private void onDragRequested(HitObjectMask mask, InputState state) => MaskDragRequested?.Invoke(mask, state); - - protected override int Compare(Drawable x, Drawable y) - { - if (!(x is HitObjectMask xMask) || !(y is HitObjectMask yMask)) - return base.Compare(x, y); - return Compare(xMask, yMask); - } - - public int Compare(HitObjectMask x, HitObjectMask y) - { - // dpeth is used to denote selected status (we always want selected masks to handle input first). - int d = x.Depth.CompareTo(y.Depth); - if (d != 0) - return d; - - // Put earlier hitobjects towards the end of the list, so they handle input first - int i = y.HitObject.HitObject.StartTime.CompareTo(x.HitObject.HitObject.StartTime); - return i == 0 ? CompareReverseChildID(x, y) : i; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Rulesets.Edit; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + public class MaskContainer : Container + { + /// + /// Invoked when any is selected. + /// + public event Action MaskSelected; + + /// + /// Invoked when any is deselected. + /// + public event Action MaskDeselected; + + /// + /// Invoked when any requests selection. + /// + public event Action MaskSelectionRequested; + + /// + /// Invoked when any requests drag. + /// + public event Action MaskDragRequested; + + private IEnumerable aliveMasks => AliveInternalChildren.Cast(); + + public MaskContainer() + { + RelativeSizeAxes = Axes.Both; + } + + public override void Add(HitObjectMask drawable) + { + base.Add(drawable); + + drawable.Selected += onMaskSelected; + drawable.Deselected += onMaskDeselected; + drawable.SelectionRequested += onSelectionRequested; + drawable.DragRequested += onDragRequested; + } + + public override bool Remove(HitObjectMask drawable) + { + var result = base.Remove(drawable); + + if (result) + { + drawable.Selected -= onMaskSelected; + drawable.Deselected -= onMaskDeselected; + drawable.SelectionRequested -= onSelectionRequested; + drawable.DragRequested -= onDragRequested; + } + + return result; + } + + /// + /// Select all masks in a given rectangle selection area. + /// + /// The rectangle to perform a selection on in screen-space coordinates. + public void Select(RectangleF rect) + { + foreach (var mask in aliveMasks.ToList()) + { + if (mask.IsPresent && rect.Contains(mask.SelectionPoint)) + mask.Select(); + else + mask.Deselect(); + } + } + + /// + /// Deselects all selected s. + /// + public void DeselectAll() => aliveMasks.ToList().ForEach(m => m.Deselect()); + + private void onMaskSelected(HitObjectMask mask) + { + MaskSelected?.Invoke(mask); + ChangeChildDepth(mask, 1); + } + + private void onMaskDeselected(HitObjectMask mask) + { + MaskDeselected?.Invoke(mask); + ChangeChildDepth(mask, 0); + } + + private void onSelectionRequested(HitObjectMask mask, InputState state) => MaskSelectionRequested?.Invoke(mask, state); + private void onDragRequested(HitObjectMask mask, InputState state) => MaskDragRequested?.Invoke(mask, state); + + protected override int Compare(Drawable x, Drawable y) + { + if (!(x is HitObjectMask xMask) || !(y is HitObjectMask yMask)) + return base.Compare(x, y); + return Compare(xMask, yMask); + } + + public int Compare(HitObjectMask x, HitObjectMask y) + { + // dpeth is used to denote selected status (we always want selected masks to handle input first). + int d = x.Depth.CompareTo(y.Depth); + if (d != 0) + return d; + + // Put earlier hitobjects towards the end of the list, so they handle input first + int i = y.HitObject.HitObject.StartTime.CompareTo(x.HitObject.HitObject.StartTime); + return i == 0 ? CompareReverseChildID(x, y) : i; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 76b8027b07..54697bad77 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -1,164 +1,164 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Types; -using OpenTK; - -namespace osu.Game.Screens.Edit.Screens.Compose.Layers -{ - /// - /// A box which surrounds s and provides interactive handles, context menus etc. - /// - public class MaskSelection : CompositeDrawable - { - public const float BORDER_RADIUS = 2; - - private readonly List selectedMasks; - - private Drawable outline; - - public MaskSelection() - { - selectedMasks = new List(); - - RelativeSizeAxes = Axes.Both; - AlwaysPresent = true; - Alpha = 0; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - InternalChild = outline = new Container - { - Masking = true, - BorderThickness = BORDER_RADIUS, - BorderColour = colours.Yellow, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - AlwaysPresent = true, - Alpha = 0 - } - }; - } - - #region User Input Handling - - public void HandleDrag(HitObjectMask m, InputState state) - { - // Todo: Various forms of snapping - - foreach (var mask in selectedMasks) - { - switch (mask.HitObject.HitObject) - { - case IHasEditablePosition editablePosition: - editablePosition.OffsetPosition(state.Mouse.Delta); - break; - } - } - } - - #endregion - - #region Selection Handling - - /// - /// Bind an action to deselect all selected masks. - /// - public Action DeselectAll { private get; set; } - - /// - /// Handle a mask becoming selected. - /// - /// The mask. - public void HandleSelected(HitObjectMask mask) => selectedMasks.Add(mask); - - /// - /// Handle a mask becoming deselected. - /// - /// The mask. - public void HandleDeselected(HitObjectMask mask) - { - selectedMasks.Remove(mask); - - // We don't want to update visibility if > 0, since we may be deselecting masks during drag-selection - if (selectedMasks.Count == 0) - UpdateVisibility(); - } - - /// - /// Handle a mask requesting selection. - /// - /// The mask. - public void HandleSelectionRequested(HitObjectMask mask, InputState state) - { - if (state.Keyboard.ControlPressed) - { - if (mask.IsSelected) - mask.Deselect(); - else - mask.Select(); - } - else - { - if (mask.IsSelected) - return; - - DeselectAll?.Invoke(); - mask.Select(); - } - - UpdateVisibility(); - } - - #endregion - - /// - /// Updates whether this is visible. - /// - internal void UpdateVisibility() - { - if (selectedMasks.Count > 0) - Show(); - else - Hide(); - } - - protected override void Update() - { - base.Update(); - - if (selectedMasks.Count == 0) - return; - - // Move the rectangle to cover the hitobjects - var topLeft = new Vector2(float.MaxValue, float.MaxValue); - var bottomRight = new Vector2(float.MinValue, float.MinValue); - - bool hasSelection = false; - - foreach (var mask in selectedMasks) - { - topLeft = Vector2.ComponentMin(topLeft, ToLocalSpace(mask.SelectionQuad.TopLeft)); - bottomRight = Vector2.ComponentMax(bottomRight, ToLocalSpace(mask.SelectionQuad.BottomRight)); - } - - topLeft -= new Vector2(5); - bottomRight += new Vector2(5); - - outline.Size = bottomRight - topLeft; - outline.Position = topLeft; - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Types; +using OpenTK; + +namespace osu.Game.Screens.Edit.Screens.Compose.Layers +{ + /// + /// A box which surrounds s and provides interactive handles, context menus etc. + /// + public class MaskSelection : CompositeDrawable + { + public const float BORDER_RADIUS = 2; + + private readonly List selectedMasks; + + private Drawable outline; + + public MaskSelection() + { + selectedMasks = new List(); + + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + Alpha = 0; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChild = outline = new Container + { + Masking = true, + BorderThickness = BORDER_RADIUS, + BorderColour = colours.Yellow, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + AlwaysPresent = true, + Alpha = 0 + } + }; + } + + #region User Input Handling + + public void HandleDrag(HitObjectMask m, InputState state) + { + // Todo: Various forms of snapping + + foreach (var mask in selectedMasks) + { + switch (mask.HitObject.HitObject) + { + case IHasEditablePosition editablePosition: + editablePosition.OffsetPosition(state.Mouse.Delta); + break; + } + } + } + + #endregion + + #region Selection Handling + + /// + /// Bind an action to deselect all selected masks. + /// + public Action DeselectAll { private get; set; } + + /// + /// Handle a mask becoming selected. + /// + /// The mask. + public void HandleSelected(HitObjectMask mask) => selectedMasks.Add(mask); + + /// + /// Handle a mask becoming deselected. + /// + /// The mask. + public void HandleDeselected(HitObjectMask mask) + { + selectedMasks.Remove(mask); + + // We don't want to update visibility if > 0, since we may be deselecting masks during drag-selection + if (selectedMasks.Count == 0) + UpdateVisibility(); + } + + /// + /// Handle a mask requesting selection. + /// + /// The mask. + public void HandleSelectionRequested(HitObjectMask mask, InputState state) + { + if (state.Keyboard.ControlPressed) + { + if (mask.IsSelected) + mask.Deselect(); + else + mask.Select(); + } + else + { + if (mask.IsSelected) + return; + + DeselectAll?.Invoke(); + mask.Select(); + } + + UpdateVisibility(); + } + + #endregion + + /// + /// Updates whether this is visible. + /// + internal void UpdateVisibility() + { + if (selectedMasks.Count > 0) + Show(); + else + Hide(); + } + + protected override void Update() + { + base.Update(); + + if (selectedMasks.Count == 0) + return; + + // Move the rectangle to cover the hitobjects + var topLeft = new Vector2(float.MaxValue, float.MaxValue); + var bottomRight = new Vector2(float.MinValue, float.MinValue); + + bool hasSelection = false; + + foreach (var mask in selectedMasks) + { + topLeft = Vector2.ComponentMin(topLeft, ToLocalSpace(mask.SelectionQuad.TopLeft)); + bottomRight = Vector2.ComponentMax(bottomRight, ToLocalSpace(mask.SelectionQuad.BottomRight)); + } + + topLeft -= new Vector2(5); + bottomRight += new Vector2(5); + + outline.Size = bottomRight - topLeft; + outline.Position = topLeft; + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index e2510ec016..0a009d9958 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -1,123 +1,123 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons -{ - public class DrawableRadioButton : TriangleButton - { - /// - /// Invoked when this has been selected. - /// - public Action Selected; - - private Color4 defaultBackgroundColour; - private Color4 defaultBubbleColour; - private Color4 selectedBackgroundColour; - private Color4 selectedBubbleColour; - - private readonly Drawable bubble; - private readonly RadioButton button; - - public DrawableRadioButton(RadioButton button) - { - this.button = button; - - Text = button.Text; - Action = button.Action; - - RelativeSizeAxes = Axes.X; - - bubble = new CircularContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Scale = new Vector2(0.5f), - X = 10, - Masking = true, - Blending = BlendingMode.Additive, - Child = new Box { RelativeSizeAxes = Axes.Both } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - defaultBackgroundColour = colours.Gray3; - defaultBubbleColour = defaultBackgroundColour.Darken(0.5f); - selectedBackgroundColour = colours.BlueDark; - selectedBubbleColour = selectedBackgroundColour.Lighten(0.5f); - - Triangles.Alpha = 0; - - Content.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 2, - Offset = new Vector2(0, 1), - Colour = Color4.Black.Opacity(0.5f) - }; - - Add(bubble); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - button.Selected.ValueChanged += v => - { - updateSelectionState(); - if (v) - Selected?.Invoke(button); - }; - - updateSelectionState(); - } - - private void updateSelectionState() - { - if (!IsLoaded) - return; - - BackgroundColour = button.Selected ? selectedBackgroundColour : defaultBackgroundColour; - bubble.Colour = button.Selected ? selectedBubbleColour : defaultBubbleColour; - } - - protected override bool OnClick(InputState state) - { - if (button.Selected) - return true; - - if (!Enabled) - return true; - - button.Selected.Value = true; - - return base.OnClick(state); - } - - protected override SpriteText CreateText() => new OsuSpriteText - { - Depth = -1, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - X = 40f - }; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons +{ + public class DrawableRadioButton : TriangleButton + { + /// + /// Invoked when this has been selected. + /// + public Action Selected; + + private Color4 defaultBackgroundColour; + private Color4 defaultBubbleColour; + private Color4 selectedBackgroundColour; + private Color4 selectedBubbleColour; + + private readonly Drawable bubble; + private readonly RadioButton button; + + public DrawableRadioButton(RadioButton button) + { + this.button = button; + + Text = button.Text; + Action = button.Action; + + RelativeSizeAxes = Axes.X; + + bubble = new CircularContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(0.5f), + X = 10, + Masking = true, + Blending = BlendingMode.Additive, + Child = new Box { RelativeSizeAxes = Axes.Both } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + defaultBackgroundColour = colours.Gray3; + defaultBubbleColour = defaultBackgroundColour.Darken(0.5f); + selectedBackgroundColour = colours.BlueDark; + selectedBubbleColour = selectedBackgroundColour.Lighten(0.5f); + + Triangles.Alpha = 0; + + Content.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 2, + Offset = new Vector2(0, 1), + Colour = Color4.Black.Opacity(0.5f) + }; + + Add(bubble); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + button.Selected.ValueChanged += v => + { + updateSelectionState(); + if (v) + Selected?.Invoke(button); + }; + + updateSelectionState(); + } + + private void updateSelectionState() + { + if (!IsLoaded) + return; + + BackgroundColour = button.Selected ? selectedBackgroundColour : defaultBackgroundColour; + bubble.Colour = button.Selected ? selectedBubbleColour : defaultBubbleColour; + } + + protected override bool OnClick(InputState state) + { + if (button.Selected) + return true; + + if (!Enabled) + return true; + + button.Selected.Value = true; + + return base.OnClick(state); + } + + protected override SpriteText CreateText() => new OsuSpriteText + { + Depth = -1, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + X = 40f + }; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs index 5c28cc106a..09fe34bedc 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs @@ -1,51 +1,51 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Configuration; - -namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons -{ - public class RadioButton - { - /// - /// Whether this is selected. - /// - /// - public readonly BindableBool Selected; - - /// - /// The text that should be displayed in this button. - /// - public string Text; - - /// - /// The that should be invoked when this button is selected. - /// - public Action Action; - - public RadioButton(string text, Action action) - { - Text = text; - Action = action; - Selected = new BindableBool(); - } - - public RadioButton(string text) - : this(text, null) - { - Text = text; - Action = null; - } - - /// - /// Selects this . - /// - public void Select() => Selected.Value = true; - - /// - /// Deselects this . - /// - public void Deselect() => Selected.Value = false; - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Configuration; + +namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons +{ + public class RadioButton + { + /// + /// Whether this is selected. + /// + /// + public readonly BindableBool Selected; + + /// + /// The text that should be displayed in this button. + /// + public string Text; + + /// + /// The that should be invoked when this button is selected. + /// + public Action Action; + + public RadioButton(string text, Action action) + { + Text = text; + Action = action; + Selected = new BindableBool(); + } + + public RadioButton(string text) + : this(text, null) + { + Text = text; + Action = null; + } + + /// + /// Selects this . + /// + public void Select() => Selected.Value = true; + + /// + /// Deselects this . + /// + public void Deselect() => Selected.Value = false; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index f178f739dd..7ba16b3d3e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -1,61 +1,61 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using OpenTK; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons -{ - public class RadioButtonCollection : CompositeDrawable - { - private IReadOnlyList items; - public IReadOnlyList Items - { - get { return items; } - set - { - if (ReferenceEquals(items, value)) - return; - items = value; - - buttonContainer.Clear(); - items.ForEach(addButton); - } - } - - private readonly FlowContainer buttonContainer; - - public RadioButtonCollection() - { - AutoSizeAxes = Axes.Y; - - InternalChild = buttonContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5) - }; - } - - private RadioButton currentlySelected; - private void addButton(RadioButton button) - { - button.Selected.ValueChanged += v => - { - if (v) - { - currentlySelected?.Deselect(); - currentlySelected = button; - } - else - currentlySelected = null; - }; - - buttonContainer.Add(new DrawableRadioButton(button)); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons +{ + public class RadioButtonCollection : CompositeDrawable + { + private IReadOnlyList items; + public IReadOnlyList Items + { + get { return items; } + set + { + if (ReferenceEquals(items, value)) + return; + items = value; + + buttonContainer.Clear(); + items.ForEach(addButton); + } + } + + private readonly FlowContainer buttonContainer; + + public RadioButtonCollection() + { + AutoSizeAxes = Axes.Y; + + InternalChild = buttonContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5) + }; + } + + private RadioButton currentlySelected; + private void addButton(RadioButton button) + { + button.Selected.ValueChanged += v => + { + if (v) + { + currentlySelected?.Deselect(); + currentlySelected = button; + } + else + currentlySelected = null; + }; + + buttonContainer.Add(new DrawableRadioButton(button)); + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/BeatmapWaveformGraph.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/BeatmapWaveformGraph.cs index f82ff5f286..72dda24b62 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/BeatmapWaveformGraph.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/BeatmapWaveformGraph.cs @@ -1,33 +1,33 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Audio; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; - -namespace osu.Game.Screens.Edit.Screens.Compose.Timeline -{ - public class BeatmapWaveformGraph : CompositeDrawable - { - public readonly Bindable Beatmap = new Bindable(); - - private readonly WaveformGraph graph; - - public BeatmapWaveformGraph() - { - InternalChild = graph = new WaveformGraph { RelativeSizeAxes = Axes.Both }; - Beatmap.ValueChanged += b => graph.Waveform = b.Waveform; - } - - /// - /// Gets or sets the . - /// - public float Resolution - { - get { return graph.Resolution; } - set { graph.Resolution = value; } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Audio; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; + +namespace osu.Game.Screens.Edit.Screens.Compose.Timeline +{ + public class BeatmapWaveformGraph : CompositeDrawable + { + public readonly Bindable Beatmap = new Bindable(); + + private readonly WaveformGraph graph; + + public BeatmapWaveformGraph() + { + InternalChild = graph = new WaveformGraph { RelativeSizeAxes = Axes.Both }; + Beatmap.ValueChanged += b => graph.Waveform = b.Waveform; + } + + /// + /// Gets or sets the . + /// + public float Resolution + { + get { return graph.Resolution; } + set { graph.Resolution = value; } + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollableTimeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollableTimeline.cs index c308b2b9f8..3223c08c1f 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollableTimeline.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollableTimeline.cs @@ -1,131 +1,131 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Screens.Edit.Screens.Compose.Timeline -{ - public class ScrollableTimeline : CompositeDrawable - { - public readonly Bindable Beatmap = new Bindable(); - - private readonly ScrollingTimelineContainer timelineContainer; - - public ScrollableTimeline() - { - Masking = true; - CornerRadius = 5; - - OsuCheckbox hitObjectsCheckbox; - OsuCheckbox hitSoundsCheckbox; - OsuCheckbox waveformCheckbox; - InternalChildren = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex("111") - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new Container - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex("222") - }, - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Y, - Width = 160, - Padding = new MarginPadding { Horizontal = 15 }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 4), - Children = new[] - { - hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hitobjects" }, - hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hitsounds" }, - waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" } - } - } - } - }, - new Container - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex("333") - }, - new Container - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - Children = new[] - { - new TimelineButton - { - RelativeSizeAxes = Axes.Y, - Height = 0.5f, - Icon = FontAwesome.fa_search_plus, - Action = () => timelineContainer.Zoom++ - }, - new TimelineButton - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Y, - Height = 0.5f, - Icon = FontAwesome.fa_search_minus, - Action = () => timelineContainer.Zoom-- - }, - } - } - } - }, - timelineContainer = new ScrollingTimelineContainer { RelativeSizeAxes = Axes.Y } - } - } - }; - - hitObjectsCheckbox.Current.Value = true; - hitSoundsCheckbox.Current.Value = true; - waveformCheckbox.Current.Value = true; - - timelineContainer.Beatmap.BindTo(Beatmap); - timelineContainer.WaveformVisible.BindTo(waveformCheckbox.Current); - } - - protected override void Update() - { - base.Update(); - - timelineContainer.Size = new Vector2(DrawSize.X - timelineContainer.DrawPosition.X, 1); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Screens.Compose.Timeline +{ + public class ScrollableTimeline : CompositeDrawable + { + public readonly Bindable Beatmap = new Bindable(); + + private readonly ScrollingTimelineContainer timelineContainer; + + public ScrollableTimeline() + { + Masking = true; + CornerRadius = 5; + + OsuCheckbox hitObjectsCheckbox; + OsuCheckbox hitSoundsCheckbox; + OsuCheckbox waveformCheckbox; + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex("111") + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new Container + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex("222") + }, + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Y, + Width = 160, + Padding = new MarginPadding { Horizontal = 15 }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 4), + Children = new[] + { + hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hitobjects" }, + hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hitsounds" }, + waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" } + } + } + } + }, + new Container + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex("333") + }, + new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + Children = new[] + { + new TimelineButton + { + RelativeSizeAxes = Axes.Y, + Height = 0.5f, + Icon = FontAwesome.fa_search_plus, + Action = () => timelineContainer.Zoom++ + }, + new TimelineButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Y, + Height = 0.5f, + Icon = FontAwesome.fa_search_minus, + Action = () => timelineContainer.Zoom-- + }, + } + } + } + }, + timelineContainer = new ScrollingTimelineContainer { RelativeSizeAxes = Axes.Y } + } + } + }; + + hitObjectsCheckbox.Current.Value = true; + hitSoundsCheckbox.Current.Value = true; + waveformCheckbox.Current.Value = true; + + timelineContainer.Beatmap.BindTo(Beatmap); + timelineContainer.WaveformVisible.BindTo(waveformCheckbox.Current); + } + + protected override void Update() + { + base.Update(); + + timelineContainer.Size = new Vector2(DrawSize.X - timelineContainer.DrawPosition.X, 1); + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs index f71607a6cf..83aa86ba61 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs @@ -1,141 +1,141 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Game.Beatmaps; -using osu.Game.Graphics; - -namespace osu.Game.Screens.Edit.Screens.Compose.Timeline -{ - public class ScrollingTimelineContainer : ScrollContainer - { - public readonly Bindable HitObjectsVisible = new Bindable(); - public readonly Bindable HitSoundsVisible = new Bindable(); - public readonly Bindable WaveformVisible = new Bindable(); - public readonly Bindable Beatmap = new Bindable(); - - private readonly BeatmapWaveformGraph waveform; - - public ScrollingTimelineContainer() - : base(Direction.Horizontal) - { - Masking = true; - - Add(waveform = new BeatmapWaveformGraph - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex("222"), - Depth = float.MaxValue - }); - - Content.AutoSizeAxes = Axes.None; - Content.RelativeSizeAxes = Axes.Both; - - waveform.Beatmap.BindTo(Beatmap); - WaveformVisible.ValueChanged += waveformVisibilityChanged; - - Zoom = 10; - } - - private float minZoom = 1; - /// - /// The minimum zoom level allowed. - /// - public float MinZoom - { - get { return minZoom; } - set - { - if (value <= 0) - throw new ArgumentOutOfRangeException(nameof(value)); - if (minZoom == value) - return; - minZoom = value; - - // Update the zoom level - Zoom = Zoom; - } - } - - private float maxZoom = 30; - /// - /// The maximum zoom level allowed. - /// - public float MaxZoom - { - get { return maxZoom; } - set - { - if (value <= 0) - throw new ArgumentOutOfRangeException(nameof(value)); - if (maxZoom == value) - return; - maxZoom = value; - - // Update the zoom level - Zoom = Zoom; - } - } - - private float zoom = 1; - /// - /// The current zoom level. - /// - public float Zoom - { - get { return zoom; } - set - { - value = MathHelper.Clamp(value, MinZoom, MaxZoom); - if (zoom == value) - return; - zoom = value; - - // Make the zoom target default to the center of the graph if it hasn't been set - if (relativeContentZoomTarget == null) - relativeContentZoomTarget = ToSpaceOfOtherDrawable(DrawSize / 2, Content).X / Content.DrawSize.X; - if (localZoomTarget == null) - localZoomTarget = DrawSize.X / 2; - - Content.ResizeWidthTo(Zoom); - - // Update the scroll position to focus on the zoom target - float scrollPos = Content.DrawSize.X * relativeContentZoomTarget.Value - localZoomTarget.Value; - ScrollTo(scrollPos, false); - - relativeContentZoomTarget = null; - localZoomTarget = null; - } - } - - /// - /// Zoom target as a relative position in the space. - /// - private float? relativeContentZoomTarget; - - /// - /// Zoom target as a position in our local space. - /// - private float? localZoomTarget; - - protected override bool OnWheel(InputState state) - { - if (!state.Keyboard.ControlPressed) - return base.OnWheel(state); - - relativeContentZoomTarget = Content.ToLocalSpace(state.Mouse.NativeState.Position).X / Content.DrawSize.X; - localZoomTarget = ToLocalSpace(state.Mouse.NativeState.Position).X; - - Zoom += state.Mouse.WheelDelta; - - return true; - } - - private void waveformVisibilityChanged(bool visible) => waveform.FadeTo(visible ? 1 : 0, 200, Easing.OutQuint); - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Edit.Screens.Compose.Timeline +{ + public class ScrollingTimelineContainer : ScrollContainer + { + public readonly Bindable HitObjectsVisible = new Bindable(); + public readonly Bindable HitSoundsVisible = new Bindable(); + public readonly Bindable WaveformVisible = new Bindable(); + public readonly Bindable Beatmap = new Bindable(); + + private readonly BeatmapWaveformGraph waveform; + + public ScrollingTimelineContainer() + : base(Direction.Horizontal) + { + Masking = true; + + Add(waveform = new BeatmapWaveformGraph + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex("222"), + Depth = float.MaxValue + }); + + Content.AutoSizeAxes = Axes.None; + Content.RelativeSizeAxes = Axes.Both; + + waveform.Beatmap.BindTo(Beatmap); + WaveformVisible.ValueChanged += waveformVisibilityChanged; + + Zoom = 10; + } + + private float minZoom = 1; + /// + /// The minimum zoom level allowed. + /// + public float MinZoom + { + get { return minZoom; } + set + { + if (value <= 0) + throw new ArgumentOutOfRangeException(nameof(value)); + if (minZoom == value) + return; + minZoom = value; + + // Update the zoom level + Zoom = Zoom; + } + } + + private float maxZoom = 30; + /// + /// The maximum zoom level allowed. + /// + public float MaxZoom + { + get { return maxZoom; } + set + { + if (value <= 0) + throw new ArgumentOutOfRangeException(nameof(value)); + if (maxZoom == value) + return; + maxZoom = value; + + // Update the zoom level + Zoom = Zoom; + } + } + + private float zoom = 1; + /// + /// The current zoom level. + /// + public float Zoom + { + get { return zoom; } + set + { + value = MathHelper.Clamp(value, MinZoom, MaxZoom); + if (zoom == value) + return; + zoom = value; + + // Make the zoom target default to the center of the graph if it hasn't been set + if (relativeContentZoomTarget == null) + relativeContentZoomTarget = ToSpaceOfOtherDrawable(DrawSize / 2, Content).X / Content.DrawSize.X; + if (localZoomTarget == null) + localZoomTarget = DrawSize.X / 2; + + Content.ResizeWidthTo(Zoom); + + // Update the scroll position to focus on the zoom target + float scrollPos = Content.DrawSize.X * relativeContentZoomTarget.Value - localZoomTarget.Value; + ScrollTo(scrollPos, false); + + relativeContentZoomTarget = null; + localZoomTarget = null; + } + } + + /// + /// Zoom target as a relative position in the space. + /// + private float? relativeContentZoomTarget; + + /// + /// Zoom target as a position in our local space. + /// + private float? localZoomTarget; + + protected override bool OnWheel(InputState state) + { + if (!state.Keyboard.ControlPressed) + return base.OnWheel(state); + + relativeContentZoomTarget = Content.ToLocalSpace(state.Mouse.NativeState.Position).X / Content.DrawSize.X; + localZoomTarget = ToLocalSpace(state.Mouse.NativeState.Position).X; + + Zoom += state.Mouse.WheelDelta; + + return true; + } + + private void waveformVisibilityChanged(bool visible) => waveform.FadeTo(visible ? 1 : 0, 200, Easing.OutQuint); + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs index a0802f0d7d..f46cc7ef9d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineButton.cs @@ -1,52 +1,52 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Screens.Edit.Screens.Compose.Timeline -{ - public class TimelineButton : CompositeDrawable - { - public Action Action; - public readonly BindableBool Enabled = new BindableBool(true); - - public FontAwesome Icon - { - get { return button.Icon; } - set { button.Icon = value; } - } - - private readonly IconButton button; - - public TimelineButton() - { - InternalChild = button = new IconButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - IconColour = OsuColour.Gray(0.35f), - IconHoverColour = Color4.White, - HoverColour = OsuColour.Gray(0.25f), - FlashColour = OsuColour.Gray(0.5f), - Action = () => Action?.Invoke() - }; - - button.Enabled.BindTo(Enabled); - Width = button.ButtonSize.X; - } - - protected override void Update() - { - base.Update(); - - button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Screens.Compose.Timeline +{ + public class TimelineButton : CompositeDrawable + { + public Action Action; + public readonly BindableBool Enabled = new BindableBool(true); + + public FontAwesome Icon + { + get { return button.Icon; } + set { button.Icon = value; } + } + + private readonly IconButton button; + + public TimelineButton() + { + InternalChild = button = new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + IconColour = OsuColour.Gray(0.35f), + IconHoverColour = Color4.White, + HoverColour = OsuColour.Gray(0.25f), + FlashColour = OsuColour.Gray(0.5f), + Action = () => Action?.Invoke() + }; + + button.Enabled.BindTo(Enabled); + Width = button.ButtonSize.X; + } + + protected override void Update() + { + base.Update(); + + button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight); + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Design/Design.cs b/osu.Game/Screens/Edit/Screens/Design/Design.cs index 16a71846ac..052a1c1d90 100644 --- a/osu.Game/Screens/Edit/Screens/Design/Design.cs +++ b/osu.Game/Screens/Edit/Screens/Design/Design.cs @@ -1,52 +1,52 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Sprites; -using OpenTK.Graphics; - -namespace osu.Game.Screens.Edit.Screens.Design -{ - public class Design : EditorScreen - { - public Design() - { - Add(new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.35f - }, - new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f - }, - new Container - { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding(20), - Child = new OsuSpriteText { Text = "Design screen" } - } - } - } - } - }); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Edit.Screens.Design +{ + public class Design : EditorScreen + { + public Design() + { + Add(new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.35f + }, + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f + }, + new Container + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + Child = new OsuSpriteText { Text = "Design screen" } + } + } + } + } + }); + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs index 009830502e..f70c462cd8 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreen.cs @@ -1,44 +1,44 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; - -namespace osu.Game.Screens.Edit.Screens -{ - /// - /// TODO: eventually make this inherit Screen and add a local scren stack inside the Editor. - /// - public class EditorScreen : Container - { - public readonly Bindable Beatmap = new Bindable(); - - protected override Container Content => content; - private readonly Container content; - - public EditorScreen() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - RelativeSizeAxes = Axes.Both; - - InternalChild = content = new Container { RelativeSizeAxes = Axes.Both }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - this.FadeTo(0) - .Then() - .FadeTo(1f, 250, Easing.OutQuint); - } - - public void Exit() - { - this.FadeOut(250).Expire(); - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; + +namespace osu.Game.Screens.Edit.Screens +{ + /// + /// TODO: eventually make this inherit Screen and add a local scren stack inside the Editor. + /// + public class EditorScreen : Container + { + public readonly Bindable Beatmap = new Bindable(); + + protected override Container Content => content; + private readonly Container content; + + public EditorScreen() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + + InternalChild = content = new Container { RelativeSizeAxes = Axes.Both }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.FadeTo(0) + .Then() + .FadeTo(1f, 250, Easing.OutQuint); + } + + public void Exit() + { + this.FadeOut(250).Expire(); + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/EditorScreenMode.cs b/osu.Game/Screens/Edit/Screens/EditorScreenMode.cs index 67e50b34ac..be8363680d 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreenMode.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreenMode.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Screens.Edit.Screens -{ - public enum EditorScreenMode - { - [Description("setup")] - SongSetup, - [Description("compose")] - Compose, - [Description("design")] - Design, - [Description("timing")] - Timing, - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Screens.Edit.Screens +{ + public enum EditorScreenMode + { + [Description("setup")] + SongSetup, + [Description("compose")] + Compose, + [Description("design")] + Design, + [Description("timing")] + Timing, + } +} diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 94238285ad..1d152361df 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -1,118 +1,118 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shaders; -using osu.Game.Screens.Menu; -using OpenTK; -using osu.Framework.Screens; - -namespace osu.Game.Screens -{ - public class Loader : OsuScreen - { - private bool showDisclaimer; - - public override bool ShowOverlaysOnEnter => false; - - public Loader() - { - ValidForResume = false; - } - - protected override void LogoArriving(OsuLogo logo, bool resuming) - { - base.LogoArriving(logo, resuming); - - logo.Triangles = false; - logo.Origin = Anchor.BottomRight; - logo.Anchor = Anchor.BottomRight; - logo.Position = new Vector2(-40); - logo.Scale = new Vector2(0.2f); - - logo.FadeInFromZero(5000, Easing.OutQuint); - } - - private OsuScreen loadScreen; - private ShaderPrecompiler precompiler; - - protected override void OnEntering(Screen last) - { - base.OnEntering(last); - - LoadComponentAsync(precompiler = new ShaderPrecompiler(loadIfReady), Add); - LoadComponentAsync(loadScreen = showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(), s => loadIfReady()); - } - - private void loadIfReady() - { - if (ChildScreen == loadScreen) return; - - if (loadScreen.LoadState != LoadState.Ready) - return; - - if (!precompiler.FinishedCompiling) - return; - - Push(loadScreen); - } - - protected override void LogoSuspending(OsuLogo logo) - { - base.LogoSuspending(logo); - logo.FadeOut(100); - } - - [BackgroundDependencyLoader] - private void load(OsuGameBase game) - { - showDisclaimer = game.IsDeployedBuild; - } - - /// - /// Compiles a set of shaders before continuing. Attempts to draw some frames between compilation by limiting to one compile per draw frame. - /// - public class ShaderPrecompiler : Drawable - { - private readonly Action onLoaded; - private readonly List loadTargets = new List(); - - public bool FinishedCompiling { get; private set; } - - public ShaderPrecompiler(Action onLoaded) - { - this.onLoaded = onLoaded; - } - - [BackgroundDependencyLoader] - private void load(ShaderManager manager) - { - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED)); - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR)); - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE)); - - loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE)); - - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE_ROUNDED)); - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); - } - - protected override void Update() - { - base.Update(); - - // if our target is null we are done. - if (loadTargets.All(s => s.Loaded)) - { - FinishedCompiling = true; - Expire(); - onLoaded?.Invoke(); - } - } - } - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shaders; +using osu.Game.Screens.Menu; +using OpenTK; +using osu.Framework.Screens; + +namespace osu.Game.Screens +{ + public class Loader : OsuScreen + { + private bool showDisclaimer; + + public override bool ShowOverlaysOnEnter => false; + + public Loader() + { + ValidForResume = false; + } + + protected override void LogoArriving(OsuLogo logo, bool resuming) + { + base.LogoArriving(logo, resuming); + + logo.Triangles = false; + logo.Origin = Anchor.BottomRight; + logo.Anchor = Anchor.BottomRight; + logo.Position = new Vector2(-40); + logo.Scale = new Vector2(0.2f); + + logo.FadeInFromZero(5000, Easing.OutQuint); + } + + private OsuScreen loadScreen; + private ShaderPrecompiler precompiler; + + protected override void OnEntering(Screen last) + { + base.OnEntering(last); + + LoadComponentAsync(precompiler = new ShaderPrecompiler(loadIfReady), Add); + LoadComponentAsync(loadScreen = showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(), s => loadIfReady()); + } + + private void loadIfReady() + { + if (ChildScreen == loadScreen) return; + + if (loadScreen.LoadState != LoadState.Ready) + return; + + if (!precompiler.FinishedCompiling) + return; + + Push(loadScreen); + } + + protected override void LogoSuspending(OsuLogo logo) + { + base.LogoSuspending(logo); + logo.FadeOut(100); + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + showDisclaimer = game.IsDeployedBuild; + } + + /// + /// Compiles a set of shaders before continuing. Attempts to draw some frames between compilation by limiting to one compile per draw frame. + /// + public class ShaderPrecompiler : Drawable + { + private readonly Action onLoaded; + private readonly List loadTargets = new List(); + + public bool FinishedCompiling { get; private set; } + + public ShaderPrecompiler(Action onLoaded) + { + this.onLoaded = onLoaded; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager manager) + { + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE)); + + loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE)); + + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE_ROUNDED)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); + } + + protected override void Update() + { + base.Update(); + + // if our target is null we are done. + if (loadTargets.All(s => s.Loaded)) + { + FinishedCompiling = true; + Expire(); + onLoaded?.Invoke(); + } + } + } + } +} diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index 268be51347..33e423a558 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -1,287 +1,287 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Extensions.Color4Extensions; -using osu.Game.Graphics.Containers; -using osu.Framework.Audio.Track; -using osu.Game.Beatmaps.ControlPoints; - -namespace osu.Game.Screens.Menu -{ - /// - /// Button designed specifically for the osu!next main menu. - /// In order to correctly flow, we have to use a negative margin on the parent container (due to the parallelogram shape). - /// - public class Button : BeatSyncedContainer, IStateful - { - public event Action StateChanged; - - private readonly Container iconText; - private readonly Container box; - private readonly Box boxHoverLayer; - private readonly SpriteIcon icon; - private readonly string sampleName; - private readonly Action clickAction; - private readonly Key triggerKey; - private SampleChannel sampleClick; - private SampleChannel sampleHover; - - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => box.ReceiveMouseInputAt(screenSpacePos); - - public Button(string text, string sampleName, FontAwesome symbol, Color4 colour, Action clickAction = null, float extraWidth = 0, Key triggerKey = Key.Unknown) - { - this.sampleName = sampleName; - this.clickAction = clickAction; - this.triggerKey = triggerKey; - - AutoSizeAxes = Axes.Both; - Alpha = 0; - - Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonSystem.BUTTON_AREA_HEIGHT); - - Children = new Drawable[] - { - box = new Container - { - Masking = true, - MaskingSmoothness = 2, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.2f), - Roundness = 5, - Radius = 8, - }, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0, 1), - Size = boxSize, - Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / boxSize.Y, 0), - Children = new[] - { - new Box - { - EdgeSmoothness = new Vector2(1.5f, 0), - RelativeSizeAxes = Axes.Both, - Colour = colour, - }, - boxHoverLayer = new Box - { - EdgeSmoothness = new Vector2(1.5f, 0), - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Colour = Color4.White, - Alpha = 0, - }, - } - }, - iconText = new Container - { - AutoSizeAxes = Axes.Both, - Position = new Vector2(extraWidth / 2, 0), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new Drawable[] - { - icon = new SpriteIcon - { - Shadow = true, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(30), - Position = new Vector2(0, 0), - Icon = symbol - }, - new OsuSpriteText - { - Shadow = true, - AllowMultiline = false, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextSize = 16, - Position = new Vector2(0, 35), - Text = text - } - } - } - }; - } - - private bool rightward; - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - if (!IsHovered) return; - - double duration = timingPoint.BeatLength / 2; - - icon.RotateTo(rightward ? 10 : -10, duration * 2, Easing.InOutSine); - - icon.Animate( - i => i.MoveToY(-10, duration, Easing.Out), - i => i.ScaleTo(1, duration, Easing.Out) - ).Then( - i => i.MoveToY(0, duration, Easing.In), - i => i.ScaleTo(new Vector2(1, 0.9f), duration, Easing.In) - ); - - rightward = !rightward; - } - - protected override bool OnHover(InputState state) - { - if (State != ButtonState.Expanded) return true; - - sampleHover?.Play(); - - box.ScaleTo(new Vector2(1.5f, 1), 500, Easing.OutElastic); - - double duration = TimeUntilNextBeat; - - icon.ClearTransforms(); - icon.RotateTo(rightward ? -10 : 10, duration, Easing.InOutSine); - icon.ScaleTo(new Vector2(1, 0.9f), duration, Easing.Out); - return true; - } - - protected override void OnHoverLost(InputState state) - { - icon.ClearTransforms(); - icon.RotateTo(0, 500, Easing.Out); - icon.MoveTo(Vector2.Zero, 500, Easing.Out); - icon.ScaleTo(Vector2.One, 200, Easing.Out); - - if (State == ButtonState.Expanded) - box.ScaleTo(new Vector2(1, 1), 500, Easing.OutElastic); - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleHover = audio.Sample.Get(@"Menu/button-hover"); - if (!string.IsNullOrEmpty(sampleName)) - sampleClick = audio.Sample.Get($@"Menu/{sampleName}"); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - boxHoverLayer.FadeTo(0.1f, 1000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - boxHoverLayer.FadeTo(0, 1000, Easing.OutQuint); - return base.OnMouseUp(state, args); - } - - protected override bool OnClick(InputState state) - { - trigger(); - return true; - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (args.Repeat || state.Keyboard.ControlPressed || state.Keyboard.ShiftPressed || state.Keyboard.AltPressed) - return false; - - if (triggerKey == args.Key && triggerKey != Key.Unknown) - { - trigger(); - return true; - } - - return false; - } - - private void trigger() - { - sampleClick?.Play(); - - clickAction?.Invoke(); - - boxHoverLayer.ClearTransforms(); - boxHoverLayer.Alpha = 0.9f; - boxHoverLayer.FadeOut(800, Easing.OutExpo); - } - - public override bool HandleKeyboardInput => state != ButtonState.Exploded; - public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; - - protected override void Update() - { - iconText.Alpha = MathHelper.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1); - base.Update(); - } - - public int ContractStyle; - - private ButtonState state; - - public ButtonState State - { - get { return state; } - - set - { - if (state == value) - return; - - state = value; - - switch (state) - { - case ButtonState.Contracted: - switch (ContractStyle) - { - default: - box.ScaleTo(new Vector2(0, 1), 500, Easing.OutExpo); - this.FadeOut(500); - break; - case 1: - box.ScaleTo(new Vector2(0, 1), 400, Easing.InSine); - this.FadeOut(800); - break; - } - break; - case ButtonState.Expanded: - const int expand_duration = 500; - box.ScaleTo(new Vector2(1, 1), expand_duration, Easing.OutExpo); - this.FadeIn(expand_duration / 6f); - break; - case ButtonState.Exploded: - const int explode_duration = 200; - box.ScaleTo(new Vector2(2, 1), explode_duration, Easing.OutExpo); - this.FadeOut(explode_duration / 4f * 3); - break; - } - - StateChanged?.Invoke(State); - } - } - } - - public enum ButtonState - { - Contracted, - Expanded, - Exploded - } -} +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; +using osu.Framework.Audio.Track; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Screens.Menu +{ + /// + /// Button designed specifically for the osu!next main menu. + /// In order to correctly flow, we have to use a negative margin on the parent container (due to the parallelogram shape). + /// + public class Button : BeatSyncedContainer, IStateful + { + public event Action StateChanged; + + private readonly Container iconText; + private readonly Container box; + private readonly Box boxHoverLayer; + private readonly SpriteIcon icon; + private readonly string sampleName; + private readonly Action clickAction; + private readonly Key triggerKey; + private SampleChannel sampleClick; + private SampleChannel sampleHover; + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => box.ReceiveMouseInputAt(screenSpacePos); + + public Button(string text, string sampleName, FontAwesome symbol, Color4 colour, Action clickAction = null, float extraWidth = 0, Key triggerKey = Key.Unknown) + { + this.sampleName = sampleName; + this.clickAction = clickAction; + this.triggerKey = triggerKey; + + AutoSizeAxes = Axes.Both; + Alpha = 0; + + Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonSystem.BUTTON_AREA_HEIGHT); + + Children = new Drawable[] + { + box = new Container + { + Masking = true, + MaskingSmoothness = 2, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Roundness = 5, + Radius = 8, + }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0, 1), + Size = boxSize, + Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / boxSize.Y, 0), + Children = new[] + { + new Box + { + EdgeSmoothness = new Vector2(1.5f, 0), + RelativeSizeAxes = Axes.Both, + Colour = colour, + }, + boxHoverLayer = new Box + { + EdgeSmoothness = new Vector2(1.5f, 0), + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Colour = Color4.White, + Alpha = 0, + }, + } + }, + iconText = new Container + { + AutoSizeAxes = Axes.Both, + Position = new Vector2(extraWidth / 2, 0), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + icon = new SpriteIcon + { + Shadow = true, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(30), + Position = new Vector2(0, 0), + Icon = symbol + }, + new OsuSpriteText + { + Shadow = true, + AllowMultiline = false, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextSize = 16, + Position = new Vector2(0, 35), + Text = text + } + } + } + }; + } + + private bool rightward; + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsHovered) return; + + double duration = timingPoint.BeatLength / 2; + + icon.RotateTo(rightward ? 10 : -10, duration * 2, Easing.InOutSine); + + icon.Animate( + i => i.MoveToY(-10, duration, Easing.Out), + i => i.ScaleTo(1, duration, Easing.Out) + ).Then( + i => i.MoveToY(0, duration, Easing.In), + i => i.ScaleTo(new Vector2(1, 0.9f), duration, Easing.In) + ); + + rightward = !rightward; + } + + protected override bool OnHover(InputState state) + { + if (State != ButtonState.Expanded) return true; + + sampleHover?.Play(); + + box.ScaleTo(new Vector2(1.5f, 1), 500, Easing.OutElastic); + + double duration = TimeUntilNextBeat; + + icon.ClearTransforms(); + icon.RotateTo(rightward ? -10 : 10, duration, Easing.InOutSine); + icon.ScaleTo(new Vector2(1, 0.9f), duration, Easing.Out); + return true; + } + + protected override void OnHoverLost(InputState state) + { + icon.ClearTransforms(); + icon.RotateTo(0, 500, Easing.Out); + icon.MoveTo(Vector2.Zero, 500, Easing.Out); + icon.ScaleTo(Vector2.One, 200, Easing.Out); + + if (State == ButtonState.Expanded) + box.ScaleTo(new Vector2(1, 1), 500, Easing.OutElastic); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get(@"Menu/button-hover"); + if (!string.IsNullOrEmpty(sampleName)) + sampleClick = audio.Sample.Get($@"Menu/{sampleName}"); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + boxHoverLayer.FadeTo(0.1f, 1000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + boxHoverLayer.FadeTo(0, 1000, Easing.OutQuint); + return base.OnMouseUp(state, args); + } + + protected override bool OnClick(InputState state) + { + trigger(); + return true; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat || state.Keyboard.ControlPressed || state.Keyboard.ShiftPressed || state.Keyboard.AltPressed) + return false; + + if (triggerKey == args.Key && triggerKey != Key.Unknown) + { + trigger(); + return true; + } + + return false; + } + + private void trigger() + { + sampleClick?.Play(); + + clickAction?.Invoke(); + + boxHoverLayer.ClearTransforms(); + boxHoverLayer.Alpha = 0.9f; + boxHoverLayer.FadeOut(800, Easing.OutExpo); + } + + public override bool HandleKeyboardInput => state != ButtonState.Exploded; + public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; + + protected override void Update() + { + iconText.Alpha = MathHelper.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1); + base.Update(); + } + + public int ContractStyle; + + private ButtonState state; + + public ButtonState State + { + get { return state; } + + set + { + if (state == value) + return; + + state = value; + + switch (state) + { + case ButtonState.Contracted: + switch (ContractStyle) + { + default: + box.ScaleTo(new Vector2(0, 1), 500, Easing.OutExpo); + this.FadeOut(500); + break; + case 1: + box.ScaleTo(new Vector2(0, 1), 400, Easing.InSine); + this.FadeOut(800); + break; + } + break; + case ButtonState.Expanded: + const int expand_duration = 500; + box.ScaleTo(new Vector2(1, 1), expand_duration, Easing.OutExpo); + this.FadeIn(expand_duration / 6f); + break; + case ButtonState.Exploded: + const int explode_duration = 200; + box.ScaleTo(new Vector2(2, 1), explode_duration, Easing.OutExpo); + this.FadeOut(explode_duration / 4f * 3); + break; + } + + StateChanged?.Invoke(State); + } + } + } + + public enum ButtonState + { + Contracted, + Expanded, + Exploded + } +} diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 72c26af2a6..f48e1925c5 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -1,376 +1,376 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Game.Graphics; -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Audio.Sample; -using osu.Framework.Audio; -using osu.Framework.Configuration; -using osu.Framework.Threading; - -namespace osu.Game.Screens.Menu -{ - public class ButtonSystem : Container, IStateful - { - public event Action StateChanged; - - private readonly BindableBool showOverlays = new BindableBool(); - - public Action OnEdit; - public Action OnExit; - public Action OnDirect; - public Action OnSolo; - public Action OnSettings; - public Action OnMulti; - public Action OnChart; - public Action OnTest; - - private readonly FlowContainerWithOrigin buttonFlow; - - //todo: make these non-internal somehow. - public const float BUTTON_AREA_HEIGHT = 100; - - public const float BUTTON_WIDTH = 140f; - public const float WEDGE_WIDTH = 20; - - private OsuLogo logo; - - public void SetOsuLogo(OsuLogo logo) - { - this.logo = logo; - - if (this.logo != null) - { - this.logo.Action = onOsuLogo; - - // osuLogo.SizeForFlow relies on loading to be complete. - buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); - - updateLogoState(); - } - } - - private readonly Drawable iconFacade; - private readonly Container buttonArea; - private readonly Box buttonAreaBackground; - - private readonly Button backButton; - private readonly Button settingsButton; - - private readonly List internal bool CanShowCursor = true; - internal readonly MenuCursor MenuCursor; - public CursorContainer Cursor => MenuCursor; + public CursorContainer Cursor { get; } public bool ProvidingUserCursor => true; public CursorOverrideContainer() { AddRangeInternal(new Drawable[] { - MenuCursor = new MenuCursor { State = Visibility.Hidden }, + Cursor = new MenuCursor { State = Visibility.Hidden }, content = new Container { RelativeSizeAxes = Axes.Both } }); } diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 4edac3e050..b0b15c8572 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -12,14 +12,15 @@ using osu.Framework.Input; using osu.Game.Configuration; using System; using System.Diagnostics; +using JetBrains.Annotations; using osu.Framework.Graphics.Textures; namespace osu.Game.Graphics.Cursor { public class MenuCursor : CursorContainer { - public bool ShowCursor = true; - public override bool IsPresent => ShowCursor && base.IsPresent; + private readonly IBindable screenshotCursorVisibility = new Bindable(true); + public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent; protected override Drawable CreateCursor() => new Cursor(); @@ -28,6 +29,17 @@ namespace osu.Game.Graphics.Cursor private bool startRotation; + private ScreenshotManager screenshotManager; + + [BackgroundDependencyLoader(true)] + private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager) + { + cursorRotate = config.GetBindable(OsuSetting.CursorRotation); + + if (screenshotManager != null) + screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility); + } + protected override bool OnMouseMove(InputState state) { if (cursorRotate && dragging) @@ -107,12 +119,6 @@ namespace osu.Game.Graphics.Cursor ActiveCursor.ScaleTo(0.6f, 250, Easing.In); } - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - cursorRotate = config.GetBindable(OsuSetting.CursorRotation); - } - public class Cursor : Container { private Container cursorContainer; diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index d7746647c4..fb111f2786 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -16,7 +16,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Configuration; -using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; @@ -25,6 +24,14 @@ namespace osu.Game.Graphics { public class ScreenshotManager : Container, IKeyBindingHandler, IHandleGlobalInput { + private readonly BindableBool cursorVisibility = new BindableBool(true); + + /// + /// Invoked when screenshots are or have finished being taken, to control whether cursors should be visible. + /// If cursors should not be visible, cursors have 3 frames to hide themselves. + /// + public IBindable CursorVisibility => cursorVisibility; + private Bindable screenshotFormat; private Bindable captureMenuCursor; @@ -33,12 +40,6 @@ namespace osu.Game.Graphics private NotificationOverlay notificationOverlay; private SampleChannel shutter; - private readonly MenuCursor menuCursor; - - public ScreenshotManager(MenuCursor menuCursor) - { - this.menuCursor = menuCursor; - } [BackgroundDependencyLoader] private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio) @@ -76,7 +77,7 @@ namespace osu.Game.Graphics if (!captureMenuCursor.Value) { - menuCursor.ShowCursor = false; + cursorVisibility.Value = false; // We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value const int frames_to_wait = 3; @@ -126,8 +127,8 @@ namespace osu.Game.Graphics { base.Update(); - if (Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0) - menuCursor.ShowCursor = true; + if (cursorVisibility == false && Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0) + cursorVisibility.Value = true; } private string getFileName() From cf658335725ee15bacafee32b438336dd3d52123 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Apr 2018 21:15:08 +0900 Subject: [PATCH 460/537] Reword xmldoc --- osu.Game/Graphics/ScreenshotManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 0626bc98b7..90580c50df 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -27,7 +27,7 @@ namespace osu.Game.Graphics private readonly BindableBool cursorVisibility = new BindableBool(true); /// - /// Invoked when screenshots are or have finished being taken, to control whether cursors should be visible. + /// Changed when screenshots are being or have finished being taken, to control whether cursors should be visible. /// If cursors should not be visible, cursors have 3 frames to hide themselves. /// public IBindable CursorVisibility => cursorVisibility; From a0f9f8d512362547701b5c7340273d10e5211d25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Apr 2018 21:46:17 +0900 Subject: [PATCH 461/537] Update frramework again --- osu-framework | 2 +- osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu-framework b/osu-framework index b210a4bd42..34172ef057 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b210a4bd42ac24e94fa29aaaa29f7fd6bc149b12 +Subproject commit 34172ef057a696f19e109bfb9230a25b4bb993f6 diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c9a7723cb1..2d65d6738d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -439,7 +439,7 @@ namespace osu.Game sensitivity.Value = 1; sensitivity.Disabled = true; - frameworkConfig.Set(FrameworkSetting.IgnoredInputHandler, string.Empty); + frameworkConfig.Set(FrameworkSetting.IgnoredInputHandlers, string.Empty); frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault(); return true; case GlobalAction.ToggleToolbar: diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 19caeb40b7..51a624330b 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input ignoredInputHandler.Value = enabled ? standard_mouse_handler : raw_mouse_handler; }; - ignoredInputHandler = config.GetBindable(FrameworkSetting.IgnoredInputHandler); + ignoredInputHandler = config.GetBindable(FrameworkSetting.IgnoredInputHandlers); ignoredInputHandler.ValueChanged += handler => { bool raw = !handler.Contains("Raw"); From a6dfe0d4c938ff22806da4995e023ba0c7381b74 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Apr 2018 22:24:19 +0900 Subject: [PATCH 462/537] Final framework update --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 34172ef057..eb6362eaf1 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 34172ef057a696f19e109bfb9230a25b4bb993f6 +Subproject commit eb6362eaf1317b0fa27b2c9e559bd9a0f1ce357c From b23b0a445be8628db6d019a61912df1519429d36 Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Fri, 13 Apr 2018 15:41:35 +0200 Subject: [PATCH 463/537] Add support for replay mods --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 40 +++++++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 69 +++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 49 +++++++++++++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 40 +++++++++++ osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 3 + osu.Game/Beatmaps/Legacy/LegacyMods.cs | 43 ++++++++++++ osu.Game/OsuGame.cs | 1 + osu.Game/Rulesets/Ruleset.cs | 3 + .../Scoring/Legacy/LegacyScoreParser.cs | 5 +- 9 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Beatmaps/Legacy/LegacyMods.cs diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 8930b09089..87de1aed97 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Rulesets.Catch { @@ -29,6 +30,45 @@ namespace osu.Game.Rulesets.Catch new KeyBinding(InputKey.Shift, CatchAction.Dash), }; + public override IEnumerable GetLegacyModsFor(LegacyMods mods) + { + if (mods.HasFlag(LegacyMods.NightCore)) + yield return new CatchModNightcore(); + else if (mods.HasFlag(LegacyMods.DoubleTime)) + yield return new CatchModDoubleTime(); + + + if (mods.HasFlag(LegacyMods.AutoPlay)) + yield return new CatchModAutoplay(); + + if (mods.HasFlag(LegacyMods.Easy)) + yield return new CatchModEasy(); + + if (mods.HasFlag(LegacyMods.FlashLight)) + yield return new CatchModFlashlight(); + + if (mods.HasFlag(LegacyMods.HalfTime)) + yield return new CatchModHalfTime(); + + if (mods.HasFlag(LegacyMods.HardRock)) + yield return new CatchModHardRock(); + + if (mods.HasFlag(LegacyMods.Hidden)) + yield return new CatchModHidden(); + + if (mods.HasFlag(LegacyMods.NoFail)) + yield return new CatchModNoFail(); + + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new CatchModPerfect(); + + if (mods.HasFlag(LegacyMods.Relax)) + yield return new CatchModRelax(); + + if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new CatchModSuddenDeath(); + } + public override IEnumerable GetModsFor(ModType type) { switch (type) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 7f37f55d14..4bd243664b 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -14,6 +14,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Rulesets.Mania { @@ -21,6 +22,74 @@ namespace osu.Game.Rulesets.Mania { public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset); + public override IEnumerable GetLegacyModsFor(LegacyMods mods) + { + if (mods.HasFlag(LegacyMods.NightCore)) + yield return new ManiaModNightcore(); + else if (mods.HasFlag(LegacyMods.DoubleTime)) + yield return new ManiaModDoubleTime(); + + if (mods.HasFlag(LegacyMods.AutoPlay)) + yield return new ManiaModAutoplay(); + + if (mods.HasFlag(LegacyMods.Easy)) + yield return new ManiaModEasy(); + + if (mods.HasFlag(LegacyMods.FadeIn)) + yield return new ManiaModFadeIn(); + + if (mods.HasFlag(LegacyMods.FlashLight)) + yield return new ManiaModFlashlight(); + + if (mods.HasFlag(LegacyMods.HalfTime)) + yield return new ManiaModHalfTime(); + + if (mods.HasFlag(LegacyMods.HardRock)) + yield return new ManiaModHardRock(); + + if (mods.HasFlag(LegacyMods.Hidden)) + yield return new ManiaModHidden(); + + if (mods.HasFlag(LegacyMods.Key1)) + yield return new ManiaModKey1(); + + if (mods.HasFlag(LegacyMods.Key2)) + yield return new ManiaModKey2(); + + if (mods.HasFlag(LegacyMods.Key3)) + yield return new ManiaModKey3(); + + if (mods.HasFlag(LegacyMods.Key4)) + yield return new ManiaModKey4(); + + if (mods.HasFlag(LegacyMods.Key5)) + yield return new ManiaModKey5(); + + if (mods.HasFlag(LegacyMods.Key6)) + yield return new ManiaModKey6(); + + if (mods.HasFlag(LegacyMods.Key7)) + yield return new ManiaModKey7(); + + if (mods.HasFlag(LegacyMods.Key8)) + yield return new ManiaModKey8(); + + if (mods.HasFlag(LegacyMods.Key9)) + yield return new ManiaModKey9(); + + if (mods.HasFlag(LegacyMods.NoFail)) + yield return new ManiaModNoFail(); + + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new ManiaModPerfect(); + + if (mods.HasFlag(LegacyMods.Random)) + yield return new ManiaModRandom(); + + if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new ManiaModSuddenDeath(); + } + public override IEnumerable GetModsFor(ModType type) { switch (type) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 37e6ec3817..7611f160ae 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Rulesets.Osu { @@ -66,6 +67,54 @@ namespace osu.Game.Rulesets.Osu }; } + public override IEnumerable GetLegacyModsFor(LegacyMods mods) + { + if (mods.HasFlag(LegacyMods.NightCore)) + yield return new OsuModNightcore(); + else if (mods.HasFlag(LegacyMods.DoubleTime)) + yield return new OsuModDoubleTime(); + + + if (mods.HasFlag(LegacyMods.AutoPilot)) + yield return new OsuModAutopilot(); + + if (mods.HasFlag(LegacyMods.AutoPlay)) + yield return new OsuModAutoplay(); + + if (mods.HasFlag(LegacyMods.Easy)) + yield return new OsuModEasy(); + + if (mods.HasFlag(LegacyMods.FlashLight)) + yield return new OsuModFlashlight(); + + if (mods.HasFlag(LegacyMods.HalfTime)) + yield return new OsuModHalfTime(); + + if (mods.HasFlag(LegacyMods.HardRock)) + yield return new OsuModHardRock(); + + if (mods.HasFlag(LegacyMods.Hidden)) + yield return new OsuModHidden(); + + if (mods.HasFlag(LegacyMods.NoFail)) + yield return new OsuModNoFail(); + + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new OsuModPerfect(); + + if (mods.HasFlag(LegacyMods.Relax)) + yield return new OsuModRelax(); + + if (mods.HasFlag(LegacyMods.SpunOut)) + yield return new OsuModSpunOut(); + + if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new OsuModSuddenDeath(); + + if (mods.HasFlag(LegacyMods.TargetPractice)) + yield return new OsuModTarget(); + } + public override IEnumerable GetModsFor(ModType type) { switch (type) diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index cb5e020601..1881e84fb0 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Taiko.Replays; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Rulesets.Taiko { @@ -31,6 +32,45 @@ namespace osu.Game.Rulesets.Taiko new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim), }; + public override IEnumerable GetLegacyModsFor(LegacyMods mods) + { + if (mods.HasFlag(LegacyMods.NightCore)) + yield return new TaikoModNightcore(); + else if (mods.HasFlag(LegacyMods.DoubleTime)) + yield return new TaikoModDoubleTime(); + + + if (mods.HasFlag(LegacyMods.AutoPlay)) + yield return new TaikoModAutoplay(); + + if (mods.HasFlag(LegacyMods.Easy)) + yield return new TaikoModEasy(); + + if (mods.HasFlag(LegacyMods.FlashLight)) + yield return new TaikoModFlashlight(); + + if (mods.HasFlag(LegacyMods.HalfTime)) + yield return new TaikoModHalfTime(); + + if (mods.HasFlag(LegacyMods.HardRock)) + yield return new TaikoModHardRock(); + + if (mods.HasFlag(LegacyMods.Hidden)) + yield return new TaikoModHidden(); + + if (mods.HasFlag(LegacyMods.NoFail)) + yield return new TaikoModNoFail(); + + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new TaikoModPerfect(); + + if (mods.HasFlag(LegacyMods.Relax)) + yield return new TaikoModRelax(); + + if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new TaikoModSuddenDeath(); + } + public override IEnumerable GetModsFor(ModType type) { switch (type) diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 0424ff84f1..4a33e5eb7e 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Beatmaps { @@ -53,6 +54,8 @@ namespace osu.Game.Beatmaps { public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; + public override IEnumerable GetLegacyModsFor(LegacyMods mods) => new Mod[] { }; + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) { throw new NotImplementedException(); diff --git a/osu.Game/Beatmaps/Legacy/LegacyMods.cs b/osu.Game/Beatmaps/Legacy/LegacyMods.cs new file mode 100644 index 0000000000..2ac7324778 --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacyMods.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Beatmaps.Legacy +{ + [Flags] + public enum LegacyMods + { + None = 0, + NoFail = 1, + Easy = 2, + TouchDevice = 4, + Hidden = 8, + HardRock = 16, + SuddenDeath = 32, + DoubleTime = 64, + Relax = 128, + HalfTime = 256, + NightCore = 512, + FlashLight = 1024, + AutoPlay = 2048, + SpunOut = 4096, + AutoPilot = 8192, + Perfect = 16384, + Key4 = 32768, + Key5 = 65536, + Key6 = 131072, + Key7 = 262144, + Key8 = 524288, + keyMod = 1015808,// k4+k5+k6+k7+k8 + FadeIn = 1048576, + Random = 2097152, + Cinema = 4194304, + TargetPractice = 8388608, + Key9 = 16777216, + Coop = 33554432, + Key1 = 67108864, + Key3 = 134217728, + Key2 = 268435456, + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d1cf372067..6c35a2ecea 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -194,6 +194,7 @@ namespace osu.Game } Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); + Beatmap.Value.Mods.BindTo(new Bindable>(s.Mods)); menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index c2af4d566c..f2cf813d38 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Rulesets { @@ -33,6 +34,8 @@ namespace osu.Game.Rulesets public abstract IEnumerable GetModsFor(ModType type); + public abstract IEnumerable GetLegacyModsFor(LegacyMods mods); + public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay); protected Ruleset(RulesetInfo rulesetInfo = null) diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index 5ee009ba98..806e8d2fdb 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -9,6 +9,9 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Users; using SharpCompress.Compressors.LZMA; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Mods; +using System.Linq; namespace osu.Game.Rulesets.Scoring.Legacy { @@ -64,7 +67,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy /* score.Perfect = */ sr.ReadBoolean(); /* score.EnabledMods = (Mods)*/ - sr.ReadInt32(); + score.Mods = currentRuleset.GetLegacyModsFor((LegacyMods)sr.ReadInt32()).ToArray(); /* score.HpGraphString = */ sr.ReadString(); /* score.Date = */ From 73ba8e1c876d2e57bdda27e8c87ea0f39a63c4df Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Fri, 13 Apr 2018 16:10:01 +0200 Subject: [PATCH 464/537] CI: remove unused directive --- osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index 806e8d2fdb..62d1cb3a32 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -10,7 +10,6 @@ using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Users; using SharpCompress.Compressors.LZMA; using osu.Game.Beatmaps.Legacy; -using osu.Game.Rulesets.Mods; using System.Linq; namespace osu.Game.Rulesets.Scoring.Legacy From 3ece54e1c33a709efba79df82369e3be6006987e Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Sat, 14 Apr 2018 00:08:54 +0200 Subject: [PATCH 465/537] make buttons only accept input when expanded both other states are "inactive"/invisible so should never accept keyboard input --- osu.Game/Screens/Menu/Button.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index 33e423a558..542ddd2c92 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -222,7 +222,7 @@ namespace osu.Game.Screens.Menu boxHoverLayer.FadeOut(800, Easing.OutExpo); } - public override bool HandleKeyboardInput => state != ButtonState.Exploded; + public override bool HandleKeyboardInput => state == ButtonState.Expanded; public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; protected override void Update() From 069d48ac14a29ac0bf20647a5cafe815500697f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Apr 2018 15:29:00 +0900 Subject: [PATCH 466/537] Remove unused variable --- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index ae3c10228b..78a3bd7468 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Direct; using osu.Game.Overlays.Profile.Sections.Beatmaps; namespace osu.Game.Overlays.Profile.Sections @@ -14,8 +13,6 @@ namespace osu.Game.Overlays.Profile.Sections public override string Identifier => "beatmaps"; - private DirectPanel currentlyPlaying; - public BeatmapsSection() { Children = new[] From fd54ae3c87eab25df96bb90990ff1f8dead1d6df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Apr 2018 16:12:42 +0900 Subject: [PATCH 467/537] Simplify logic --- osu.Game/Overlays/Direct/PlayButton.cs | 3 ++- .../Beatmaps/PaginatedBeatmapContainer.cs | 17 +++++++++-------- .../Profile/Sections/BeatmapsSection.cs | 7 ++----- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 9f36d5acb7..44e24d8157 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -173,8 +173,9 @@ namespace osu.Game.Overlays.Direct if (trackLoader != d) return; Preview = d?.Preview; - Playing.TriggerChange(); + updatePreviewTrack(Playing); loading = false; + Add(trackLoader); }); } diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 31433e64b3..3fec9d8697 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -59,13 +59,13 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps panel.PreviewPlaying.ValueChanged += isPlaying => { - if (!isPlaying) return; + StopPlayingPreview(); - BeganPlayingPreview?.Invoke(this); - if (currentlyPlaying != null && currentlyPlaying != panel) - StopPlayingPreview(); - - currentlyPlaying = panel; + if (isPlaying) + { + BeganPlayingPreview?.Invoke(this); + currentlyPlaying = panel; + } }; } }; @@ -75,8 +75,9 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps public void StopPlayingPreview() { - if (currentlyPlaying != null) - currentlyPlaying.PreviewPlaying.Value = false; + if (currentlyPlaying == null) return; + currentlyPlaying.PreviewPlaying.Value = false; + currentlyPlaying = null; } } } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 78a3bd7468..92abd20f93 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -25,13 +25,10 @@ namespace osu.Game.Overlays.Profile.Sections foreach (var paginatedBeatmapContainer in Children.OfType()) { - paginatedBeatmapContainer.BeganPlayingPreview += beatmapContainer => + paginatedBeatmapContainer.BeganPlayingPreview += _ => { foreach (var bc in Children.OfType()) - { - if (bc != beatmapContainer) - bc.StopPlayingPreview(); - } + bc.StopPlayingPreview(); }; } } From c095fe1919c3d8e5d7f2262f726896accdf84457 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Apr 2018 00:21:03 +0900 Subject: [PATCH 468/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index eb6362eaf1..f155804739 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit eb6362eaf1317b0fa27b2c9e559bd9a0f1ce357c +Subproject commit f155804739b8bf6e8e04cbdbadca88618d325a32 From 96ef564f442d4d94742cd00ac7cf33f5ba8e8552 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Apr 2018 00:25:05 +0900 Subject: [PATCH 469/537] Remove unused field --- osu.Game/Graphics/Cursor/MenuCursor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 575e2979cd..5f57fb76b0 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -29,8 +29,6 @@ namespace osu.Game.Graphics.Cursor private bool startRotation; - private ScreenshotManager screenshotManager; - [BackgroundDependencyLoader(true)] private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager) { From 2eefe722f3d213ac0507ef7269e5f6dbc66db610 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Apr 2018 00:38:32 +0900 Subject: [PATCH 470/537] Move cache to a slightly more familiar place and add comment --- osu.Game/OsuGame.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4265cc0140..941e49e87e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -131,7 +131,6 @@ namespace osu.Game } dependencies.CacheAs(this); - dependencies.Cache(screenshotManager = new ScreenshotManager()); // bind config int to database RulesetInfo configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); @@ -203,6 +202,9 @@ namespace osu.Game protected override void LoadComplete() { + // this needs to be cached before base.LoadComplete as it is used by CursorOverrideContainer. + dependencies.Cache(screenshotManager = new ScreenshotManager()); + base.LoadComplete(); // The next time this is updated is in UpdateAfterChildren, which occurs too late and results From c3086531e08ea6a60b2878841c34a6b7d14550cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Apr 2018 02:54:24 +0900 Subject: [PATCH 471/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index f155804739..7e8788e601 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit f155804739b8bf6e8e04cbdbadca88618d325a32 +Subproject commit 7e8788e601b62577e51197a29e24f56eeeac0286 From 3454ec1ca385d1725432d7322c98fc9dd1f6226f Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Sun, 15 Apr 2018 20:23:57 +0200 Subject: [PATCH 472/537] Set Value instead of BindTo --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 18dc9f4ea1..f69135e5c9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -196,7 +196,7 @@ namespace osu.Game } Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); - Beatmap.Value.Mods.BindTo(new Bindable>(s.Mods)); + Beatmap.Value.Mods.Value = s.Mods; menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); } From 6ca714d93b9e92d9f2869e31379a38fb6ab55e06 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 15 Apr 2018 23:44:59 +0200 Subject: [PATCH 473/537] add badges to ProfileHeader --- osu.Game.Tests/Visual/TestCaseRankGraph.cs | 2 +- osu.Game.Tests/Visual/TestCaseUserProfile.cs | 61 ++++-- .../Overlays/Profile/Header/BadgeContainer.cs | 190 ++++++++++++++++++ .../Profile/{ => Header}/RankGraph.cs | 8 +- .../Profile/{ => Header}/SupporterIcon.cs | 4 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 22 +- osu.Game/Users/Badge.cs | 20 ++ osu.Game/Users/User.cs | 3 + osu.Game/Users/UserPanel.cs | 2 +- 9 files changed, 281 insertions(+), 31 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Header/BadgeContainer.cs rename osu.Game/Overlays/Profile/{ => Header}/RankGraph.cs (99%) rename osu.Game/Overlays/Profile/{ => Header}/SupporterIcon.cs (98%) create mode 100644 osu.Game/Users/Badge.cs diff --git a/osu.Game.Tests/Visual/TestCaseRankGraph.cs b/osu.Game.Tests/Visual/TestCaseRankGraph.cs index 45f6651537..f5558620ad 100644 --- a/osu.Game.Tests/Visual/TestCaseRankGraph.cs +++ b/osu.Game.Tests/Visual/TestCaseRankGraph.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Overlays.Profile; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; @@ -11,6 +10,7 @@ using System.Collections.Generic; using System; using NUnit.Framework; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Profile.Header; using osu.Game.Users; namespace osu.Game.Tests.Visual diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index b060b9f2f8..86af26098a 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Profile; +using osu.Game.Overlays.Profile.Header; using osu.Game.Users; namespace osu.Game.Tests.Visual @@ -23,6 +24,7 @@ namespace osu.Game.Tests.Visual typeof(UserProfileOverlay), typeof(RankGraph), typeof(LineGraph), + typeof(BadgeContainer) }; public TestCaseUserProfile() @@ -34,27 +36,24 @@ namespace osu.Game.Tests.Visual { base.LoadComplete(); - AddStep("Show offline dummy", () => profile.ShowUser(new User + AddStep("Show offline dummy", () => profile.ShowUser(createDummyUser(new Badge[0]), false)); + + AddStep("Show with badge", () => profile.ShowUser(createDummyUser(new[] { - Username = @"Somebody", - Id = 1, - Country = new Country { FullName = @"Alien" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", - JoinDate = DateTimeOffset.Now.AddDays(-1), - LastVisit = DateTimeOffset.Now, - Age = 1, - ProfileOrder = new[] { "me" }, - Statistics = new UserStatistics + new Badge { - Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, - PP = 4567.89m, - }, - RankHistory = new User.RankHistoryData - { - Mode = @"osu", - Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() + AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569), + Description = "Outstanding help by being a voluntary test subject.", + ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg" } - }, false)); + }), false)); + + AddStep("Show many badges", () => profile.ShowUser(createDummyUser(Enumerable.Range(0, 10).Select(i => new Badge + { + AwardedAt = DateTimeOffset.Now, + Description = i.ToString(), + ImageUrl = "Flags/__" + }).ToArray()), false)); checkSupporterTag(false); @@ -95,6 +94,32 @@ namespace osu.Game.Tests.Visual AddAssert("no supporter", () => profile.Header.SupporterTag.Alpha == 0); } + private User createDummyUser(Badge[] badges) + { + return new User + { + Username = @"Somebody", + Id = 1, + Country = new Country { FullName = @"Alien" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", + JoinDate = DateTimeOffset.Now.AddDays(-1), + LastVisit = DateTimeOffset.Now, + Age = 1, + ProfileOrder = new[] { "me" }, + Statistics = new UserStatistics + { + Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, + PP = 4567.89m, + }, + RankHistory = new User.RankHistoryData + { + Mode = @"osu", + Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() + }, + Badges = badges + }; + } + private class TestUserProfileOverlay : UserProfileOverlay { public new ProfileHeader Header => base.Header; diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs new file mode 100644 index 0000000000..b4d0705e50 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -0,0 +1,190 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using OpenTK; + +namespace osu.Game.Overlays.Profile.Header +{ + public class BadgeContainer : Container + { + private const float outer_container_width = 98; + private const float outer_container_padding = 3; + private static readonly Vector2 badge_size = new Vector2(outer_container_width - outer_container_padding * 2, 46); + + private OsuSpriteText badgeCountText; + private FillFlowContainer badgeFlowContainer; + private FillFlowContainer outerBadgeContainer; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Child = new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Masking = true, + CornerRadius = 4, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray3 + }, + outerBadgeContainer = new OuterBadgeContainer(onOuterHover, onOuterHoverLost) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Direction = FillDirection.Vertical, + Padding = new MarginPadding(outer_container_padding), + Width = outer_container_width, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + badgeCountText = new OsuSpriteText + { + Alpha = 0, + TextSize = 12, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Font = "Exo2.0-Regular" + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Child = badgeFlowContainer = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + } + } + } + }, + } + }; + + Scheduler.AddDelayed(rotateBadges, 3000, true); + } + + private void rotateBadges() + { + if (outerBadgeContainer.IsHovered) return; + + visibleBadge = (visibleBadge + 1) % badgeCount; + + badgeFlowContainer.MoveToX(-badge_size.X * visibleBadge, 500, Easing.InOutQuad); + } + + private int visibleBadge; + private int badgeCount; + + public void ShowBadges(Badge[] badges) + { + switch (badges.Length) + { + case 0: + Hide(); + return; + case 1: + badgeCountText.Hide(); + break; + default: + badgeCountText.Show(); + badgeCountText.Text = $"{badges.Length} badges"; + break; + } + + Show(); + badgeCount = badges.Length; + visibleBadge = 0; + + foreach (var badge in badges) + { + LoadComponentAsync(new DrawableBadge(badge) + { + Size = badge_size, + }, badgeFlowContainer.Add); + } + } + + private void onOuterHover() + { + badgeFlowContainer.ClearTransforms(); + badgeFlowContainer.X = 0; + badgeFlowContainer.Direction = FillDirection.Full; + outerBadgeContainer.AutoSizeAxes = Axes.Both; + + badgeFlowContainer.MaximumSize = new Vector2(ChildSize.X, float.MaxValue); + } + + private void onOuterHoverLost() + { + rotateBadges(); + badgeFlowContainer.Direction = FillDirection.Horizontal; + outerBadgeContainer.AutoSizeAxes = Axes.Y; + outerBadgeContainer.Width = outer_container_width; + } + + private class OuterBadgeContainer : FillFlowContainer + { + private readonly Action hoverAction; + private readonly Action hoverLostAction; + + public OuterBadgeContainer(Action hoverAction, Action hoverLostAction) + { + this.hoverAction = hoverAction; + this.hoverLostAction = hoverLostAction; + } + + protected override bool OnHover(InputState state) + { + hoverAction(); + return true; + } + + protected override void OnHoverLost(InputState state) => hoverLostAction(); + } + + private class DrawableBadge : Container, IHasTooltip + { + private readonly Badge badge; + + public DrawableBadge(Badge badge) + { + this.badge = badge; + Padding = new MarginPadding(3); + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Child = new Sprite + { + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(badge.ImageUrl), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + OnLoadComplete = d => d.FadeInFromZero(200) + }; + } + + public string TooltipText => badge.Description; + } + } +} diff --git a/osu.Game/Overlays/Profile/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs similarity index 99% rename from osu.Game/Overlays/Profile/RankGraph.cs rename to osu.Game/Overlays/Profile/Header/RankGraph.cs index 72dd4352f6..2c70507536 100644 --- a/osu.Game/Overlays/Profile/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs @@ -2,9 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Linq; -using OpenTK; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -14,10 +15,9 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Users; -using System.Collections.Generic; -using osu.Framework.Configuration; +using OpenTK; -namespace osu.Game.Overlays.Profile +namespace osu.Game.Overlays.Profile.Header { public class RankGraph : Container { diff --git a/osu.Game/Overlays/Profile/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs similarity index 98% rename from osu.Game/Overlays/Profile/SupporterIcon.cs rename to osu.Game/Overlays/Profile/Header/SupporterIcon.cs index e8d52bf50e..37ad63464c 100644 --- a/osu.Game/Overlays/Profile/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -9,8 +8,9 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; +using OpenTK; -namespace osu.Game.Overlays.Profile +namespace osu.Game.Overlays.Profile.Header { public class SupporterIcon : CircularContainer, IHasTooltip { diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 4a88431cc4..189eaf0bdf 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -17,6 +17,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Profile.Header; using osu.Game.Users; namespace osu.Game.Overlays.Profile @@ -35,6 +36,7 @@ namespace osu.Game.Overlays.Profile private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA; private readonly Box colourBar; private readonly DrawableFlag countryFlag; + private readonly BadgeContainer badgeContainer; private const float cover_height = 350; private const float info_height = 150; @@ -42,6 +44,7 @@ namespace osu.Game.Overlays.Profile private const float avatar_size = 110; private const float level_position = 30; private const float level_height = 60; + private const float stats_width = 280; public ProfileHeader(User user) { @@ -66,9 +69,9 @@ namespace osu.Game.Overlays.Profile { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - X = UserProfileOverlay.CONTENT_X_MARGIN, - Y = -20, - AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN, Bottom = 20, Right = stats_width + UserProfileOverlay.CONTENT_X_MARGIN }, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, Children = new Drawable[] { new UpdateableAvatar @@ -116,7 +119,14 @@ namespace osu.Game.Overlays.Profile Height = 20 } } - } + }, + badgeContainer = new BadgeContainer + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Bottom = 5 }, + Alpha = 0, + }, } }, colourBar = new Box @@ -156,7 +166,7 @@ namespace osu.Game.Overlays.Profile { X = -UserProfileOverlay.CONTENT_X_MARGIN, RelativeSizeAxes = Axes.Y, - Width = 280, + Width = stats_width, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Children = new Drawable[] @@ -417,6 +427,8 @@ namespace osu.Game.Overlays.Profile rankGraph.User.Value = user; } + + badgeContainer.ShowBadges(user.Badges); } private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null) diff --git a/osu.Game/Users/Badge.cs b/osu.Game/Users/Badge.cs new file mode 100644 index 0000000000..25ef8ffdf4 --- /dev/null +++ b/osu.Game/Users/Badge.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using Newtonsoft.Json; + +namespace osu.Game.Users +{ + public class Badge + { + [JsonProperty("awarded_at")] + public DateTimeOffset AwardedAt; + + [JsonProperty("description")] + public string Description; + + [JsonProperty("image_url")] + public string ImageUrl; + } +} diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 441e4ffd28..b983b639f0 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -137,6 +137,9 @@ namespace osu.Game.Users [JsonProperty(@"rankHistory")] public RankHistoryData RankHistory; + [JsonProperty("badges")] + public Badge[] Badges; + public override string ToString() => Username; } } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 424d7b7a8f..bcb91c1955 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -17,7 +17,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.Containers; -using osu.Game.Overlays.Profile; +using osu.Game.Overlays.Profile.Header; namespace osu.Game.Users { From 1b9d54a6ad4bd555a965b769d7b7863c625d1509 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Apr 2018 17:39:03 +0900 Subject: [PATCH 474/537] Fix various data races causing crashes or incorrect leaderboard states --- .../Select/Leaderboards/Leaderboard.cs | 79 +++++++++++-------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 3a6ab8f84b..a6bbbce9b9 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -39,8 +39,9 @@ namespace osu.Game.Screens.Select.Leaderboards private readonly LoadingAnimation loading; - private IEnumerable scores; + private ScheduledDelegate showScoresDelegate; + private IEnumerable scores; public IEnumerable Scores { get { return scores; } @@ -59,29 +60,34 @@ namespace osu.Game.Screens.Select.Leaderboards // ensure placeholder is hidden when displaying scores PlaceholderState = PlaceholderState.Successful; - // schedule because we may not be loaded yet (LoadComponentAsync complains). - Schedule(() => + var flow = scrollFlow = new FillFlowContainer { - LoadComponentAsync(new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, 5f), - Padding = new MarginPadding { Top = 10, Bottom = 5 }, - ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) }) - }, f => - { - scrollContainer.Add(scrollFlow = f); + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, 5f), + Padding = new MarginPadding { Top = 10, Bottom = 5 }, + ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) }) + }; - int i = 0; - foreach (var s in f.Children) - { - using (s.BeginDelayedSequence(i++ * 50, true)) - s.Show(); - } + // schedule because we may not be loaded yet (LoadComponentAsync complains). + showScoresDelegate?.Cancel(); + if (!IsLoaded) + showScoresDelegate = Schedule(showScores); + else + showScores(); - scrollContainer.ScrollTo(0f, false); - }); + void showScores() => LoadComponentAsync(flow, _ => + { + scrollContainer.Add(flow); + + int i = 0; + foreach (var s in flow.Children) + { + using (s.BeginDelayedSequence(i++ * 50, true)) + s.Show(); + } + + scrollContainer.ScrollTo(0f, false); }); } } @@ -249,26 +255,33 @@ namespace osu.Game.Screens.Select.Leaderboards PlaceholderState = PlaceholderState.Retrieving; loading.Show(); + var localBeatmap = Beatmap; + getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope); - getScoresRequest.Success += r => + getScoresRequest.Success += r => Schedule(() => { + if (localBeatmap != Beatmap) + return; + Scores = r.Scores; PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; - }; - getScoresRequest.Failure += onUpdateFailed; + }); + + getScoresRequest.Failure += e => Schedule(() => + { + if (localBeatmap != Beatmap) + return; + + if (e is OperationCanceledException) + return; + + PlaceholderState = PlaceholderState.NetworkFailure; + Logger.Error(e, @"Couldn't fetch beatmap scores!"); + }); api.Queue(getScoresRequest); } - private void onUpdateFailed(Exception e) - { - if (e is OperationCanceledException) - return; - - PlaceholderState = PlaceholderState.NetworkFailure; - Logger.Error(e, @"Couldn't fetch beatmap scores!"); - } - private void replacePlaceholder(Placeholder placeholder) { var existingPlaceholder = placeholderContainer.Children.LastOrDefault() as Placeholder; From b9220a1e293c9eef775d6606e398d012c50180dc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Apr 2018 17:39:55 +0900 Subject: [PATCH 475/537] Fix leaderboard placeholder sometimes disappearing indefinitely --- .../Screens/Select/Leaderboards/Leaderboard.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index a6bbbce9b9..4d8d43f60a 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -282,24 +282,29 @@ namespace osu.Game.Screens.Select.Leaderboards api.Queue(getScoresRequest); } + private Placeholder currentPlaceholder; + private void replacePlaceholder(Placeholder placeholder) { - var existingPlaceholder = placeholderContainer.Children.LastOrDefault() as Placeholder; - - if (placeholder != null && placeholder.Equals(existingPlaceholder)) + if (placeholder != null && placeholder.Equals(currentPlaceholder)) return; - existingPlaceholder?.FadeOut(150, Easing.OutQuint).Expire(); + currentPlaceholder?.FadeOut(150, Easing.OutQuint).Expire(); if (placeholder == null) + { + currentPlaceholder = null; return; + } Scores = null; - placeholderContainer.Add(placeholder); + placeholderContainer.Child = placeholder; placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint); placeholder.FadeInFromZero(fade_duration, Easing.OutQuint); + + currentPlaceholder = placeholder; } protected override void Update() From 9af6ef18646d8700ab4be15a6748c8a6f9762c47 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Apr 2018 17:48:49 +0900 Subject: [PATCH 476/537] Remove extra unneded safety --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 4d8d43f60a..429e6a405d 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -255,23 +255,15 @@ namespace osu.Game.Screens.Select.Leaderboards PlaceholderState = PlaceholderState.Retrieving; loading.Show(); - var localBeatmap = Beatmap; - getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope); getScoresRequest.Success += r => Schedule(() => { - if (localBeatmap != Beatmap) - return; - Scores = r.Scores; PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; }); getScoresRequest.Failure += e => Schedule(() => { - if (localBeatmap != Beatmap) - return; - if (e is OperationCanceledException) return; From ea0683adb2369489c0684a11f2e6c1437538aa31 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Apr 2018 20:18:33 +0900 Subject: [PATCH 477/537] Fix hitobject lengths not being calculated for overlapping speed changes Fixes #2359 --- .../OverlappingSpeedChangeVisualiser.cs | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index 2365582645..c5e91c6929 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Lists; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; using OpenTK; @@ -22,8 +23,24 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { foreach (var obj in hitObjects) { - var controlPoint = controlPointAt(obj.HitObject.StartTime); - obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier; + obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPointAt(obj.HitObject.StartTime).Multiplier; + + if (obj.HitObject is IHasEndTime endTime) + { + var diff = -positionAt(endTime.EndTime, obj, timeRange); + + switch (direction) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + obj.Height = (float)(diff * length.Y); + break; + case ScrollingDirection.Left: + case ScrollingDirection.Right: + obj.Width = (float)(diff * length.X); + break; + } + } if (obj.HasNestedHitObjects) { @@ -37,9 +54,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { foreach (var obj in hitObjects) { - var controlPoint = controlPointAt(obj.HitObject.StartTime); - - var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange; + var position = positionAt(currentTime, obj, timeRange); switch (direction) { @@ -59,6 +74,9 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers } } + private double positionAt(double time, DrawableHitObject obj, double timeRange) + => (obj.HitObject.StartTime - time) * controlPointAt(obj.HitObject.StartTime).Multiplier / timeRange; + private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); private MultiplierControlPoint controlPointAt(double time) { From 9b36cf2066d499f9ca0c57cb7741cee6db6cfc7f Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Mon, 16 Apr 2018 14:14:40 +0200 Subject: [PATCH 478/537] Review changes --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 9 ++- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 8 +-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 13 ++-- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 9 ++- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 - osu.Game/Beatmaps/Legacy/LegacyMods.cs | 59 +++++++++---------- osu.Game/Rulesets/Ruleset.cs | 7 ++- .../Scoring/Legacy/LegacyScoreParser.cs | 2 +- 8 files changed, 54 insertions(+), 55 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 87de1aed97..cfe0fc5cec 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -30,21 +30,20 @@ namespace osu.Game.Rulesets.Catch new KeyBinding(InputKey.Shift, CatchAction.Dash), }; - public override IEnumerable GetLegacyModsFor(LegacyMods mods) + public override IEnumerable ConvertLegacyMods(LegacyMods mods) { - if (mods.HasFlag(LegacyMods.NightCore)) + if (mods.HasFlag(LegacyMods.Nightcore)) yield return new CatchModNightcore(); else if (mods.HasFlag(LegacyMods.DoubleTime)) yield return new CatchModDoubleTime(); - - if (mods.HasFlag(LegacyMods.AutoPlay)) + if (mods.HasFlag(LegacyMods.Autoplay)) yield return new CatchModAutoplay(); if (mods.HasFlag(LegacyMods.Easy)) yield return new CatchModEasy(); - if (mods.HasFlag(LegacyMods.FlashLight)) + if (mods.HasFlag(LegacyMods.Flashlight)) yield return new CatchModFlashlight(); if (mods.HasFlag(LegacyMods.HalfTime)) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 4bd243664b..0546cbc174 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -22,14 +22,14 @@ namespace osu.Game.Rulesets.Mania { public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset); - public override IEnumerable GetLegacyModsFor(LegacyMods mods) + public override IEnumerable ConvertLegacyMods(LegacyMods mods) { - if (mods.HasFlag(LegacyMods.NightCore)) + if (mods.HasFlag(LegacyMods.Nightcore)) yield return new ManiaModNightcore(); else if (mods.HasFlag(LegacyMods.DoubleTime)) yield return new ManiaModDoubleTime(); - if (mods.HasFlag(LegacyMods.AutoPlay)) + if (mods.HasFlag(LegacyMods.Autoplay)) yield return new ManiaModAutoplay(); if (mods.HasFlag(LegacyMods.Easy)) @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania if (mods.HasFlag(LegacyMods.FadeIn)) yield return new ManiaModFadeIn(); - if (mods.HasFlag(LegacyMods.FlashLight)) + if (mods.HasFlag(LegacyMods.Flashlight)) yield return new ManiaModFlashlight(); if (mods.HasFlag(LegacyMods.HalfTime)) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 7611f160ae..e0ecee97a3 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -67,24 +67,23 @@ namespace osu.Game.Rulesets.Osu }; } - public override IEnumerable GetLegacyModsFor(LegacyMods mods) + public override IEnumerable ConvertLegacyMods(LegacyMods mods) { - if (mods.HasFlag(LegacyMods.NightCore)) + if (mods.HasFlag(LegacyMods.Nightcore)) yield return new OsuModNightcore(); else if (mods.HasFlag(LegacyMods.DoubleTime)) yield return new OsuModDoubleTime(); - - if (mods.HasFlag(LegacyMods.AutoPilot)) + if (mods.HasFlag(LegacyMods.Autopilot)) yield return new OsuModAutopilot(); - if (mods.HasFlag(LegacyMods.AutoPlay)) + if (mods.HasFlag(LegacyMods.Autoplay)) yield return new OsuModAutoplay(); if (mods.HasFlag(LegacyMods.Easy)) yield return new OsuModEasy(); - if (mods.HasFlag(LegacyMods.FlashLight)) + if (mods.HasFlag(LegacyMods.Flashlight)) yield return new OsuModFlashlight(); if (mods.HasFlag(LegacyMods.HalfTime)) @@ -111,7 +110,7 @@ namespace osu.Game.Rulesets.Osu if (mods.HasFlag(LegacyMods.SuddenDeath)) yield return new OsuModSuddenDeath(); - if (mods.HasFlag(LegacyMods.TargetPractice)) + if (mods.HasFlag(LegacyMods.Target)) yield return new OsuModTarget(); } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 1881e84fb0..06a8e44a14 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -32,21 +32,20 @@ namespace osu.Game.Rulesets.Taiko new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim), }; - public override IEnumerable GetLegacyModsFor(LegacyMods mods) + public override IEnumerable ConvertLegacyMods(LegacyMods mods) { - if (mods.HasFlag(LegacyMods.NightCore)) + if (mods.HasFlag(LegacyMods.Nightcore)) yield return new TaikoModNightcore(); else if (mods.HasFlag(LegacyMods.DoubleTime)) yield return new TaikoModDoubleTime(); - - if (mods.HasFlag(LegacyMods.AutoPlay)) + if (mods.HasFlag(LegacyMods.Autoplay)) yield return new TaikoModAutoplay(); if (mods.HasFlag(LegacyMods.Easy)) yield return new TaikoModEasy(); - if (mods.HasFlag(LegacyMods.FlashLight)) + if (mods.HasFlag(LegacyMods.Flashlight)) yield return new TaikoModFlashlight(); if (mods.HasFlag(LegacyMods.HalfTime)) diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 4a33e5eb7e..48cee0d512 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -54,8 +54,6 @@ namespace osu.Game.Beatmaps { public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; - public override IEnumerable GetLegacyModsFor(LegacyMods mods) => new Mod[] { }; - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) { throw new NotImplementedException(); diff --git a/osu.Game/Beatmaps/Legacy/LegacyMods.cs b/osu.Game/Beatmaps/Legacy/LegacyMods.cs index 2ac7324778..0983610ba0 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyMods.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyMods.cs @@ -9,35 +9,34 @@ namespace osu.Game.Beatmaps.Legacy public enum LegacyMods { None = 0, - NoFail = 1, - Easy = 2, - TouchDevice = 4, - Hidden = 8, - HardRock = 16, - SuddenDeath = 32, - DoubleTime = 64, - Relax = 128, - HalfTime = 256, - NightCore = 512, - FlashLight = 1024, - AutoPlay = 2048, - SpunOut = 4096, - AutoPilot = 8192, - Perfect = 16384, - Key4 = 32768, - Key5 = 65536, - Key6 = 131072, - Key7 = 262144, - Key8 = 524288, - keyMod = 1015808,// k4+k5+k6+k7+k8 - FadeIn = 1048576, - Random = 2097152, - Cinema = 4194304, - TargetPractice = 8388608, - Key9 = 16777216, - Coop = 33554432, - Key1 = 67108864, - Key3 = 134217728, - Key2 = 268435456, + NoFail = 1 << 0, + Easy = 1 << 1, + TouchDevice = 1 << 2, + Hidden = 1 << 3, + HardRock = 1 << 4, + SuddenDeath = 1 << 5, + DoubleTime = 1 << 6, + Relax = 1 << 7, + HalfTime = 1 << 8, + Nightcore = 1 << 9, + Flashlight = 1 << 10, + Autoplay = 1 << 11, + SpunOut = 1 << 12, + Autopilot = 1 << 13, + Perfect = 1 << 14, + Key4 = 1 << 15, + Key5 = 1 << 16, + Key6 = 1 << 17, + Key7 = 1 << 18, + Key8 = 1 << 19, + FadeIn = 1 << 20, + Random = 1 << 21, + Cinema = 1 << 22, + Target = 1 << 23, + Key9 = 1 << 24, + KeyCoop = 1 << 25, + Key1 = 1 << 26, + Key3 = 1 << 27, + Key2 = 1 << 28, } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index f2cf813d38..cd1d030afe 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -34,7 +34,12 @@ namespace osu.Game.Rulesets public abstract IEnumerable GetModsFor(ModType type); - public abstract IEnumerable GetLegacyModsFor(LegacyMods mods); + /// + /// Converts mods from legacy enum values. Do not override if you're not a legacy ruleset. + /// + /// The legacy enum which will be converted + /// An enumerable of constructed s + public virtual IEnumerable ConvertLegacyMods(LegacyMods mods) => new Mod[] { }; public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay); diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index 62d1cb3a32..239f200e29 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy /* score.Perfect = */ sr.ReadBoolean(); /* score.EnabledMods = (Mods)*/ - score.Mods = currentRuleset.GetLegacyModsFor((LegacyMods)sr.ReadInt32()).ToArray(); + score.Mods = currentRuleset.ConvertLegacyMods((LegacyMods)sr.ReadInt32()).ToArray(); /* score.HpGraphString = */ sr.ReadString(); /* score.Date = */ From 1feba556ebee972cd812795bd2e0a6135de154e6 Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Mon, 16 Apr 2018 14:25:43 +0200 Subject: [PATCH 479/537] CI: unused directive --- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 48cee0d512..0424ff84f1 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Textures; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; -using osu.Game.Beatmaps.Legacy; namespace osu.Game.Beatmaps { From af0c49cca88d08c83cf02f88b8da8ae993935bfc Mon Sep 17 00:00:00 2001 From: jorolf Date: Mon, 16 Apr 2018 18:27:18 +0200 Subject: [PATCH 480/537] badge_size is the actual badge size now also fixes rotateBadges bug when hover is lost --- .../Overlays/Profile/Header/BadgeContainer.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index b4d0705e50..c22bba9a43 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -19,9 +19,8 @@ namespace osu.Game.Overlays.Profile.Header { public class BadgeContainer : Container { - private const float outer_container_width = 98; - private const float outer_container_padding = 3; - private static readonly Vector2 badge_size = new Vector2(outer_container_width - outer_container_padding * 2, 46); + private static readonly Vector2 badge_size = new Vector2(86, 40); + private static readonly MarginPadding outer_padding = new MarginPadding(3); private OsuSpriteText badgeCountText; private FillFlowContainer badgeFlowContainer; @@ -49,8 +48,8 @@ namespace osu.Game.Overlays.Profile.Header Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Direction = FillDirection.Vertical, - Padding = new MarginPadding(outer_container_padding), - Width = outer_container_width, + Padding = outer_padding, + Width = badge_size.X + outer_padding.TotalHorizontal * 2, AutoSizeAxes = Axes.Y, Children = new Drawable[] { @@ -117,7 +116,7 @@ namespace osu.Game.Overlays.Profile.Header { LoadComponentAsync(new DrawableBadge(badge) { - Size = badge_size, + Size = badge_size + outer_padding.Total, }, badgeFlowContainer.Add); } } @@ -134,10 +133,10 @@ namespace osu.Game.Overlays.Profile.Header private void onOuterHoverLost() { - rotateBadges(); + badgeFlowContainer.MoveToX(-badge_size.X * visibleBadge, 500, Easing.InOutQuad); badgeFlowContainer.Direction = FillDirection.Horizontal; outerBadgeContainer.AutoSizeAxes = Axes.Y; - outerBadgeContainer.Width = outer_container_width; + outerBadgeContainer.Width = badge_size.X + outer_padding.TotalHorizontal * 2; } private class OuterBadgeContainer : FillFlowContainer From 2a18b4c3f8b46da5417ec071d7d2da243ff85f0d Mon Sep 17 00:00:00 2001 From: jorolf Date: Mon, 16 Apr 2018 18:51:35 +0200 Subject: [PATCH 481/537] add a DRAWABLE_BADGE_SIZE const and center badges --- osu.Game.Tests/Visual/TestCaseUserProfile.cs | 2 +- .../Overlays/Profile/Header/BadgeContainer.cs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index 86af26098a..f691279093 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual { AwardedAt = DateTimeOffset.Now, Description = i.ToString(), - ImageUrl = "Flags/__" + ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg" }).ToArray()), false)); checkSupporterTag(false); diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index c22bba9a43..7d8bdc6009 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Profile.Header Origin = Anchor.BottomLeft, Direction = FillDirection.Vertical, Padding = outer_padding, - Width = badge_size.X + outer_padding.TotalHorizontal * 2, + Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal, AutoSizeAxes = Axes.Y, Children = new Drawable[] { @@ -68,6 +68,8 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.Both, Child = badgeFlowContainer = new FillFlowContainer { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, } @@ -86,7 +88,7 @@ namespace osu.Game.Overlays.Profile.Header visibleBadge = (visibleBadge + 1) % badgeCount; - badgeFlowContainer.MoveToX(-badge_size.X * visibleBadge, 500, Easing.InOutQuad); + badgeFlowContainer.MoveToX(-DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge, 500, Easing.InOutQuad); } private int visibleBadge; @@ -116,7 +118,8 @@ namespace osu.Game.Overlays.Profile.Header { LoadComponentAsync(new DrawableBadge(badge) { - Size = badge_size + outer_padding.Total, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, }, badgeFlowContainer.Add); } } @@ -136,7 +139,7 @@ namespace osu.Game.Overlays.Profile.Header badgeFlowContainer.MoveToX(-badge_size.X * visibleBadge, 500, Easing.InOutQuad); badgeFlowContainer.Direction = FillDirection.Horizontal; outerBadgeContainer.AutoSizeAxes = Axes.Y; - outerBadgeContainer.Width = badge_size.X + outer_padding.TotalHorizontal * 2; + outerBadgeContainer.Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal; } private class OuterBadgeContainer : FillFlowContainer @@ -161,12 +164,15 @@ namespace osu.Game.Overlays.Profile.Header private class DrawableBadge : Container, IHasTooltip { + public static readonly Vector2 DRAWABLE_BADGE_SIZE = badge_size + outer_padding.Total; + private readonly Badge badge; public DrawableBadge(Badge badge) { this.badge = badge; - Padding = new MarginPadding(3); + Padding = outer_padding; + Size = DRAWABLE_BADGE_SIZE; } [BackgroundDependencyLoader] From 046412f962ed7b85f5279097fa2d595f6702aa8f Mon Sep 17 00:00:00 2001 From: jorolf Date: Mon, 16 Apr 2018 18:52:09 +0200 Subject: [PATCH 482/537] make hover lost transition instant (similar to hover transition) --- osu.Game/Overlays/Profile/Header/BadgeContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index 7d8bdc6009..7bff7efa48 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -136,7 +136,7 @@ namespace osu.Game.Overlays.Profile.Header private void onOuterHoverLost() { - badgeFlowContainer.MoveToX(-badge_size.X * visibleBadge, 500, Easing.InOutQuad); + badgeFlowContainer.X = -DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge; badgeFlowContainer.Direction = FillDirection.Horizontal; outerBadgeContainer.AutoSizeAxes = Axes.Y; outerBadgeContainer.Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal; From de9f15f620c827af040805e99f2de450f1c7c0e6 Mon Sep 17 00:00:00 2001 From: jorolf Date: Mon, 16 Apr 2018 19:22:11 +0200 Subject: [PATCH 483/537] add new test case and fix rotation not working --- .../Visual/TestCaseBadgeContainer.cs | 62 +++++++++++++++++ osu.Game.Tests/Visual/TestCaseUserProfile.cs | 68 ++++++++----------- .../Overlays/Profile/Header/BadgeContainer.cs | 5 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 3 +- 4 files changed, 92 insertions(+), 46 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCaseBadgeContainer.cs diff --git a/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs b/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs new file mode 100644 index 0000000000..8177e2e272 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Overlays.Profile.Header; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseBadgeContainer : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(BadgeContainer) }; + + public TestCaseBadgeContainer() + { + BadgeContainer badgeContainer; + + Child = badgeContainer = new BadgeContainer + { + RelativeSizeAxes = Axes.Both + }; + + AddStep("Show 1 badge", () => badgeContainer.ShowBadges(new[] + { + new Badge + { + AwardedAt = DateTimeOffset.Now, + Description = "Appreciates compasses", + ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png", + } + })); + + AddStep("Show 2 badges", () => badgeContainer.ShowBadges(new[] + { + new Badge + { + AwardedAt = DateTimeOffset.Now, + Description = "Contributed to osu!lazer testing", + ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.png", + }, + new Badge + { + AwardedAt = DateTimeOffset.Now, + Description = "Appreciates compasses", + ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png", + } + })); + + AddStep("Show many badges", () => badgeContainer.ShowBadges(Enumerable.Range(1, 20).Select(i => new Badge + { + AwardedAt = DateTimeOffset.Now, + Description = $"Contributed to osu!lazer testing {i} times", + ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg", + }).ToArray())); + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index f691279093..0fdc01a974 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -36,24 +36,36 @@ namespace osu.Game.Tests.Visual { base.LoadComplete(); - AddStep("Show offline dummy", () => profile.ShowUser(createDummyUser(new Badge[0]), false)); - - AddStep("Show with badge", () => profile.ShowUser(createDummyUser(new[] + AddStep("Show offline dummy", () => profile.ShowUser(new User { - new Badge + Username = @"Somebody", + Id = 1, + Country = new Country { FullName = @"Alien" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", + JoinDate = DateTimeOffset.Now.AddDays(-1), + LastVisit = DateTimeOffset.Now, + Age = 1, + ProfileOrder = new[] { "me" }, + Statistics = new UserStatistics { - AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569), - Description = "Outstanding help by being a voluntary test subject.", - ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg" + Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, + PP = 4567.89m, + }, + RankHistory = new User.RankHistoryData + { + Mode = @"osu", + Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() + }, + Badges = new[] + { + new Badge + { + AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569), + Description = "Outstanding help by being a voluntary test subject.", + ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg" + } } - }), false)); - - AddStep("Show many badges", () => profile.ShowUser(createDummyUser(Enumerable.Range(0, 10).Select(i => new Badge - { - AwardedAt = DateTimeOffset.Now, - Description = i.ToString(), - ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg" - }).ToArray()), false)); + }, false)); checkSupporterTag(false); @@ -94,32 +106,6 @@ namespace osu.Game.Tests.Visual AddAssert("no supporter", () => profile.Header.SupporterTag.Alpha == 0); } - private User createDummyUser(Badge[] badges) - { - return new User - { - Username = @"Somebody", - Id = 1, - Country = new Country { FullName = @"Alien" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", - JoinDate = DateTimeOffset.Now.AddDays(-1), - LastVisit = DateTimeOffset.Now, - Age = 1, - ProfileOrder = new[] { "me" }, - Statistics = new UserStatistics - { - Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 }, - PP = 4567.89m, - }, - RankHistory = new User.RankHistoryData - { - Mode = @"osu", - Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() - }, - Badges = badges - }; - } - private class TestUserProfileOverlay : UserProfileOverlay { public new ProfileHeader Header => base.Header; diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index 7bff7efa48..a44940de1c 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -31,8 +31,6 @@ namespace osu.Game.Overlays.Profile.Header { Child = new Container { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, Masking = true, CornerRadius = 4, AutoSizeAxes = Axes.Both, @@ -68,8 +66,6 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.Both, Child = badgeFlowContainer = new FillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, } @@ -114,6 +110,7 @@ namespace osu.Game.Overlays.Profile.Header badgeCount = badges.Length; visibleBadge = 0; + badgeFlowContainer.Clear(); foreach (var badge in badges) { LoadComponentAsync(new DrawableBadge(badge) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 189eaf0bdf..ec0e45d5ca 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -122,7 +122,8 @@ namespace osu.Game.Overlays.Profile }, badgeContainer = new BadgeContainer { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Origin = Anchor.BottomLeft, Margin = new MarginPadding { Bottom = 5 }, Alpha = 0, From a827fb51bc05b340779f488a245ec3b8978eaaa1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Apr 2018 11:42:55 +0900 Subject: [PATCH 484/537] Simplify child definition --- osu.Game/Overlays/Profile/Header/BadgeContainer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index a44940de1c..291db45e97 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -180,12 +180,15 @@ namespace osu.Game.Overlays.Profile.Header FillMode = FillMode.Fit, RelativeSizeAxes = Axes.Both, Texture = textures.Get(badge.ImageUrl), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - OnLoadComplete = d => d.FadeInFromZero(200) }; } + protected override void LoadComplete() + { + base.LoadComplete(); + Child.FadeInFromZero(200); + } + public string TooltipText => badge.Description; } } From a27f39a55575411584f7bd02c444cfcaece7d6fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Apr 2018 11:57:06 +0900 Subject: [PATCH 485/537] Add documentation explaining intertwining logic --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 429e6a405d..43f6f13db3 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -109,6 +109,10 @@ namespace osu.Game.Screens.Select.Leaderboards private PlaceholderState placeholderState; + /// + /// Update the placeholder visibility. + /// Setting this to anything other than PlaceholderState.Successful will cancel all existing retrieval requests and hide scores. + /// protected PlaceholderState PlaceholderState { get { return placeholderState; } From 188c8ce1e7582f3789827a7004831b35e932bc34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Apr 2018 11:58:55 +0900 Subject: [PATCH 486/537] Remove unnecessary score nulling (already happens in PlaceholderState_Set) --- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 43f6f13db3..9dae8fb273 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -293,8 +293,6 @@ namespace osu.Game.Screens.Select.Leaderboards return; } - Scores = null; - placeholderContainer.Child = placeholder; placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint); From 68a13a5fb50093930d815ae9585c278ff1fccfbd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Apr 2018 14:18:56 +0900 Subject: [PATCH 487/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 02d7a0fa47..f858902d16 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 02d7a0fa4798d197cd08570ee48951edbb7c7860 +Subproject commit f858902d167c42d000662cb3a1cd202d723ea182 From d5ce618d9bf32e4098d6a0a2c947981be7d59982 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Apr 2018 15:12:48 +0900 Subject: [PATCH 488/537] Update with framework changes --- osu.Game/Tests/Visual/OsuTestCase.cs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index decf0c9bdb..02f425c296 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.IO; using System.Reflection; using osu.Framework.Testing; @@ -10,28 +9,27 @@ namespace osu.Game.Tests.Visual { public abstract class OsuTestCase : TestCase { - public override void RunTest() - { - using (var host = new CleanRunHeadlessGameHost($"test-{Guid.NewGuid()}", realtime: false)) - host.Run(new OsuTestCaseTestRunner(this)); - } + protected override ITestCaseTestRunner CreateRunner() => new OsuTestCaseTestRunner(); - public class OsuTestCaseTestRunner : OsuGameBase + public class OsuTestCaseTestRunner : OsuGameBase, ITestCaseTestRunner { - private readonly OsuTestCase testCase; - protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location; - public OsuTestCaseTestRunner(OsuTestCase testCase) + private readonly TestCaseTestRunner.TestRunner runner; + + public OsuTestCaseTestRunner() { - this.testCase = testCase; + runner = new TestCaseTestRunner.TestRunner(); } protected override void LoadComplete() { base.LoadComplete(); - Add(new TestCaseTestRunner.TestRunner(testCase)); + + Add(runner); } + + public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test); } } } From 560fcfc661e3a5917631bb472f1adda4276c636e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Apr 2018 15:15:29 +0900 Subject: [PATCH 489/537] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index f858902d16..16e6a453db 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit f858902d167c42d000662cb3a1cd202d723ea182 +Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a From 1be2571d33b9561267398042b02d1f30dd25b600 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Apr 2018 16:04:02 +0900 Subject: [PATCH 490/537] Make BeatmapSetOverlay accept nulls everywhere --- .../Visual/TestCaseBeatmapScoresContainer.cs | 4 +- .../Visual/TestCaseBeatmapSetOverlay.cs | 27 +++++ osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 56 ++++++---- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 27 ++++- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 33 ++++-- .../{ => Buttons}/DownloadButton.cs | 2 +- .../{ => Buttons}/FavouriteButton.cs | 2 +- .../BeatmapSet/{ => Buttons}/HeaderButton.cs | 4 +- .../BeatmapSet/{ => Buttons}/PreviewButton.cs | 8 +- osu.Game/Overlays/BeatmapSet/Details.cs | 20 +++- osu.Game/Overlays/BeatmapSet/Header.cs | 36 +++++-- osu.Game/Overlays/BeatmapSet/Info.cs | 11 +- .../BeatmapSet/Scores/ScoresContainer.cs | 102 +++++++++++------- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 26 +++-- osu.Game/Overlays/BeatmapSetOverlay.cs | 43 +++----- osu.Game/Overlays/Direct/PlayButton.cs | 16 +-- .../Sections/BeatmapMetadataContainer.cs | 2 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 2 +- .../Screens/Select/Details/AdvancedStats.cs | 8 +- .../Screens/Select/Details/FailRetryGraph.cs | 6 +- .../Screens/Select/Details/UserRatings.cs | 25 +++-- osu.Game/Users/UpdateableAvatar.cs | 22 ++-- 23 files changed, 322 insertions(+), 162 deletions(-) rename osu.Game/Overlays/BeatmapSet/{ => Buttons}/DownloadButton.cs (97%) rename osu.Game/Overlays/BeatmapSet/{ => Buttons}/FavouriteButton.cs (98%) rename osu.Game/Overlays/BeatmapSet/{ => Buttons}/HeaderButton.cs (94%) rename osu.Game/Overlays/BeatmapSet/{ => Buttons}/PreviewButton.cs (97%) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs index 6cb6a342a8..85f3364039 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs @@ -47,9 +47,7 @@ namespace osu.Game.Tests.Visual AddStep("scores pack 1", () => scoresContainer.Scores = scores); AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); AddStep("only top score", () => scoresContainer.Scores = new[] { topScore }); - AddStep("remove scores", scoresContainer.CleanAllScores); - AddStep("turn on loading", () => scoresContainer.IsLoading = true); - AddStep("turn off loading", () => scoresContainer.IsLoading = false); + AddStep("remove scores", () => scoresContainer.Scores = null); AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs index 69955a90c4..025562f75f 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs @@ -8,6 +8,9 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapSet; +using osu.Game.Overlays.BeatmapSet.Buttons; +using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets; using osu.Game.Users; @@ -18,6 +21,26 @@ namespace osu.Game.Tests.Visual { private readonly BeatmapSetOverlay overlay; + public override IReadOnlyList RequiredTypes => new[] + { + typeof(Header), + typeof(ClickableUsername), + typeof(DrawableScore), + typeof(DrawableTopScore), + typeof(ScoresContainer), + typeof(AuthorInfo), + typeof(BasicStats), + typeof(BeatmapPicker), + typeof(Details), + typeof(DownloadButton), + typeof(FavouriteButton), + typeof(Header), + typeof(HeaderButton), + typeof(Info), + typeof(PreviewButton), + typeof(SuccessRate), + }; + public TestCaseBeatmapSetOverlay() { Add(overlay = new BeatmapSetOverlay()); @@ -29,6 +52,10 @@ namespace osu.Game.Tests.Visual var mania = rulesets.GetRuleset(3); var taiko = rulesets.GetRuleset(1); + AddStep(@"show loading", () => overlay.ShowBeatmapSet(null)); + + AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55)); + AddStep(@"show first", () => { overlay.ShowBeatmapSet(new BeatmapSetInfo diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f69135e5c9..578d88bd89 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -158,7 +158,7 @@ namespace osu.Game /// Show a beatmap set as an overlay. /// /// The set to display. - public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId); + public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId); /// /// Show a user's profile as an overlay. diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index 8b19bca671..66e3148065 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -23,9 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet private readonly ClickableArea clickableArea; private readonly FillFlowContainer fields; - private UserProfileOverlay profile; - private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -34,28 +33,36 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - var i = BeatmapSet.OnlineInfo; + updateDisplay(); + } + } - avatar.User = BeatmapSet.Metadata.Author; - clickableArea.Action = () => profile?.ShowUser(avatar.User); + private void updateDisplay() + { + avatar.User = BeatmapSet?.Metadata.Author; - fields.Children = new Drawable[] - { - new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), - new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold") - { - Margin = new MarginPadding { Top = 5 }, - }, - }; + fields.Clear(); + if (BeatmapSet == null) + return; - if (i.Ranked.HasValue) + var online = BeatmapSet.OnlineInfo; + + fields.Children = new Drawable[] + { + new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), + new Field("submitted on", online.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold") { - fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); - } - else if (i.LastUpdated.HasValue) - { - fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); - } + Margin = new MarginPadding { Top = 5 }, + }, + }; + + if (online.Ranked.HasValue) + { + fields.Add(new Field("ranked on ", online.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); + } + else if (online.LastUpdated.HasValue) + { + fields.Add(new Field("last updated on ", online.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); } } @@ -73,6 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet Masking = true, Child = avatar = new UpdateableAvatar { + ShowGuestOnNull = false, Size = new Vector2(height), }, EdgeEffect = new EdgeEffectParameters @@ -95,8 +103,12 @@ namespace osu.Game.Overlays.BeatmapSet [BackgroundDependencyLoader(true)] private void load(UserProfileOverlay profile) { - this.profile = profile; - clickableArea.Action = () => profile?.ShowUser(avatar.User); + clickableArea.Action = () => + { + if (avatar.User != null) profile?.ShowUser(avatar.User); + }; + + updateDisplay(); } private class Field : FillFlowContainer diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 8fd34914a7..a7b6b16dcc 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -18,6 +18,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Statistic length, bpm, circleCount, sliderCount; private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -26,11 +27,12 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##"); + updateDisplay(); } } private BeatmapInfo beatmap; + public BeatmapInfo Beatmap { get { return beatmap; } @@ -39,6 +41,22 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmap) return; beatmap = value; + updateDisplay(); + } + } + + private void updateDisplay() + { + bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-"; + + if (beatmap == null) + { + length.Value = string.Empty; + circleCount.Value = string.Empty; + sliderCount.Value = string.Empty; + } + else + { length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss"); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); @@ -62,12 +80,19 @@ namespace osu.Game.Overlays.BeatmapSet }; } + [BackgroundDependencyLoader] + private void load() + { + updateDisplay(); + } + private class Statistic : Container, IHasTooltip { private readonly string name; private readonly OsuSpriteText value; public string TooltipText => name; + public string Value { get { return value.Text; } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 87dc9b104b..6b75ac095d 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -41,9 +41,16 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - Beatmap.Value = BeatmapSet.Beatmaps.First(); - plays.Value = BeatmapSet.OnlineInfo.PlayCount; - favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; + updateDisplay(); + } + } + + private void updateDisplay() + { + difficulties.Clear(); + + if (BeatmapSet != null) + { difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b) { State = DifficultySelectorState.NotSelected, @@ -53,14 +60,16 @@ namespace osu.Game.Overlays.BeatmapSet starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##"); starRating.FadeIn(100); }, - OnClicked = beatmap => - { - Beatmap.Value = beatmap; - }, + OnClicked = beatmap => { Beatmap.Value = beatmap; }, }); - - updateDifficultyButtons(); } + + starRating.FadeOut(100); + Beatmap.Value = BeatmapSet?.Beatmaps.FirstOrDefault(); + plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0; + favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0; + + updateDifficultyButtons(); } public BeatmapPicker() @@ -140,6 +149,7 @@ namespace osu.Game.Overlays.BeatmapSet private void load(OsuColour colours) { starRating.Colour = colours.Yellow; + updateDisplay(); } protected override void LoadComplete() @@ -150,7 +160,10 @@ namespace osu.Game.Overlays.BeatmapSet Beatmap.TriggerChange(); } - private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version; + private void showBeatmap(BeatmapInfo beatmap) + { + version.Text = beatmap?.Version; + } private void updateDifficultyButtons() { diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs similarity index 97% rename from osu.Game/Overlays/BeatmapSet/DownloadButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index 0c6414c718..c699ae2328 100644 --- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using OpenTK; -namespace osu.Game.Overlays.BeatmapSet +namespace osu.Game.Overlays.BeatmapSet.Buttons { public class DownloadButton : HeaderButton { diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs similarity index 98% rename from osu.Game/Overlays/BeatmapSet/FavouriteButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 29bc00038c..3821c96369 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using OpenTK; -namespace osu.Game.Overlays.BeatmapSet +namespace osu.Game.Overlays.BeatmapSet.Buttons { public class FavouriteButton : HeaderButton { diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs similarity index 94% rename from osu.Game/Overlays/BeatmapSet/HeaderButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs index e1c4f5cc67..b46b5d2a0e 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs @@ -1,12 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Framework.Allocation; -namespace osu.Game.Overlays.BeatmapSet +namespace osu.Game.Overlays.BeatmapSet.Buttons { public class HeaderButton : TriangleButton { diff --git a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs similarity index 97% rename from osu.Game/Overlays/BeatmapSet/PreviewButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 6b5ffa57ad..08a99f1aea 100644 --- a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,12 +12,11 @@ using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Overlays.Direct; using OpenTK; using OpenTK.Graphics; -using osu.Game.Overlays.Direct; -using osu.Framework.Configuration; -namespace osu.Game.Overlays.BeatmapSet +namespace osu.Game.Overlays.BeatmapSet.Buttons { public class PreviewButton : OsuClickableContainer { @@ -85,6 +85,8 @@ namespace osu.Game.Overlays.BeatmapSet // prevent negative (potential infinite) width if a track without length was loaded progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; } + else + progress.Width = 0; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 7f3b6d1584..5264caf936 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -1,11 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; +using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Screens.Select.Details; using OpenTK; using OpenTK.Graphics; @@ -20,6 +22,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly UserRatings ratings; private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -33,19 +36,24 @@ namespace osu.Game.Overlays.BeatmapSet } private BeatmapInfo beatmap; + public BeatmapInfo Beatmap { get { return beatmap; } set { if (value == beatmap) return; - beatmap = value; - basic.Beatmap = advanced.Beatmap = Beatmap; - ratings.Metrics = Beatmap.Metrics; + basic.Beatmap = advanced.Beatmap = beatmap = value; + updateDisplay(); } } + private void updateDisplay() + { + ratings.Metrics = Beatmap?.Metrics; + } + public Details() { Width = BeatmapSetOverlay.RIGHT_WIDTH; @@ -88,6 +96,12 @@ namespace osu.Game.Overlays.BeatmapSet }; } + [BackgroundDependencyLoader] + private void load() + { + updateDisplay(); + } + public void StopPreview() => preview.Playing.Value = false; private class DetailBox : Container diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 755039e7bc..9b25d61f58 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.BeatmapSet.Buttons; using OpenTK; using OpenTK.Graphics; @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet { public class Header : Container { - private const float transition_duration = 250; + private const float transition_duration = 200; private const float tabs_height = 50; private const float buttons_height = 45; private const float buttons_spacing = 5; @@ -34,12 +35,13 @@ namespace osu.Game.Overlays.BeatmapSet public Details Details; private BeatmapManager beatmaps; - private DelayedLoadWrapper cover; public readonly BeatmapPicker Picker; private BeatmapSetInfo beatmapSet; + private readonly FavouriteButton favouriteButton; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -49,15 +51,26 @@ namespace osu.Game.Overlays.BeatmapSet beatmapSet = value; Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; - title.Text = BeatmapSet.Metadata.Title; - artist.Text = BeatmapSet.Metadata.Artist; - onlineStatusPill.Status = BeatmapSet.OnlineInfo.Status; - downloadButtonsContainer.FadeIn(); + updateDisplay(); + } + } + + private void updateDisplay() + { + title.Text = BeatmapSet?.Metadata.Title ?? string.Empty; + artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty; + onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None; + + cover?.FadeOut(400, Easing.Out); + if (BeatmapSet != null) + { + downloadButtonsContainer.FadeIn(transition_duration); + favouriteButton.FadeIn(transition_duration); + noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration); videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration); - cover?.FadeOut(400, Easing.Out); coverContainer.Add(cover = new DelayedLoadWrapper( new BeatmapSetCover(BeatmapSet) { @@ -71,6 +84,11 @@ namespace osu.Game.Overlays.BeatmapSet RelativeSizeAxes = Axes.Both, }); } + else + { + downloadButtonsContainer.FadeOut(transition_duration); + favouriteButton.FadeOut(transition_duration); + } } public Header() @@ -166,7 +184,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Top = 10 }, Children = new Drawable[] { - new FavouriteButton(), + favouriteButton = new FavouriteButton(), downloadButtonsContainer = new Container { RelativeSizeAxes = Axes.Both, @@ -238,6 +256,8 @@ namespace osu.Game.Overlays.BeatmapSet this.beatmaps = beatmaps; beatmaps.ItemAdded += handleBeatmapAdd; + + updateDisplay(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 9a402515ae..cd0b7386e8 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -34,11 +34,16 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - source.Text = BeatmapSet.Metadata.Source; - tags.Text = BeatmapSet.Metadata.Tags; + updateDisplay(); } } + private void updateDisplay() + { + source.Text = BeatmapSet?.Metadata.Source ?? string.Empty; + tags.Text = BeatmapSet?.Metadata.Tags ?? string.Empty; + } + public BeatmapInfo Beatmap { get { return successRate.Beatmap; } @@ -132,6 +137,8 @@ namespace osu.Game.Overlays.BeatmapSet successRateBackground.Colour = colours.GrayE; source.TextColour = description.TextColour = colours.Gray5; tags.TextColour = colours.BlueDark; + + updateDisplay(); } private class MetadataSection : FillFlowContainer diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index d5c5bd8ddd..185282bec9 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -2,15 +2,15 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Online.API; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -22,49 +22,75 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly FillFlowContainer flow; private readonly DrawableTopScore topScore; private readonly LoadingAnimation loadingAnimation; - private readonly Box foreground; - private bool isLoading; - public bool IsLoading + private bool loading { - get { return isLoading; } - set - { - if (isLoading == value) return; - isLoading = value; - - foreground.FadeTo(isLoading ? 1 : 0, fade_duration); - loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration); - } + set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); } private IEnumerable scores; + private BeatmapInfo beatmap; + public IEnumerable Scores { get { return scores; } set { + getScoresRequest?.Cancel(); scores = value; - var scoresAmount = scores.Count(); - if (scoresAmount == 0) - { - CleanAllScores(); - return; - } - topScore.Score = scores.FirstOrDefault(); - topScore.Show(); - - flow.Clear(); - - if (scoresAmount < 2) - return; - - for (int i = 1; i < scoresAmount; i++) - flow.Add(new DrawableScore(i, scores.ElementAt(i))); + updateDisplay(); } } + private GetScoresRequest getScoresRequest; + private APIAccess api; + + public BeatmapInfo Beatmap + { + get => beatmap; + set + { + beatmap = value; + + Scores = null; + + if (beatmap?.OnlineBeatmapID.HasValue != true) + return; + + loading = true; + + getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); + getScoresRequest.Success += r => Scores = r.Scores; + api.Queue(getScoresRequest); + } + } + + private void updateDisplay() + { + loading = false; + + var scoreCount = scores?.Count() ?? 0; + + if (scoreCount == 0) + { + topScore.Hide(); + flow.Clear(); + return; + } + + topScore.Score = scores.FirstOrDefault(); + topScore.Show(); + + flow.Clear(); + + if (scoreCount < 2) + return; + + for (int i = 1; i < scoreCount; i++) + flow.Add(new DrawableScore(i, scores.ElementAt(i))); + } + public ScoresContainer() { RelativeSizeAxes = Axes.X; @@ -93,23 +119,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }, } }, - foreground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.7f), - Alpha = 0, - }, loadingAnimation = new LoadingAnimation { Alpha = 0, + Margin = new MarginPadding(20) }, }; } - public void CleanAllScores() + [BackgroundDependencyLoader] + private void load(APIAccess api) { - topScore.Hide(); - flow.Clear(); + this.api = api; + updateDisplay(); } } } diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index c64d3988a6..6572e520bd 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -29,18 +29,23 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmap) return; beatmap = value; - int passCount = beatmap.OnlineInfo.PassCount; - int playCount = beatmap.OnlineInfo.PlayCount; - - var rate = playCount != 0 ? (float)passCount / playCount : 0; - successPercent.Text = rate.ToString("P0"); - successRate.Length = rate; - percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); - - graph.Metrics = Beatmap.Metrics; + updateDisplay(); } } + private void updateDisplay() + { + int passCount = beatmap?.OnlineInfo.PassCount ?? 0; + int playCount = beatmap?.OnlineInfo.PlayCount ?? 0; + + var rate = playCount != 0 ? (float)passCount / playCount : 0; + successPercent.Text = rate.ToString("P0"); + successRate.Length = rate; + percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); + + graph.Metrics = beatmap?.Metrics; + } + public SuccessRate() { Children = new Drawable[] @@ -74,7 +79,6 @@ namespace osu.Game.Overlays.BeatmapSet { Anchor = Anchor.TopRight, Origin = Anchor.TopCentre, - Text = @"0%", TextSize = 13, }, }, @@ -103,6 +107,8 @@ namespace osu.Game.Overlays.BeatmapSet successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5; successRate.AccentColour = colours.Green; successRate.BackgroundColour = colours.GrayD; + + updateDisplay(); } protected override void UpdateAfterChildren() diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index d2cf0a6ef1..74550e3a9b 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -27,19 +27,31 @@ namespace osu.Game.Overlays private readonly Header header; private readonly Info info; - private readonly ScoresContainer scores; - private APIAccess api; private RulesetStore rulesets; - private GetScoresRequest getScoresRequest; private readonly ScrollContainer scroll; + private BeatmapSetInfo beatmapSet; + + public BeatmapSetInfo BeatmapSet + { + get => beatmapSet; + set + { + if (value == beatmapSet) + return; + + header.BeatmapSet = info.BeatmapSet = beatmapSet = value; + } + } + // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; public BeatmapSetOverlay() { + ScoresContainer scores; Waves.FirstWaveColour = OsuColour.Gray(0.4f); Waves.SecondWaveColour = OsuColour.Gray(0.3f); Waves.ThirdWaveColour = OsuColour.Gray(0.2f); @@ -88,31 +100,10 @@ namespace osu.Game.Overlays header.Picker.Beatmap.ValueChanged += b => { info.Beatmap = b; - updateScores(b); + scores.Beatmap = b; }; } - private void updateScores(BeatmapInfo beatmap) - { - getScoresRequest?.Cancel(); - - if (!beatmap.OnlineBeatmapID.HasValue) - { - scores.CleanAllScores(); - return; - } - - scores.IsLoading = true; - - getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += r => - { - scores.Scores = r.Scores; - scores.IsLoading = false; - }; - api.Queue(getScoresRequest); - } - [BackgroundDependencyLoader] private void load(APIAccess api, RulesetStore rulesets) { @@ -139,7 +130,7 @@ namespace osu.Game.Overlays return true; } - public void ShowBeatmapSet(int beatmapSetId) + public void FetchAndShowBeatmapSet(int beatmapSetId) { // todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. var req = new GetBeatmapSetRequest(beatmapSetId); diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 44e24d8157..4913b11ae1 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -78,12 +78,7 @@ namespace osu.Game.Overlays.Direct loadingAnimation = new LoadingAnimation(), }); - Playing.ValueChanged += playing => - { - icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play; - icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); - updatePreviewTrack(playing); - }; + Playing.ValueChanged += updatePreviewTrack; } [BackgroundDependencyLoader] @@ -125,6 +120,15 @@ namespace osu.Game.Overlays.Direct private void updatePreviewTrack(bool playing) { + if (playing && BeatmapSet == null) + { + Playing.Value = false; + return; + } + + icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play; + icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); + if (playing) { if (Preview == null) diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index da08c08179..97079c77f3 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Sections { Action = () => { - if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); + if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); }; Child = new FillFlowContainer diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index ad7588edde..d554a22735 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Select.Carousel restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); dialogOverlay = overlay; if (beatmapOverlay != null) - viewDetails = beatmapOverlay.ShowBeatmapSet; + viewDetails = beatmapOverlay.FetchAndShowBeatmapSet; Children = new Drawable[] { diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index cad7ed7d81..852e4f190f 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -40,10 +40,10 @@ namespace osu.Game.Screens.Select.Details firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0; } - hpDrain.Value = beatmap.BaseDifficulty?.DrainRate ?? 0; - accuracy.Value = beatmap.BaseDifficulty?.OverallDifficulty ?? 0; - approachRate.Value = beatmap.BaseDifficulty?.ApproachRate ?? 0; - starDifficulty.Value = (float)beatmap.StarDifficulty; + hpDrain.Value = Beatmap?.BaseDifficulty?.DrainRate ?? 0; + accuracy.Value = Beatmap?.BaseDifficulty?.OverallDifficulty ?? 0; + approachRate.Value = Beatmap?.BaseDifficulty?.ApproachRate ?? 0; + starDifficulty.Value = (float)(Beatmap?.StarDifficulty ?? 0); } } diff --git a/osu.Game/Screens/Select/Details/FailRetryGraph.cs b/osu.Game/Screens/Select/Details/FailRetryGraph.cs index a33cee21ed..bf4eb07108 100644 --- a/osu.Game/Screens/Select/Details/FailRetryGraph.cs +++ b/osu.Game/Screens/Select/Details/FailRetryGraph.cs @@ -25,10 +25,10 @@ namespace osu.Game.Screens.Select.Details if (value == metrics) return; metrics = value; - var retries = Metrics.Retries; - var fails = Metrics.Fails; + var retries = Metrics?.Retries ?? new int[0]; + var fails = Metrics?.Fails ?? new int[0]; - float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max(); + float maxValue = fails.Any() ? fails.Zip(retries, (fail, retry) => fail + retry).Max() : 0; failGraph.MaxValue = maxValue; retryGraph.MaxValue = maxValue; diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index bf50217048..787b22f965 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -21,6 +21,7 @@ namespace osu.Game.Screens.Select.Details private readonly BarGraph graph; private BeatmapMetrics metrics; + public BeatmapMetrics Metrics { get { return metrics; } @@ -31,15 +32,25 @@ namespace osu.Game.Screens.Select.Details const int rating_range = 10; - var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. + if (metrics == null) + { + negativeRatings.Text = "0"; + positiveRatings.Text = "0"; + ratingsBar.Length = 0; + graph.Values = new float[rating_range]; + } + else + { + var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. - var negativeCount = ratings.Take(rating_range / 2).Sum(); - var totalCount = ratings.Sum(); + var negativeCount = ratings.Take(rating_range / 2).Sum(); + var totalCount = ratings.Sum(); - negativeRatings.Text = negativeCount.ToString(); - positiveRatings.Text = (totalCount - negativeCount).ToString(); - ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; - graph.Values = ratings.Take(rating_range).Select(r => (float)r); + negativeRatings.Text = negativeCount.ToString(); + positiveRatings.Text = (totalCount - negativeCount).ToString(); + ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; + graph.Values = ratings.Take(rating_range).Select(r => (float)r); + } } } diff --git a/osu.Game/Users/UpdateableAvatar.cs b/osu.Game/Users/UpdateableAvatar.cs index 31455801da..6c0b841abf 100644 --- a/osu.Game/Users/UpdateableAvatar.cs +++ b/osu.Game/Users/UpdateableAvatar.cs @@ -15,6 +15,11 @@ namespace osu.Game.Users private User user; + /// + /// Whether to show a default guest representation on null user (as opposed to nothing). + /// + public bool ShowGuestOnNull = true; + public User User { get { return user; } @@ -40,13 +45,16 @@ namespace osu.Game.Users { displayedAvatar?.FadeOut(300); displayedAvatar?.Expire(); - Add(displayedAvatar = new DelayedLoadWrapper( - new Avatar(user) - { - RelativeSizeAxes = Axes.Both, - OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), - }) - ); + if (user != null || ShowGuestOnNull) + { + Add(displayedAvatar = new DelayedLoadWrapper( + new Avatar(user) + { + RelativeSizeAxes = Axes.Both, + OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), + }) + ); + } } } } From 1728dd65021b3627b131f16b79d9895886b312ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Apr 2018 15:58:45 +0900 Subject: [PATCH 491/537] Make BeatmapSetOverlay accept nulls everywhere --- .../Visual/TestCaseBeatmapScoresContainer.cs | 4 +- .../Visual/TestCaseBeatmapSetOverlay.cs | 27 +++++ osu.Game/OsuGame.cs | 4 +- osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 56 ++++++---- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 27 ++++- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 33 ++++-- .../{ => Buttons}/DownloadButton.cs | 2 +- .../{ => Buttons}/FavouriteButton.cs | 2 +- .../BeatmapSet/{ => Buttons}/HeaderButton.cs | 4 +- .../BeatmapSet/{ => Buttons}/PreviewButton.cs | 8 +- osu.Game/Overlays/BeatmapSet/Details.cs | 20 +++- osu.Game/Overlays/BeatmapSet/Header.cs | 39 +++++-- osu.Game/Overlays/BeatmapSet/Info.cs | 14 ++- .../BeatmapSet/Scores/ScoresContainer.cs | 102 +++++++++++------- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 26 +++-- osu.Game/Overlays/BeatmapSetOverlay.cs | 52 +-------- osu.Game/Overlays/Direct/PlayButton.cs | 16 +-- .../Sections/BeatmapMetadataContainer.cs | 2 +- .../Carousel/DrawableCarouselBeatmap.cs | 2 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 2 +- .../Screens/Select/Details/AdvancedStats.cs | 8 +- .../Screens/Select/Details/FailRetryGraph.cs | 6 +- .../Screens/Select/Details/UserRatings.cs | 25 +++-- osu.Game/Users/UpdateableAvatar.cs | 22 ++-- 24 files changed, 312 insertions(+), 191 deletions(-) rename osu.Game/Overlays/BeatmapSet/{ => Buttons}/DownloadButton.cs (97%) rename osu.Game/Overlays/BeatmapSet/{ => Buttons}/FavouriteButton.cs (98%) rename osu.Game/Overlays/BeatmapSet/{ => Buttons}/HeaderButton.cs (94%) rename osu.Game/Overlays/BeatmapSet/{ => Buttons}/PreviewButton.cs (97%) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs index 6cb6a342a8..85f3364039 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs @@ -47,9 +47,7 @@ namespace osu.Game.Tests.Visual AddStep("scores pack 1", () => scoresContainer.Scores = scores); AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); AddStep("only top score", () => scoresContainer.Scores = new[] { topScore }); - AddStep("remove scores", scoresContainer.CleanAllScores); - AddStep("turn on loading", () => scoresContainer.IsLoading = true); - AddStep("turn off loading", () => scoresContainer.IsLoading = false); + AddStep("remove scores", () => scoresContainer.Scores = null); AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs index c2b61e4d74..4154dbde47 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs @@ -8,6 +8,9 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapSet; +using osu.Game.Overlays.BeatmapSet.Buttons; +using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets; using osu.Game.Users; @@ -18,6 +21,26 @@ namespace osu.Game.Tests.Visual { private readonly BeatmapSetOverlay overlay; + public override IReadOnlyList RequiredTypes => new[] + { + typeof(Header), + typeof(ClickableUsername), + typeof(DrawableScore), + typeof(DrawableTopScore), + typeof(ScoresContainer), + typeof(AuthorInfo), + typeof(BasicStats), + typeof(BeatmapPicker), + typeof(Details), + typeof(DownloadButton), + typeof(FavouriteButton), + typeof(Header), + typeof(HeaderButton), + typeof(Info), + typeof(PreviewButton), + typeof(SuccessRate), + }; + public TestCaseBeatmapSetOverlay() { Add(overlay = new BeatmapSetOverlay()); @@ -29,6 +52,10 @@ namespace osu.Game.Tests.Visual var mania = rulesets.GetRuleset(3); var taiko = rulesets.GetRuleset(1); + AddStep(@"show loading", () => overlay.ShowBeatmapSet(null)); + + AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55)); + AddStep(@"show first", () => { overlay.ShowBeatmapSet(new BeatmapSetInfo diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d740302a12..3852580c49 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -158,7 +158,7 @@ namespace osu.Game /// Show a beatmap set as an overlay. /// /// The set to display. - public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId); + public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId); /// /// Show a user's profile as an overlay. @@ -170,7 +170,7 @@ namespace osu.Game /// Show a beatmap's set as an overlay, displaying the given beatmap. /// /// The beatmap to show. - public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.ShowBeatmap(beatmapId); + public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId); protected void LoadScore(Score s) { diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index 8b19bca671..66e3148065 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -23,9 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet private readonly ClickableArea clickableArea; private readonly FillFlowContainer fields; - private UserProfileOverlay profile; - private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -34,28 +33,36 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - var i = BeatmapSet.OnlineInfo; + updateDisplay(); + } + } - avatar.User = BeatmapSet.Metadata.Author; - clickableArea.Action = () => profile?.ShowUser(avatar.User); + private void updateDisplay() + { + avatar.User = BeatmapSet?.Metadata.Author; - fields.Children = new Drawable[] - { - new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), - new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold") - { - Margin = new MarginPadding { Top = 5 }, - }, - }; + fields.Clear(); + if (BeatmapSet == null) + return; - if (i.Ranked.HasValue) + var online = BeatmapSet.OnlineInfo; + + fields.Children = new Drawable[] + { + new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), + new Field("submitted on", online.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold") { - fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); - } - else if (i.LastUpdated.HasValue) - { - fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); - } + Margin = new MarginPadding { Top = 5 }, + }, + }; + + if (online.Ranked.HasValue) + { + fields.Add(new Field("ranked on ", online.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); + } + else if (online.LastUpdated.HasValue) + { + fields.Add(new Field("last updated on ", online.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); } } @@ -73,6 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet Masking = true, Child = avatar = new UpdateableAvatar { + ShowGuestOnNull = false, Size = new Vector2(height), }, EdgeEffect = new EdgeEffectParameters @@ -95,8 +103,12 @@ namespace osu.Game.Overlays.BeatmapSet [BackgroundDependencyLoader(true)] private void load(UserProfileOverlay profile) { - this.profile = profile; - clickableArea.Action = () => profile?.ShowUser(avatar.User); + clickableArea.Action = () => + { + if (avatar.User != null) profile?.ShowUser(avatar.User); + }; + + updateDisplay(); } private class Field : FillFlowContainer diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 8fd34914a7..a7b6b16dcc 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -18,6 +18,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Statistic length, bpm, circleCount, sliderCount; private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -26,11 +27,12 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##"); + updateDisplay(); } } private BeatmapInfo beatmap; + public BeatmapInfo Beatmap { get { return beatmap; } @@ -39,6 +41,22 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmap) return; beatmap = value; + updateDisplay(); + } + } + + private void updateDisplay() + { + bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-"; + + if (beatmap == null) + { + length.Value = string.Empty; + circleCount.Value = string.Empty; + sliderCount.Value = string.Empty; + } + else + { length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss"); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); @@ -62,12 +80,19 @@ namespace osu.Game.Overlays.BeatmapSet }; } + [BackgroundDependencyLoader] + private void load() + { + updateDisplay(); + } + private class Statistic : Container, IHasTooltip { private readonly string name; private readonly OsuSpriteText value; public string TooltipText => name; + public string Value { get { return value.Text; } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index d8ec804646..6b75ac095d 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -41,9 +41,16 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - Beatmap.Value = BeatmapSet.Beatmaps.First(); - plays.Value = BeatmapSet.OnlineInfo.PlayCount; - favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; + updateDisplay(); + } + } + + private void updateDisplay() + { + difficulties.Clear(); + + if (BeatmapSet != null) + { difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b) { State = DifficultySelectorState.NotSelected, @@ -53,14 +60,16 @@ namespace osu.Game.Overlays.BeatmapSet starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##"); starRating.FadeIn(100); }, - OnClicked = beatmap => - { - Beatmap.Value = beatmap; - }, + OnClicked = beatmap => { Beatmap.Value = beatmap; }, }); - - updateDifficultyButtons(); } + + starRating.FadeOut(100); + Beatmap.Value = BeatmapSet?.Beatmaps.FirstOrDefault(); + plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0; + favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0; + + updateDifficultyButtons(); } public BeatmapPicker() @@ -140,6 +149,7 @@ namespace osu.Game.Overlays.BeatmapSet private void load(OsuColour colours) { starRating.Colour = colours.Yellow; + updateDisplay(); } protected override void LoadComplete() @@ -150,7 +160,10 @@ namespace osu.Game.Overlays.BeatmapSet Beatmap.TriggerChange(); } - private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap?.Version; + private void showBeatmap(BeatmapInfo beatmap) + { + version.Text = beatmap?.Version; + } private void updateDifficultyButtons() { diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs similarity index 97% rename from osu.Game/Overlays/BeatmapSet/DownloadButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index 0c6414c718..c699ae2328 100644 --- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using OpenTK; -namespace osu.Game.Overlays.BeatmapSet +namespace osu.Game.Overlays.BeatmapSet.Buttons { public class DownloadButton : HeaderButton { diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs similarity index 98% rename from osu.Game/Overlays/BeatmapSet/FavouriteButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 29bc00038c..3821c96369 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using OpenTK; -namespace osu.Game.Overlays.BeatmapSet +namespace osu.Game.Overlays.BeatmapSet.Buttons { public class FavouriteButton : HeaderButton { diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs similarity index 94% rename from osu.Game/Overlays/BeatmapSet/HeaderButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs index e1c4f5cc67..b46b5d2a0e 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs @@ -1,12 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Framework.Allocation; -namespace osu.Game.Overlays.BeatmapSet +namespace osu.Game.Overlays.BeatmapSet.Buttons { public class HeaderButton : TriangleButton { diff --git a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs similarity index 97% rename from osu.Game/Overlays/BeatmapSet/PreviewButton.cs rename to osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 6b5ffa57ad..08a99f1aea 100644 --- a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,12 +12,11 @@ using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Overlays.Direct; using OpenTK; using OpenTK.Graphics; -using osu.Game.Overlays.Direct; -using osu.Framework.Configuration; -namespace osu.Game.Overlays.BeatmapSet +namespace osu.Game.Overlays.BeatmapSet.Buttons { public class PreviewButton : OsuClickableContainer { @@ -85,6 +85,8 @@ namespace osu.Game.Overlays.BeatmapSet // prevent negative (potential infinite) width if a track without length was loaded progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; } + else + progress.Width = 0; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 7f3b6d1584..5264caf936 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -1,11 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; +using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Screens.Select.Details; using OpenTK; using OpenTK.Graphics; @@ -20,6 +22,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly UserRatings ratings; private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -33,19 +36,24 @@ namespace osu.Game.Overlays.BeatmapSet } private BeatmapInfo beatmap; + public BeatmapInfo Beatmap { get { return beatmap; } set { if (value == beatmap) return; - beatmap = value; - basic.Beatmap = advanced.Beatmap = Beatmap; - ratings.Metrics = Beatmap.Metrics; + basic.Beatmap = advanced.Beatmap = beatmap = value; + updateDisplay(); } } + private void updateDisplay() + { + ratings.Metrics = Beatmap?.Metrics; + } + public Details() { Width = BeatmapSetOverlay.RIGHT_WIDTH; @@ -88,6 +96,12 @@ namespace osu.Game.Overlays.BeatmapSet }; } + [BackgroundDependencyLoader] + private void load() + { + updateDisplay(); + } + public void StopPreview() => preview.Playing.Value = false; private class DetailBox : Container diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index ab3a981e77..9b25d61f58 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.BeatmapSet.Buttons; using OpenTK; using OpenTK.Graphics; @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet { public class Header : Container { - private const float transition_duration = 250; + private const float transition_duration = 200; private const float tabs_height = 50; private const float buttons_height = 45; private const float buttons_spacing = 5; @@ -34,12 +35,13 @@ namespace osu.Game.Overlays.BeatmapSet public Details Details; private BeatmapManager beatmaps; - private DelayedLoadWrapper cover; public readonly BeatmapPicker Picker; private BeatmapSetInfo beatmapSet; + private readonly FavouriteButton favouriteButton; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -48,19 +50,27 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - if (beatmapSet == null) - return; - Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; - title.Text = BeatmapSet.Metadata.Title; - artist.Text = BeatmapSet.Metadata.Artist; - onlineStatusPill.Status = BeatmapSet.OnlineInfo.Status; - downloadButtonsContainer.FadeIn(); + updateDisplay(); + } + } + + private void updateDisplay() + { + title.Text = BeatmapSet?.Metadata.Title ?? string.Empty; + artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty; + onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None; + + cover?.FadeOut(400, Easing.Out); + if (BeatmapSet != null) + { + downloadButtonsContainer.FadeIn(transition_duration); + favouriteButton.FadeIn(transition_duration); + noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration); videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration); - cover?.FadeOut(400, Easing.Out); coverContainer.Add(cover = new DelayedLoadWrapper( new BeatmapSetCover(BeatmapSet) { @@ -74,6 +84,11 @@ namespace osu.Game.Overlays.BeatmapSet RelativeSizeAxes = Axes.Both, }); } + else + { + downloadButtonsContainer.FadeOut(transition_duration); + favouriteButton.FadeOut(transition_duration); + } } public Header() @@ -169,7 +184,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Top = 10 }, Children = new Drawable[] { - new FavouriteButton(), + favouriteButton = new FavouriteButton(), downloadButtonsContainer = new Container { RelativeSizeAxes = Axes.Both, @@ -241,6 +256,8 @@ namespace osu.Game.Overlays.BeatmapSet this.beatmaps = beatmaps; beatmaps.ItemAdded += handleBeatmapAdd; + + updateDisplay(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 48485a8835..cd0b7386e8 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -34,14 +34,16 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmapSet) return; beatmapSet = value; - if (beatmapSet == null) - return; - - source.Text = BeatmapSet.Metadata.Source; - tags.Text = BeatmapSet.Metadata.Tags; + updateDisplay(); } } + private void updateDisplay() + { + source.Text = BeatmapSet?.Metadata.Source ?? string.Empty; + tags.Text = BeatmapSet?.Metadata.Tags ?? string.Empty; + } + public BeatmapInfo Beatmap { get { return successRate.Beatmap; } @@ -135,6 +137,8 @@ namespace osu.Game.Overlays.BeatmapSet successRateBackground.Colour = colours.GrayE; source.TextColour = description.TextColour = colours.Gray5; tags.TextColour = colours.BlueDark; + + updateDisplay(); } private class MetadataSection : FillFlowContainer diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index d5c5bd8ddd..185282bec9 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -2,15 +2,15 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK; -using OpenTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Online.API; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -22,49 +22,75 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly FillFlowContainer flow; private readonly DrawableTopScore topScore; private readonly LoadingAnimation loadingAnimation; - private readonly Box foreground; - private bool isLoading; - public bool IsLoading + private bool loading { - get { return isLoading; } - set - { - if (isLoading == value) return; - isLoading = value; - - foreground.FadeTo(isLoading ? 1 : 0, fade_duration); - loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration); - } + set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); } private IEnumerable scores; + private BeatmapInfo beatmap; + public IEnumerable Scores { get { return scores; } set { + getScoresRequest?.Cancel(); scores = value; - var scoresAmount = scores.Count(); - if (scoresAmount == 0) - { - CleanAllScores(); - return; - } - topScore.Score = scores.FirstOrDefault(); - topScore.Show(); - - flow.Clear(); - - if (scoresAmount < 2) - return; - - for (int i = 1; i < scoresAmount; i++) - flow.Add(new DrawableScore(i, scores.ElementAt(i))); + updateDisplay(); } } + private GetScoresRequest getScoresRequest; + private APIAccess api; + + public BeatmapInfo Beatmap + { + get => beatmap; + set + { + beatmap = value; + + Scores = null; + + if (beatmap?.OnlineBeatmapID.HasValue != true) + return; + + loading = true; + + getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); + getScoresRequest.Success += r => Scores = r.Scores; + api.Queue(getScoresRequest); + } + } + + private void updateDisplay() + { + loading = false; + + var scoreCount = scores?.Count() ?? 0; + + if (scoreCount == 0) + { + topScore.Hide(); + flow.Clear(); + return; + } + + topScore.Score = scores.FirstOrDefault(); + topScore.Show(); + + flow.Clear(); + + if (scoreCount < 2) + return; + + for (int i = 1; i < scoreCount; i++) + flow.Add(new DrawableScore(i, scores.ElementAt(i))); + } + public ScoresContainer() { RelativeSizeAxes = Axes.X; @@ -93,23 +119,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }, } }, - foreground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.7f), - Alpha = 0, - }, loadingAnimation = new LoadingAnimation { Alpha = 0, + Margin = new MarginPadding(20) }, }; } - public void CleanAllScores() + [BackgroundDependencyLoader] + private void load(APIAccess api) { - topScore.Hide(); - flow.Clear(); + this.api = api; + updateDisplay(); } } } diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index c64d3988a6..6572e520bd 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -29,18 +29,23 @@ namespace osu.Game.Overlays.BeatmapSet if (value == beatmap) return; beatmap = value; - int passCount = beatmap.OnlineInfo.PassCount; - int playCount = beatmap.OnlineInfo.PlayCount; - - var rate = playCount != 0 ? (float)passCount / playCount : 0; - successPercent.Text = rate.ToString("P0"); - successRate.Length = rate; - percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); - - graph.Metrics = Beatmap.Metrics; + updateDisplay(); } } + private void updateDisplay() + { + int passCount = beatmap?.OnlineInfo.PassCount ?? 0; + int playCount = beatmap?.OnlineInfo.PlayCount ?? 0; + + var rate = playCount != 0 ? (float)passCount / playCount : 0; + successPercent.Text = rate.ToString("P0"); + successRate.Length = rate; + percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); + + graph.Metrics = beatmap?.Metrics; + } + public SuccessRate() { Children = new Drawable[] @@ -74,7 +79,6 @@ namespace osu.Game.Overlays.BeatmapSet { Anchor = Anchor.TopRight, Origin = Anchor.TopCentre, - Text = @"0%", TextSize = 13, }, }, @@ -103,6 +107,8 @@ namespace osu.Game.Overlays.BeatmapSet successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5; successRate.AccentColour = colours.Green; successRate.BackgroundColour = colours.GrayD; + + updateDisplay(); } protected override void UpdateAfterChildren() diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 3be635ca00..5a3bbf69ad 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -18,7 +18,6 @@ using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; using osu.Game.Overlays.BeatmapSet.Scores; using System.Linq; -using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays { @@ -31,12 +30,9 @@ namespace osu.Game.Overlays private readonly Header header; private readonly Info info; - private readonly ScoresContainer scores; - private readonly LoadingAnimation loading; private APIAccess api; private RulesetStore rulesets; - private GetScoresRequest getScoresRequest; private readonly ScrollContainer scroll; @@ -50,18 +46,7 @@ namespace osu.Game.Overlays if (value == beatmapSet) return; - beatmapSet = value; - - if (beatmapSet == null) - { - scroll.FadeOut(fade_duration); - loading.Show(); - return; - } - - header.BeatmapSet = info.BeatmapSet = beatmapSet; - loading.Hide(); - scroll.FadeIn(fade_duration); + header.BeatmapSet = info.BeatmapSet = beatmapSet = value; } } @@ -70,6 +55,7 @@ namespace osu.Game.Overlays public BeatmapSetOverlay() { + ScoresContainer scores; Waves.FirstWaveColour = OsuColour.Gray(0.4f); Waves.SecondWaveColour = OsuColour.Gray(0.3f); Waves.ThirdWaveColour = OsuColour.Gray(0.2f); @@ -96,15 +82,10 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.2f) }, - loading = new LoadingAnimation - { - State = Visibility.Visible, - }, scroll = new ScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, - Alpha = 0, Child = new ReverseChildIDFillFlowContainer { RelativeSizeAxes = Axes.X, @@ -123,33 +104,10 @@ namespace osu.Game.Overlays header.Picker.Beatmap.ValueChanged += b => { info.Beatmap = b; - - if (b != null) - updateScores(b); + scores.Beatmap = b; }; } - private void updateScores(BeatmapInfo beatmap) - { - getScoresRequest?.Cancel(); - - if (!beatmap.OnlineBeatmapID.HasValue) - { - scores.CleanAllScores(); - return; - } - - scores.IsLoading = true; - - getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += r => - { - scores.Scores = r.Scores; - scores.IsLoading = false; - }; - api.Queue(getScoresRequest); - } - [BackgroundDependencyLoader] private void load(APIAccess api, RulesetStore rulesets) { @@ -178,7 +136,7 @@ namespace osu.Game.Overlays return true; } - public void ShowBeatmap(int beatmapId) + public void FetchAndShowBeatmap(int beatmapId) { BeatmapSet = null; var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); @@ -191,7 +149,7 @@ namespace osu.Game.Overlays Show(); } - public void ShowBeatmapSet(int beatmapSetId) + public void FetchAndShowBeatmapSet(int beatmapSetId) { BeatmapSet = null; var req = new GetBeatmapSetRequest(beatmapSetId); diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 44e24d8157..4913b11ae1 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -78,12 +78,7 @@ namespace osu.Game.Overlays.Direct loadingAnimation = new LoadingAnimation(), }); - Playing.ValueChanged += playing => - { - icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play; - icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); - updatePreviewTrack(playing); - }; + Playing.ValueChanged += updatePreviewTrack; } [BackgroundDependencyLoader] @@ -125,6 +120,15 @@ namespace osu.Game.Overlays.Direct private void updatePreviewTrack(bool playing) { + if (playing && BeatmapSet == null) + { + Playing.Value = false; + return; + } + + icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play; + icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); + if (playing) { if (Preview == null) diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index da08c08179..97079c77f3 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Sections { Action = () => { - if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); + if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); }; Child = new FillFlowContainer diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index d60e7669a5..f39952dc31 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Select.Carousel new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap)), new OsuMenuItem("Details", MenuItemType.Standard, () => { - if (beatmap.OnlineBeatmapID.HasValue) beatmapOverlay?.ShowBeatmap(beatmap.OnlineBeatmapID.Value); + if (beatmap.OnlineBeatmapID.HasValue) beatmapOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value); }), }; } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index ad7588edde..d554a22735 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Select.Carousel restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); dialogOverlay = overlay; if (beatmapOverlay != null) - viewDetails = beatmapOverlay.ShowBeatmapSet; + viewDetails = beatmapOverlay.FetchAndShowBeatmapSet; Children = new Drawable[] { diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index cad7ed7d81..852e4f190f 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -40,10 +40,10 @@ namespace osu.Game.Screens.Select.Details firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0; } - hpDrain.Value = beatmap.BaseDifficulty?.DrainRate ?? 0; - accuracy.Value = beatmap.BaseDifficulty?.OverallDifficulty ?? 0; - approachRate.Value = beatmap.BaseDifficulty?.ApproachRate ?? 0; - starDifficulty.Value = (float)beatmap.StarDifficulty; + hpDrain.Value = Beatmap?.BaseDifficulty?.DrainRate ?? 0; + accuracy.Value = Beatmap?.BaseDifficulty?.OverallDifficulty ?? 0; + approachRate.Value = Beatmap?.BaseDifficulty?.ApproachRate ?? 0; + starDifficulty.Value = (float)(Beatmap?.StarDifficulty ?? 0); } } diff --git a/osu.Game/Screens/Select/Details/FailRetryGraph.cs b/osu.Game/Screens/Select/Details/FailRetryGraph.cs index a33cee21ed..bf4eb07108 100644 --- a/osu.Game/Screens/Select/Details/FailRetryGraph.cs +++ b/osu.Game/Screens/Select/Details/FailRetryGraph.cs @@ -25,10 +25,10 @@ namespace osu.Game.Screens.Select.Details if (value == metrics) return; metrics = value; - var retries = Metrics.Retries; - var fails = Metrics.Fails; + var retries = Metrics?.Retries ?? new int[0]; + var fails = Metrics?.Fails ?? new int[0]; - float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max(); + float maxValue = fails.Any() ? fails.Zip(retries, (fail, retry) => fail + retry).Max() : 0; failGraph.MaxValue = maxValue; retryGraph.MaxValue = maxValue; diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index bf50217048..787b22f965 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -21,6 +21,7 @@ namespace osu.Game.Screens.Select.Details private readonly BarGraph graph; private BeatmapMetrics metrics; + public BeatmapMetrics Metrics { get { return metrics; } @@ -31,15 +32,25 @@ namespace osu.Game.Screens.Select.Details const int rating_range = 10; - var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. + if (metrics == null) + { + negativeRatings.Text = "0"; + positiveRatings.Text = "0"; + ratingsBar.Length = 0; + graph.Values = new float[rating_range]; + } + else + { + var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. - var negativeCount = ratings.Take(rating_range / 2).Sum(); - var totalCount = ratings.Sum(); + var negativeCount = ratings.Take(rating_range / 2).Sum(); + var totalCount = ratings.Sum(); - negativeRatings.Text = negativeCount.ToString(); - positiveRatings.Text = (totalCount - negativeCount).ToString(); - ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; - graph.Values = ratings.Take(rating_range).Select(r => (float)r); + negativeRatings.Text = negativeCount.ToString(); + positiveRatings.Text = (totalCount - negativeCount).ToString(); + ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; + graph.Values = ratings.Take(rating_range).Select(r => (float)r); + } } } diff --git a/osu.Game/Users/UpdateableAvatar.cs b/osu.Game/Users/UpdateableAvatar.cs index 31455801da..6c0b841abf 100644 --- a/osu.Game/Users/UpdateableAvatar.cs +++ b/osu.Game/Users/UpdateableAvatar.cs @@ -15,6 +15,11 @@ namespace osu.Game.Users private User user; + /// + /// Whether to show a default guest representation on null user (as opposed to nothing). + /// + public bool ShowGuestOnNull = true; + public User User { get { return user; } @@ -40,13 +45,16 @@ namespace osu.Game.Users { displayedAvatar?.FadeOut(300); displayedAvatar?.Expire(); - Add(displayedAvatar = new DelayedLoadWrapper( - new Avatar(user) - { - RelativeSizeAxes = Axes.Both, - OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), - }) - ); + if (user != null || ShowGuestOnNull) + { + Add(displayedAvatar = new DelayedLoadWrapper( + new Avatar(user) + { + RelativeSizeAxes = Axes.Both, + OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), + }) + ); + } } } } From e395a471125bc360035062e25a62856859cd1d0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Apr 2018 19:26:54 +0900 Subject: [PATCH 492/537] Changes to naming and text --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- .../Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs | 4 ++-- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index ebc674c900..3efaa02a31 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -86,7 +86,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); Set(OsuSetting.ScreenshotCaptureMenuCursor, false); - Set(OsuSetting.SelectScrollRightClick, false); + Set(OsuSetting.SongSelectRightMouseScroll, false); } public OsuConfigManager(Storage storage) : base(storage) @@ -133,6 +133,6 @@ namespace osu.Game.Configuration Skin, ScreenshotFormat, ScreenshotCaptureMenuCursor, - SelectScrollRightClick + SongSelectRightMouseScroll } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index c3d8022a63..7893d76fb8 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -19,8 +19,8 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsCheckbox { - LabelText = "Right click to scroll", - Bindable = config.GetBindable(OsuSetting.SelectScrollRightClick), + LabelText = "Right mouse drag to absolute scroll", + Bindable = config.GetBindable(OsuSetting.SongSelectRightMouseScroll), }, new SettingsCheckbox { diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index e88fec8106..3c9a14e1f4 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Select private void load(OsuConfigManager config) { config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); - config.BindWith(OsuSetting.SelectScrollRightClick, RightClickScrollingEnabled); + config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled); RightClickScrollingEnabled.ValueChanged += v => RightMouseScrollbar = v; RightClickScrollingEnabled.TriggerChange(); From 278a878b09fc08d5a854f3d11c736738c90a31d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Apr 2018 15:52:24 +0900 Subject: [PATCH 493/537] Add non-interactive deploy mode --- osu.Desktop.Deploy/Program.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 6095ce062d..8c460f03cf 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -57,8 +57,12 @@ namespace osu.Desktop.Deploy private static string codeSigningPassword; + private static bool interactive; + public static void Main(string[] args) { + interactive = args.Length == 0; + displayHeader(); findSolutionPath(); @@ -82,15 +86,15 @@ namespace osu.Desktop.Deploy string version = $"{verBase}{increment}"; Console.ForegroundColor = ConsoleColor.White; - Console.Write($"Ready to deploy {version}: "); - Console.ReadLine(); + Console.Write($"Ready to deploy {version}!"); + pauseIfInteractive(); sw.Start(); if (!string.IsNullOrEmpty(CodeSigningCertificate)) { Console.Write("Enter code signing password: "); - codeSigningPassword = readLineMasked(); + codeSigningPassword = args.Length > 0 ? args[0] : readLineMasked(); } write("Updating AssemblyInfo..."); @@ -124,7 +128,7 @@ namespace osu.Desktop.Deploy updateCsprojVersion("0.0.0"); write("Done!", ConsoleColor.White); - Console.ReadLine(); + pauseIfInteractive(); } private static void displayHeader() @@ -388,10 +392,18 @@ namespace osu.Desktop.Deploy Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"FATAL ERROR: {message}"); - Console.ReadLine(); + pauseIfInteractive(); Environment.Exit(-1); } + private static void pauseIfInteractive() + { + if (interactive) + Console.ReadLine(); + else + Console.WriteLine(); + } + private static void write(string message, ConsoleColor col = ConsoleColor.Gray) { if (sw.ElapsedMilliseconds > 0) From 312068d7a29716ab1737c4aa86b4a3bee6153df4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Apr 2018 17:15:06 +0900 Subject: [PATCH 494/537] Add deploy config --- appveyor_deploy.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 appveyor_deploy.yml diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml new file mode 100644 index 0000000000..3887557fcb --- /dev/null +++ b/appveyor_deploy.yml @@ -0,0 +1,28 @@ +# 2017-09-14 +clone_depth: 1 +version: '{branch}-{build}' +image: Visual Studio 2017 +configuration: Debug +cache: + - packages -> **\packages.config +install: + - cmd: git submodule update --init --recursive --depth=5 +before_build: + - cmd: nuget restore -verbosity quiet +build: + project: osu.Desktop.Deploy/osu.Desktop.Deploy.csproj + verbosity: minimal +after_build: + - ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1')) + - appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate + - cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx + - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration + - cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net461\osu.Desktop.Deploy.exe.config + - cd osu.Desktop.Deploy\bin\Debug\net461\ + - osu.Desktop.Deploy.exe %code_signing_password% +environment: + TargetFramework: net461 + decode_secret: + secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY= + code_signing_password: + secure: 34tLNqvjmmZEi97MLKfrnQ== From 8a243461a697d6cfcf1c8adec6167fb523b22775 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Apr 2018 17:42:06 +0900 Subject: [PATCH 495/537] Expose appveyor artifacts --- appveyor_deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml index 3887557fcb..cd241eb88f 100644 --- a/appveyor_deploy.yml +++ b/appveyor_deploy.yml @@ -26,3 +26,5 @@ environment: secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY= code_signing_password: secure: 34tLNqvjmmZEi97MLKfrnQ== +artifacts: + - path: 'Releases\*' \ No newline at end of file From 0356e5e6bf9a31ed35a3741d64d5649cf39093e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Apr 2018 21:00:22 +0900 Subject: [PATCH 496/537] Remove unused property --- osu.Game/Overlays/BeatmapSetOverlay.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 74550e3a9b..366c34eae3 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -32,20 +32,6 @@ namespace osu.Game.Overlays private readonly ScrollContainer scroll; - private BeatmapSetInfo beatmapSet; - - public BeatmapSetInfo BeatmapSet - { - get => beatmapSet; - set - { - if (value == beatmapSet) - return; - - header.BeatmapSet = info.BeatmapSet = beatmapSet = value; - } - } - // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; From 77af8ce556cde3b36c32fe7771404cb5daa87f30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Apr 2018 21:11:16 +0900 Subject: [PATCH 497/537] Add back online load test for BeatmapScoresContainer --- osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs index 85f3364039..5be7386238 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs @@ -14,6 +14,8 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Users; using System.Collections.Generic; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu; namespace osu.Game.Tests.Visual { @@ -50,6 +52,8 @@ namespace osu.Game.Tests.Visual AddStep("remove scores", () => scoresContainer.Scores = null); AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); + AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapSetID = 1, OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo }); + scores = new[] { From 64709da5461d35d6e66ed02bc6f81c1940296b48 Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Thu, 19 Apr 2018 14:46:42 +0200 Subject: [PATCH 498/537] Update profile header to osu-web changes --- osu.Game/Overlays/Profile/ProfileHeader.cs | 10 ++++++++-- osu.Game/Users/User.cs | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index ec0e45d5ca..361804a367 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -350,7 +350,7 @@ namespace osu.Game.Overlays.Profile if (user.Country != null) { - infoTextLeft.AddText("from ", lightText); + infoTextLeft.AddText("From ", lightText); infoTextLeft.AddText(user.Country.FullName, boldItalic); countryFlag.Country = user.Country; } @@ -378,6 +378,10 @@ namespace osu.Game.Overlays.Profile infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); } + infoTextLeft.NewLine(); + infoTextLeft.AddText("Contributed ", lightText); + infoTextLeft.AddText($@"{user.PostCount} forum posts", boldItalic); + string websiteWithoutProtcol = user.Website; if (!string.IsNullOrEmpty(websiteWithoutProtcol)) { @@ -392,8 +396,10 @@ namespace osu.Game.Overlays.Profile infoTextRight.NewParagraph(); if (!string.IsNullOrEmpty(user.Twitter)) tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); - tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website); + tryAddInfoRightLine(FontAwesome.fa_question, user.Discord); tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); + tryAddInfoRightLine(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); + tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website); if (user.Statistics != null) { diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index b983b639f0..e1f68e1ce8 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -98,9 +98,15 @@ namespace osu.Game.Users [JsonProperty(@"skype")] public string Skype; + [JsonProperty(@"discord")] + public string Discord; + [JsonProperty(@"website")] public string Website; + [JsonProperty(@"post_count")] + public int PostCount; + [JsonProperty(@"playstyle")] public string[] PlayStyle; From 54fa725309b01e940c93f966d41ed86e216d65ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 12:46:45 +0900 Subject: [PATCH 499/537] Remove unnecessary test --- osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs index 4154dbde47..025562f75f 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs @@ -401,7 +401,6 @@ namespace osu.Game.Tests.Visual AddStep(@"hide", overlay.Hide); AddStep(@"show without reload", overlay.Show); - AddStep(@"show loading", () => overlay.BeatmapSet = null); } } } From 8eefd04fcb9dd1ef25431726402b0edb20b509e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 12:47:23 +0900 Subject: [PATCH 500/537] Don't return overlay to null until it has been completely hidden --- osu.Game/Overlays/BeatmapSetOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 5a3bbf69ad..096f7bb63c 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -126,8 +126,7 @@ namespace osu.Game.Overlays base.PopOut(); header.Details.StopPreview(); - FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); - BeatmapSet = null; + FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null); } protected override bool OnClick(InputState state) From 8649ddc68b407ec12a0f84c1eeffdba101d296b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 13:01:23 +0900 Subject: [PATCH 501/537] Make forum posts a web link --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 4 ++-- osu.Game/Overlays/Profile/ProfileHeader.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 8e18dbd2f5..c39e9abf8b 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -61,9 +61,9 @@ namespace osu.Game.Graphics.Containers AddText(text.Substring(previousLinkEnd)); } - public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null) + public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action creationParameters = null) { - AddInternal(new DrawableLinkCompiler(AddText(text).ToList()) + AddInternal(new DrawableLinkCompiler(AddText(text, creationParameters).ToList()) { TooltipText = tooltipText ?? (url != text ? url : string.Empty), Action = () => diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 361804a367..49a4f71fd8 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile { public class ProfileHeader : Container { - private readonly OsuTextFlowContainer infoTextLeft; + private readonly LinkFlowContainer infoTextLeft; private readonly LinkFlowContainer infoTextRight; private readonly FillFlowContainer scoreText, scoreNumberText; private readonly RankGraph rankGraph; @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Profile } } }, - infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14) + infoTextLeft = new LinkFlowContainer(t => t.TextSize = 14) { X = UserProfileOverlay.CONTENT_X_MARGIN, Y = cover_height + 20, @@ -380,7 +380,7 @@ namespace osu.Game.Overlays.Profile infoTextLeft.NewLine(); infoTextLeft.AddText("Contributed ", lightText); - infoTextLeft.AddText($@"{user.PostCount} forum posts", boldItalic); + infoTextLeft.AddLink($@"{user.PostCount} forum posts", url: $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: boldItalic); string websiteWithoutProtcol = user.Website; if (!string.IsNullOrEmpty(websiteWithoutProtcol)) From a64ed142f07e6ddade0ea17110a1e92124d2c8c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 13:51:30 +0900 Subject: [PATCH 502/537] Use a more suiting icon for discord for now --- osu.Game/Overlays/Profile/ProfileHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 49a4f71fd8..4c411b3210 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -396,7 +396,7 @@ namespace osu.Game.Overlays.Profile infoTextRight.NewParagraph(); if (!string.IsNullOrEmpty(user.Twitter)) tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); - tryAddInfoRightLine(FontAwesome.fa_question, user.Discord); + tryAddInfoRightLine(FontAwesome.fa_gamepad, user.Discord); tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); tryAddInfoRightLine(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}"); tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website); From 1bab601cbc85318eff49efc6ca049ec9fab4198a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Apr 2018 13:51:36 +0900 Subject: [PATCH 503/537] Comments + xmldocs --- .../OverlappingSpeedChangeVisualiser.cs | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index c5e91c6929..6045ab50e7 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -23,21 +23,24 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { foreach (var obj in hitObjects) { + // For optimal lifetimes, the speed of the hitobject is factored into the time range obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPointAt(obj.HitObject.StartTime).Multiplier; if (obj.HitObject is IHasEndTime endTime) { - var diff = -positionAt(endTime.EndTime, obj, timeRange); + // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. + // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. + var hitObjectLength = -hitObjectPositionAt(obj, endTime.EndTime, timeRange); switch (direction) { case ScrollingDirection.Up: case ScrollingDirection.Down: - obj.Height = (float)(diff * length.Y); + obj.Height = (float)(hitObjectLength * length.Y); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - obj.Width = (float)(diff * length.X); + obj.Width = (float)(hitObjectLength * length.X); break; } } @@ -45,6 +48,8 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers if (obj.HasNestedHitObjects) { ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); + + // Nested hitobjects don't need to scroll, but they do need accurate positions ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); } } @@ -54,7 +59,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { foreach (var obj in hitObjects) { - var position = positionAt(currentTime, obj, timeRange); + var position = hitObjectPositionAt(obj, currentTime, timeRange); switch (direction) { @@ -74,10 +79,26 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers } } - private double positionAt(double time, DrawableHitObject obj, double timeRange) + /// + /// Computes the position of a at a point in time.
+ /// At t < startTime, position > 0.
+ /// At t = startTime, position = 0.
+ /// At t > startTime, position < 0. + ///
+ /// The . + /// The time to find the position of at. + /// The amount of time visualised by the scrolling area. + /// The position of in the scrolling area at time = . + private double hitObjectPositionAt(DrawableHitObject obj, double time, double timeRange) => (obj.HitObject.StartTime - time) * controlPointAt(obj.HitObject.StartTime).Multiplier / timeRange; private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); + + /// + /// Finds the which affects the speed of hitobjects at a specific time. + /// + /// The time which the should affect. + /// The . private MultiplierControlPoint controlPointAt(double time) { if (controlPoints.Count == 0) From 48b421b4b417ec2113980b8561c76482bc66fbbf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Apr 2018 14:16:30 +0900 Subject: [PATCH 504/537] Add comments to SequentialSpeedChangeVisualiser --- .../OverlappingSpeedChangeVisualiser.cs | 4 ++- .../SequentialSpeedChangeVisualiser.cs | 29 ++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index 6045ab50e7..e45e391dbc 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -80,10 +80,12 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers } /// - /// Computes the position of a at a point in time.
+ /// Computes the position of a at a point in time. + /// /// At t < startTime, position > 0.
/// At t = startTime, position = 0.
/// At t > startTime, position < 0. + ///
///
/// The . /// The time to find the position of at. diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs index 708a2f173b..c8725fab56 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs @@ -25,23 +25,25 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { foreach (var obj in hitObjects) { + // To reduce iterations when updating hitobject positions later on, their initial positions are cached var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange); + // Todo: This is approximate and will be incorrect in the case of extreme speed changes obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000; if (obj.HitObject is IHasEndTime endTime) { - var diff = positionAt(endTime.EndTime, timeRange) - startPosition; + var hitObjectLength = positionAt(endTime.EndTime, timeRange) - startPosition; switch (direction) { case ScrollingDirection.Up: case ScrollingDirection.Down: - obj.Height = (float)(diff * length.Y); + obj.Height = (float)(hitObjectLength * length.Y); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - obj.Width = (float)(diff * length.X); + obj.Width = (float)(hitObjectLength * length.X); break; } } @@ -49,6 +51,8 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers if (obj.HasNestedHitObjects) { ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); + + // Nested hitobjects don't need to scroll, but they do need accurate positions ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); } } @@ -80,21 +84,38 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers } } + /// + /// Finds the position which corresponds to a point in time. + /// This is a non-linear operation that depends on all the control points up to and including the one active at the time value. + /// + /// The time to find the position at. + /// The amount of time visualised by the scrolling area. + /// A positive value indicating the position at . private double positionAt(double time, double timeRange) { double length = 0; + + // We need to consider all timing points until the specified time and not just the currently-active one, + // since each timing point individually affects the positions of _all_ hitobjects after its start time for (int i = 0; i < controlPoints.Count; i++) { var current = controlPoints[i]; var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null; + // We don't need to consider any control points beyond the current time, since it will not yet + // affect any hitobjects if (i > 0 && current.StartTime > time) continue; // Duration of the current control point var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime; - length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange; + // We want to consider the minimal amount of time that this control point has affected, + // which may be either its duration, or the amount of time that has passed within it + var durationInCurrent = Math.Min(currentDuration, time - current.StartTime); + + // Figure out how much of the time range the duration represents, and adjust it by the speed multiplier + length += durationInCurrent / timeRange * current.Multiplier; } return length; From f3fddcc82cf3dbb92f3290fba90792d2522a56da Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Apr 2018 14:20:04 +0900 Subject: [PATCH 505/537] Reorder parameter for consistency --- .../Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index e45e391dbc..eaac10873e 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers /// The amount of time visualised by the scrolling area. /// The position of in the scrolling area at time = . private double hitObjectPositionAt(DrawableHitObject obj, double time, double timeRange) - => (obj.HitObject.StartTime - time) * controlPointAt(obj.HitObject.StartTime).Multiplier / timeRange; + => (obj.HitObject.StartTime - time) / timeRange * controlPointAt(obj.HitObject.StartTime).Multiplier; private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); From 52e3ffff303327052c0ab962682c2574c7f3a572 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Apr 2018 14:20:16 +0900 Subject: [PATCH 506/537] Add some more commenting to lifetime calculation --- .../Visualisers/OverlappingSpeedChangeVisualiser.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index eaac10873e..d0dc65fe33 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -23,8 +23,10 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { foreach (var obj in hitObjects) { - // For optimal lifetimes, the speed of the hitobject is factored into the time range - obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPointAt(obj.HitObject.StartTime).Multiplier; + // The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases + double visibleDuration = timeRange / controlPointAt(obj.HitObject.StartTime).Multiplier; + + obj.LifetimeStart = obj.HitObject.StartTime - visibleDuration; if (obj.HitObject is IHasEndTime endTime) { From 11b943c820280282b276dcd3b923abaf9376f072 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Apr 2018 14:22:48 +0900 Subject: [PATCH 507/537] ComputePositions -> UpdatePositions --- .../UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- .../Scrolling/Visualisers/ISpeedChangeVisualiser.cs | 13 ++++++------- .../Visualisers/OverlappingSpeedChangeVisualiser.cs | 4 ++-- .../Visualisers/SequentialSpeedChangeVisualiser.cs | 4 ++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index edfea57e94..36c6b07f54 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.UI.Scrolling base.UpdateAfterChildrenLife(); // We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions - speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize); + speedChangeVisualiser.UpdatePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize); } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs index 02791e0517..097e28b2dc 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs @@ -10,24 +10,23 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers public interface ISpeedChangeVisualiser { /// - /// Computes the states of s that are constant, such as lifetime and spatial length. + /// Computes the states of s that remain constant while scrolling, such as lifetime and spatial length. /// This is invoked once whenever or changes. /// /// The s whose states should be computed. /// The scrolling direction. - /// The duration required to scroll through one length of the screen before any control point adjustments. + /// The duration required to scroll through one length of the screen before any speed adjustments. /// The length of the screen that is scrolled through. void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length); /// - /// Computes the states of s that change depending on , such as position. - /// This is invoked once per frame. + /// Updates the positions of s, depending on the current time. This is invoked once per frame. /// - /// The s whose states should be computed. + /// The s whose positions should be computed. /// The scrolling direction. /// The current time. - /// The duration required to scroll through one length of the screen before any control point adjustments. + /// The duration required to scroll through one length of the screen before any speed adjustments. /// The length of the screen that is scrolled through. - void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length); + void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index d0dc65fe33..35a91275a7 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -52,12 +52,12 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); // Nested hitobjects don't need to scroll, but they do need accurate positions - ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); } } } - public void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) { foreach (var obj in hitObjects) { diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs index c8725fab56..e353c07e9f 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs @@ -53,12 +53,12 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); // Nested hitobjects don't need to scroll, but they do need accurate positions - ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); } } } - public void ComputePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) { var timelinePosition = positionAt(currentTime, timeRange); From de424648d27bd82d9d00f55a9da62b6d23d25af4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 14:41:57 +0900 Subject: [PATCH 508/537] Update to .NET 4.7.1 Resolves #2368. --- .../RulesetTests__catch_.xml | 22 ++++++++ .../RulesetTests__mania_.xml | 22 ++++++++ .../runConfigurations/RulesetTests__osu__.xml | 22 ++++++++ .../RulesetTests__taiko_.xml | 22 ++++++++ ...__net461_.xml => VisualTests__net471_.xml} | 6 +-- .../{osu___net461_.xml => osu___net471_.xml} | 6 +-- .vscode/launch.json | 16 +++--- .vscode/tasks.json | 6 +-- README.md | 2 +- appveyor.yml | 52 +++++++++---------- appveyor_deploy.yml | 6 +-- osu-framework | 2 +- osu.Desktop.Deploy/.vscode/launch.json | 4 +- osu.Desktop.Deploy/osu.Desktop.Deploy.csproj | 4 +- osu.Desktop/osu.Desktop.csproj | 6 +-- .../.vscode/launch.json | 8 +-- .../.vscode/tasks.json | 6 +-- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../.vscode/launch.json | 8 +-- .../.vscode/tasks.json | 6 +-- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- .../.vscode/launch.json | 8 +-- .../.vscode/tasks.json | 6 +-- .../osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../.vscode/launch.json | 8 +-- .../.vscode/tasks.json | 6 +-- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 17 ++++-- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- 29 files changed, 188 insertions(+), 93 deletions(-) create mode 100644 .idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml create mode 100644 .idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml create mode 100644 .idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml create mode 100644 .idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml rename .idea/.idea.osu/.idea/runConfigurations/{VisualTests__net461_.xml => VisualTests__net471_.xml} (82%) rename .idea/.idea.osu/.idea/runConfigurations/{osu___net461_.xml => osu___net471_.xml} (83%) diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml new file mode 100644 index 0000000000..f8003ae339 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml new file mode 100644 index 0000000000..417ab1529b --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml new file mode 100644 index 0000000000..df93422f50 --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml new file mode 100644 index 0000000000..bb913778cc --- /dev/null +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net471_.xml similarity index 82% rename from .idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml rename to .idea/.idea.osu/.idea/runConfigurations/VisualTests__net471_.xml index cf4bccfe60..20a15f985f 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net461_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__net471_.xml @@ -1,6 +1,6 @@ - - \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml b/.idea/.idea.osu/.idea/runConfigurations/osu___net471_.xml similarity index 83% rename from .idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml rename to .idea/.idea.osu/.idea/runConfigurations/osu___net471_.xml index 971868a81b..7196e486d2 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/osu___net461_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/osu___net471_.xml @@ -1,6 +1,6 @@ - - \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 624e584f10..df5b11f63a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe", + "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe", + "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, @@ -30,13 +30,13 @@ "console": "internalConsole" }, { - "name": "osu! (Debug, net461)", + "name": "osu! (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net461/osu!.exe", + "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net471/osu!.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -44,13 +44,13 @@ "console": "internalConsole" }, { - "name": "osu! (Release, net461)", + "name": "osu! (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/osu.Desktop/bin/Release/net461/osu!.exe", + "program": "${workspaceRoot}/osu.Desktop/bin/Release/net471/osu!.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b1d2c6b57d..7144a584f3 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,7 +8,7 @@ "type": "shell", "command": "msbuild", "args": [ - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -22,7 +22,7 @@ "command": "msbuild", "args": [ "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -64,7 +64,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ diff --git a/README.md b/README.md index 47df86f57e..9d19f16ebd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This is still heavily under development and is not intended for end-user use. Th # Requirements -- A desktop platform that can compile .NET 4.6.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here. +- A desktop platform that can compile .NET 4.7.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here. # Getting Started - Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`) diff --git a/appveyor.yml b/appveyor.yml index 4c4b70827f..c25c2a659e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,27 +1,27 @@ -# 2017-09-14 -clone_depth: 1 -version: '{branch}-{build}' -image: Visual Studio 2017 -configuration: Debug -cache: - - C:\ProgramData\chocolatey\bin -> appveyor.yml - - C:\ProgramData\chocolatey\lib -> appveyor.yml - - inspectcode -> appveyor.yml - - packages -> **\packages.config -install: - - cmd: git submodule update --init --recursive --depth=5 - - cmd: choco install resharper-clt -y - - cmd: choco install nvika -y - - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe -before_build: - - cmd: CodeFileSanity.exe - - cmd: nuget restore -verbosity quiet -environment: - TargetFramework: net461 -build: - project: osu.sln - parallel: true - verbosity: minimal -after_build: - - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL +# 2017-09-14 +clone_depth: 1 +version: '{branch}-{build}' +image: Visual Studio 2017 +configuration: Debug +cache: + - C:\ProgramData\chocolatey\bin -> appveyor.yml + - C:\ProgramData\chocolatey\lib -> appveyor.yml + - inspectcode -> appveyor.yml + - packages -> **\packages.config +install: + - cmd: git submodule update --init --recursive --depth=5 + - cmd: choco install resharper-clt -y + - cmd: choco install nvika -y + - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe +before_build: + - cmd: CodeFileSanity.exe + - cmd: nuget restore -verbosity quiet +environment: + TargetFramework: net471 +build: + project: osu.sln + parallel: true + verbosity: minimal +after_build: + - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml index cd241eb88f..dc43a9cb13 100644 --- a/appveyor_deploy.yml +++ b/appveyor_deploy.yml @@ -17,11 +17,11 @@ after_build: - appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate - cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration - - cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net461\osu.Desktop.Deploy.exe.config - - cd osu.Desktop.Deploy\bin\Debug\net461\ + - cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net471\osu.Desktop.Deploy.exe.config + - cd osu.Desktop.Deploy\bin\Debug\net471\ - osu.Desktop.Deploy.exe %code_signing_password% environment: - TargetFramework: net461 + TargetFramework: net471 decode_secret: secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY= code_signing_password: diff --git a/osu-framework b/osu-framework index 16e6a453db..a5e5b64a92 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a +Subproject commit a5e5b64a9270df6704e7d78126e7b1541064f209 diff --git a/osu.Desktop.Deploy/.vscode/launch.json b/osu.Desktop.Deploy/.vscode/launch.json index 82cd6b4c13..8c35d211bd 100644 --- a/osu.Desktop.Deploy/.vscode/launch.json +++ b/osu.Desktop.Deploy/.vscode/launch.json @@ -7,7 +7,7 @@ "name": "Deploy (Debug)", "request": "launch", "type": "mono", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Desktop.Deploy.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Desktop.Deploy.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug)", "runtimeExecutable": null, @@ -18,7 +18,7 @@ "name": "Deploy (Release)", "request": "launch", "type": "clr", - "program": "${workspaceRoot}/bin/Release/net461/osu.Desktop.Deploy.exe", + "program": "${workspaceRoot}/bin/Release/net471/osu.Desktop.Deploy.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release)", "runtimeExecutable": null, diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index 6b7509a381..d3f6a4aed5 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -1,7 +1,7 @@ - net461 + net471 Exe AnyCPU true @@ -12,7 +12,7 @@ - + \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 2ad7b67842..27bc3f7597 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -1,7 +1,7 @@  - net461;netcoreapp2.0 + net471;netcoreapp2.0 WinExe AnyCPU true @@ -14,7 +14,7 @@ 0.0.0.0 - $(DefineConstants);NET_FRAMEWORK + $(DefineConstants);NET_FRAMEWORK osu.Desktop.Program @@ -31,7 +31,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json index 5098b78a42..eb80f4474c 100644 --- a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Catch.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Catch.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json index d21bb8a69a..41ae88f425 100644 --- a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json @@ -9,7 +9,7 @@ "command": "msbuild", "args": [ "osu.Game.Rulesets.Catch.Tests.csproj", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -24,7 +24,7 @@ "args": [ "osu.Game.Rulesets.Catch.Tests.csproj", "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -66,7 +66,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ 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 7a4c7b3f1c..3797edde61 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 @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json index c71178059b..fceb403f30 100644 --- a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Mania.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Mania.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json index 781e89598f..b04b068b0d 100644 --- a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json @@ -9,7 +9,7 @@ "command": "msbuild", "args": [ "osu.Game.Rulesets.Mania.Tests.csproj", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -24,7 +24,7 @@ "args": [ "osu.Game.Rulesets.Mania.Tests.csproj", "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -66,7 +66,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ 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 02040fd23f..e90155568e 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 @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json index 24431eb8de..714fb6db6f 100644 --- a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Osu.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Osu.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json index 734e15353b..657fe07e1a 100644 --- a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json @@ -9,7 +9,7 @@ "command": "msbuild", "args": [ "osu.Game.Rulesets.Osu.Tests.csproj", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -24,7 +24,7 @@ "args": [ "osu.Game.Rulesets.Osu.Tests.csproj", "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -66,7 +66,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 49c5f15713..1695ceacee 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json index caa90c32ce..e1df54e99b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json @@ -2,13 +2,13 @@ "version": "0.2.0", "configurations": [ { - "name": "VisualTests (Debug, net461)", + "name": "VisualTests (Debug, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Taiko.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, msbuild)", "runtimeExecutable": null, @@ -16,13 +16,13 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, net461)", + "name": "VisualTests (Release, net471)", "windows": { "type": "clr" }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Taiko.Tests.exe", + "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json index 13044e1ccb..8bdbcd8e8e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json @@ -9,7 +9,7 @@ "command": "msbuild", "args": [ "osu.Game.Rulesets.Taiko.Tests.csproj", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -24,7 +24,7 @@ "args": [ "osu.Game.Rulesets.Taiko.Tests.csproj", "/p:Configuration=Release", - "/p:TargetFramework=net461", + "/p:TargetFramework=net471", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -66,7 +66,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Restore (net461)", + "label": "Restore (net471)", "type": "shell", "command": "nuget", "args": [ 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 ccd69c574d..1221584a2b 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 @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 9c9589f398..6e0cf6be2e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -116,8 +116,8 @@ namespace osu.Game.Tests.Beatmaps.Formats // [TestCase(with_sb)] public void TestParity(string beatmap) { - var beatmaps = decode(beatmap); - beatmaps.jsonDecoded.ShouldDeepEqual(beatmaps.legacyDecoded); + var legacy = decode(beatmap, out Beatmap json); + json.ShouldDeepEqual(legacy); } /// @@ -126,15 +126,20 @@ namespace osu.Game.Tests.Beatmaps.Formats /// /// The .osu file to decode. /// The after being decoded by an . - private Beatmap decodeAsJson(string filename) => decode(filename).jsonDecoded; + private Beatmap decodeAsJson(string filename) + { + decode(filename, out Beatmap jsonDecoded); + return jsonDecoded; + } /// /// Reads a .osu file first with a , serializes the resulting to JSON /// and then deserializes the result back into a through an . /// /// The .osu file to decode. + /// The after being decoded by an . /// The after being decoded by an . - private (Beatmap legacyDecoded, Beatmap jsonDecoded) decode(string filename) + private Beatmap decode(string filename, out Beatmap jsonDecoded) { using (var stream = Resource.OpenResource(filename)) using (var sr = new StreamReader(stream)) @@ -149,7 +154,9 @@ namespace osu.Game.Tests.Beatmaps.Formats sw.Flush(); ms.Position = 0; - return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2)); + + jsonDecoded = new JsonBeatmapDecoder().Decode(sr2); + return legacyDecoded; } } } diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 2646e953cf..057c2c2de1 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net461 + netcoreapp2.0;net471 From 0b993561d807a74969e5f76df9afddb91d4e0391 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 16:05:34 +0900 Subject: [PATCH 509/537] Fix BadgeContainer being unsable to handle null badges This fixes a failing test (hidden becaues the test wasn't being run). - [ ] Merge osu-framework#1530 first. --- .../Overlays/Profile/Header/BadgeContainer.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index 291db45e97..36a9a9b01a 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -92,22 +92,18 @@ namespace osu.Game.Overlays.Profile.Header public void ShowBadges(Badge[] badges) { - switch (badges.Length) + if (badges == null || badges.Length == 0) { - case 0: - Hide(); - return; - case 1: - badgeCountText.Hide(); - break; - default: - badgeCountText.Show(); - badgeCountText.Text = $"{badges.Length} badges"; - break; + Hide(); + return; } - Show(); badgeCount = badges.Length; + + badgeCountText.FadeTo(badgeCount > 1 ? 1 : 0); + badgeCountText.Text = $"{badges.Length} badges"; + + Show(); visibleBadge = 0; badgeFlowContainer.Clear(); From 8bf25542cbdff7e9aed769ed7ded18f1991fde15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 17:30:27 +0900 Subject: [PATCH 510/537] Add PlayerLoader TestCase and fix dummy beatmap load procedure --- osu.Game.Tests/Visual/TestCasePlayerLoader.cs | 24 +++++++++++++++++++ osu.Game/Screens/Play/Player.cs | 16 ++++++------- osu.Game/Screens/Play/PlayerLoader.cs | 5 +++- 3 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCasePlayerLoader.cs diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs new file mode 100644 index 0000000000..1e7618232d --- /dev/null +++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + public class TestCasePlayerLoader : OsuTestCase + { + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + AddStep("load dummy beatmap", () => Add(new PlayerLoader(new Player + { + InitialBeatmap = new DummyWorkingBeatmap(game), + AllowPause = false, + AllowLeadIn = false, + AllowResults = false, + }))); + } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ec7c1a1009..83958b2912 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play private DrawableStoryboard storyboard; private Container storyboardContainer; - private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true; + public bool LoadedBeatmapSuccessfully => RulesetContainer?.Objects.Any() == true; [BackgroundDependencyLoader] private void load(AudioManager audio, APIAccess api, OsuConfigManager config) @@ -86,10 +86,7 @@ namespace osu.Game.Screens.Play WorkingBeatmap working = Beatmap.Value; if (working is DummyWorkingBeatmap) - { - Exit(); return; - } sampleRestart = audio.Sample.Get(@"Gameplay/restart"); @@ -122,14 +119,15 @@ namespace osu.Game.Screens.Play } if (!RulesetContainer.Objects.Any()) - throw new InvalidOperationException("Beatmap contains no hit objects!"); + { + Logger.Error(new InvalidOperationException("Beatmap contains no hit objects!"), "Beatmap contains no hit objects!"); + return; + } } catch (Exception e) { Logger.Error(e, "Could not load beatmap sucessfully!"); - //couldn't load, hard abort! - Exit(); return; } @@ -293,7 +291,7 @@ namespace osu.Game.Screens.Play { base.OnEntering(last); - if (!loadedSuccessfully) + if (!LoadedBeatmapSuccessfully) return; Content.Alpha = 0; @@ -343,7 +341,7 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } - if (loadedSuccessfully) + if (LoadedBeatmapSuccessfully) pauseContainer?.Pause(); return true; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index c31c64a95d..56fbd7b6e7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -163,7 +163,10 @@ namespace osu.Game.Screens.Play //Note that this may change if the player we load requested a re-run. ValidForResume = false; - Push(player); + if (player.LoadedBeatmapSuccessfully) + Push(player); + else + Exit(); }); }, 500); } From 0dce7a5b614b49d14cb2fca6995da8ebf5012292 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Apr 2018 18:19:17 +0900 Subject: [PATCH 511/537] Update framework + fix CI errors --- osu-framework | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 2 +- .../Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu-framework b/osu-framework index 02d7a0fa47..f1751c27ff 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 02d7a0fa4798d197cd08570ee48951edbb7c7860 +Subproject commit f1751c27ffe2c5febece74129368596b5ad3a4e2 diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index f9d21ea5bb..10539f85a2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, - Children = new Drawable[] + Children = new[] { Background = new SpinnerBackground { diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index ede98d986a..b9a8e9914a 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers var dragLayer = new DragLayer(maskContainer.Select); dragLayer.DragEnd += () => maskSelection.UpdateVisibility(); - InternalChildren = new Drawable[] + InternalChildren = new[] { dragLayer, maskSelection, From b16e25c3e98b1d4cea1f88a532578cc36debf79c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 18:32:24 +0900 Subject: [PATCH 512/537] Add error handling on a per-line level in LegacyDecoder Resolves #2306. --- osu.Game/Beatmaps/Formats/Decoder.cs | 2 +- osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs | 8 ++++---- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 13 ++++++++++--- osu.Game/Skinning/LegacySkinDecoder.cs | 8 ++++---- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index bcd11f1fc8..2927654f62 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -21,7 +21,7 @@ namespace osu.Game.Beatmaps.Formats return output; } - protected abstract void ParseStreamInto(StreamReader stream, TOutput beatmap); + protected abstract void ParseStreamInto(StreamReader stream, TOutput output); } public abstract class Decoder diff --git a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs index e5574cd82e..fba89b8ac1 100644 --- a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs @@ -13,15 +13,15 @@ namespace osu.Game.Beatmaps.Formats AddDecoder("{", m => new JsonBeatmapDecoder()); } - protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) + protected override void ParseStreamInto(StreamReader stream, Beatmap output) { stream.BaseStream.Position = 0; stream.DiscardBufferedData(); - stream.ReadToEnd().DeserializeInto(beatmap); + stream.ReadToEnd().DeserializeInto(output); - foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + foreach (var hitObject in output.HitObjects) + hitObject.ApplyDefaults(output.ControlPointInfo, output.BeatmapInfo.BaseDifficulty); } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 06160a87e0..e77efd8508 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps.Formats FormatVersion = version; } - protected override void ParseStreamInto(StreamReader stream, T beatmap) + protected override void ParseStreamInto(StreamReader stream, T output) { Section section = Section.None; @@ -33,14 +33,21 @@ namespace osu.Game.Beatmaps.Formats { if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) { - Logger.Log($"Unknown section \"{line}\" in {beatmap}"); + Logger.Log($"Unknown section \"{line}\" in {output}"); section = Section.None; } continue; } - ParseLine(beatmap, section, line); + try + { + ParseLine(output, section, line); + } + catch (Exception e) + { + Logger.Error(e, $"Failed to process line \"{line}\" into {output}"); + } } } diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 995518af0a..0ef54c7310 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -12,7 +12,7 @@ namespace osu.Game.Skinning { } - protected override void ParseLine(SkinConfiguration output, Section section, string line) + protected override void ParseLine(SkinConfiguration skin, Section section, string line) { switch (section) { @@ -22,17 +22,17 @@ namespace osu.Game.Skinning switch (pair.Key) { case @"Name": - output.SkinInfo.Name = pair.Value; + skin.SkinInfo.Name = pair.Value; break; case @"Author": - output.SkinInfo.Creator = pair.Value; + skin.SkinInfo.Creator = pair.Value; break; } break; } - base.ParseLine(output, section, line); + base.ParseLine(skin, section, line); } } } From 19e270062f464b2735f1bd59f7688e85bee99507 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 19:12:47 +0900 Subject: [PATCH 513/537] Only build on correct branch --- appveyor.yml | 1 - appveyor_deploy.yml | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 4c4b70827f..bf90cfd200 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,3 @@ -# 2017-09-14 clone_depth: 1 version: '{branch}-{build}' image: Visual Studio 2017 diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml index cd241eb88f..422aaa55b6 100644 --- a/appveyor_deploy.yml +++ b/appveyor_deploy.yml @@ -1,4 +1,8 @@ -# 2017-09-14 +branches: + only: + - release +skip_tags: true +skip_branch_with_pr: true clone_depth: 1 version: '{branch}-{build}' image: Visual Studio 2017 From 1cad6f7a8ec1149da16277e02ea96c837a558c19 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 19:17:11 +0900 Subject: [PATCH 514/537] Add support for updating AppVeyor version --- osu.Desktop.Deploy/Program.cs | 21 ++++++++++++++++++++ osu.Desktop.Deploy/osu.Desktop.Deploy.csproj | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 8c460f03cf..16bbf90cd4 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -7,6 +7,7 @@ using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; +using System.Management.Automation; using Newtonsoft.Json; using osu.Framework.IO.Network; using FileWebRequest = osu.Framework.IO.Network.FileWebRequest; @@ -99,6 +100,7 @@ namespace osu.Desktop.Deploy write("Updating AssemblyInfo..."); updateCsprojVersion(version); + updateAppveyorVersion(version); write("Running build process..."); foreach (string targetName in TargetNames.Split(',')) @@ -404,6 +406,25 @@ namespace osu.Desktop.Deploy Console.WriteLine(); } + private static bool updateAppveyorVersion(string version) + { + try + { + using (PowerShell ps = PowerShell.Create()) + { + ps.AddScript($"Update-AppveyorBuild -Version \"{version}\""); + ps.Invoke(); + } + return true; + } + catch + { + // we don't have appveyor and don't care + } + + return false; + } + private static void write(string message, ConsoleColor col = ConsoleColor.Gray) { if (sw.ElapsedMilliseconds > 0) diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index 6b7509a381..96dcf2a0d0 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -1,4 +1,4 @@ - + net461 @@ -14,5 +14,6 @@ + \ No newline at end of file From 7dd07503b24f08bfca01f7daba28d8f8043414b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Apr 2018 21:08:41 +0900 Subject: [PATCH 515/537] Fix wrong targets on ruleset test configuration --- .../.idea/runConfigurations/RulesetTests__catch_.xml | 2 +- .../.idea/runConfigurations/RulesetTests__mania_.xml | 2 +- .../.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml | 4 ++-- .../.idea/runConfigurations/RulesetTests__taiko_.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml index f8003ae339..be69e92748 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml @@ -1,6 +1,6 @@ -