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..be69e92748
--- /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..da968b54b7
--- /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..f2b8155e37
--- /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..941455f8b4
--- /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 @@
-
-
+
+
@@ -12,7 +12,7 @@
-
+
\ 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 @@
-
-
+
+
@@ -12,7 +12,7 @@
-
+
\ 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..15484e4c68 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,27 +1,26 @@
-# 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
+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
new file mode 100644
index 0000000000..cadebd9d11
--- /dev/null
+++ b/appveyor_deploy.yml
@@ -0,0 +1,34 @@
+branches:
+ only:
+ - release
+skip_tags: true
+skip_branch_with_pr: true
+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\net471\osu.Desktop.Deploy.exe.config
+ - cd osu.Desktop.Deploy\bin\Debug\net471\
+ - osu.Desktop.Deploy.exe %code_signing_password%
+environment:
+ TargetFramework: net471
+ decode_secret:
+ secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
+ code_signing_password:
+ secure: 34tLNqvjmmZEi97MLKfrnQ==
+artifacts:
+ - path: 'Releases\*'
\ No newline at end of file
diff --git a/osu-framework b/osu-framework
index 16e6a453db..0773d895d9 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a
+Subproject commit 0773d895d9aa0729995cd4a23efc28238e35ceed
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/Program.cs b/osu.Desktop.Deploy/Program.cs
index 6095ce062d..a1c2a8aef2 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;
@@ -18,7 +19,7 @@ namespace osu.Desktop.Deploy
{
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 static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.8.0\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"];
@@ -57,8 +58,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,19 +87,20 @@ 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...");
updateCsprojVersion(version);
+ updateAppveyorVersion(version);
write("Running build process...");
foreach (string targetName in TargetNames.Split(','))
@@ -109,7 +115,7 @@ namespace osu.Desktop.Deploy
checkReleaseFiles();
write("Running squirrel build...");
- runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi");
+ runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --framework-version=net471 --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi");
//prune again to clean up before upload.
pruneReleases();
@@ -124,7 +130,7 @@ namespace osu.Desktop.Deploy
updateCsprojVersion("0.0.0");
write("Done!", ConsoleColor.White);
- Console.ReadLine();
+ pauseIfInteractive();
}
private static void displayHeader()
@@ -388,10 +394,37 @@ 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 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..063fb89918 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,8 @@
-
+
+
\ No newline at end of file
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 2ad7b67842..3d64cab84e 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
@@ -10,11 +10,11 @@
osu!lazer
osu!lazer
lazer.ico
- 0.0.0.0
- 0.0.0.0
+ 0.0.0
+ 0.0.0
- $(DefineConstants);NET_FRAMEWORK
+ $(DefineConstants);NET_FRAMEWORK
osu.Desktop.Program
@@ -31,9 +31,9 @@
-
+
-
\ 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
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.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs
index 3518e030b6..6213bb1329 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/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.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index 8330aa8e9d..b63f85623e 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);
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/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
index 790c4cedc3..886c1120d4 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
@@ -52,6 +52,9 @@ namespace osu.Game.Tests.Visual
infoWedge.UpdateBeatmap(beatmap);
});
+ // select part is redundant, but wait for load isn't
+ selectBeatmap(beatmap.Value.Beatmap);
+
AddWaitStep(3);
AddStep("hide", () => { infoWedge.State = Visibility.Hidden; });
@@ -63,10 +66,11 @@ namespace osu.Game.Tests.Visual
foreach (var rulesetInfo in rulesets.AvailableRulesets)
{
var ruleset = rulesetInfo.CreateInstance();
- beatmaps.Add(createTestBeatmap(rulesetInfo));
+ var testBeatmap = createTestBeatmap(rulesetInfo);
- var name = rulesetInfo.ShortName;
- selectBeatmap(name);
+ beatmaps.Add(testBeatmap);
+
+ selectBeatmap(testBeatmap);
// TODO: adjust cases once more info is shown for other gamemodes
switch (ruleset)
@@ -108,14 +112,14 @@ namespace osu.Game.Tests.Visual
AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
}
- private void selectBeatmap(string name)
+ private void selectBeatmap(Beatmap b)
{
- var infoBefore = infoWedge.Info;
+ BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null;
- AddStep($"select {name} beatmap", () =>
+ AddStep($"select {b.Metadata.Title} beatmap", () =>
{
- beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name));
- infoWedge.UpdateBeatmap(beatmap);
+ infoBefore = infoWedge.Info;
+ infoWedge.UpdateBeatmap(beatmap.Value = new TestWorkingBeatmap(b));
});
AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load");
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
index 6cb6a342a8..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
{
@@ -47,11 +49,11 @@ 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));
+ AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapSetID = 1, OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
+
scores = new[]
{
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.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.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
index 0fdc01a974..aca832110a 100644
--- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
@@ -5,7 +5,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Overlays.Profile;
using osu.Game.Overlays.Profile.Header;
@@ -17,6 +19,7 @@ namespace osu.Game.Tests.Visual
public class TestCaseUserProfile : OsuTestCase
{
private readonly TestUserProfileOverlay profile;
+ private APIAccess api;
public override IReadOnlyList RequiredTypes => new[]
{
@@ -32,6 +35,12 @@ namespace osu.Game.Tests.Visual
Add(profile = new TestUserProfileOverlay());
}
+ [BackgroundDependencyLoader]
+ private void load(APIAccess api)
+ {
+ this.api = api;
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
@@ -79,9 +88,10 @@ namespace osu.Game.Tests.Visual
{
Username = @"peppy",
Id = 2,
+ IsSupporter = true,
Country = new Country { FullName = @"Australia", FlagName = @"AU" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
- }));
+ }, api.IsLoggedIn));
checkSupporterTag(true);
@@ -91,7 +101,7 @@ namespace osu.Game.Tests.Visual
Id = 3103765,
Country = new Country { FullName = @"Japan", FlagName = @"JP" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
- }));
+ }, api.IsLoggedIn));
AddStep("Hide", profile.Hide);
AddStep("Show without reload", profile.Show);
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
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/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 366b4f163f..655355913c 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -373,17 +373,18 @@ namespace osu.Game.Beatmaps.Formats
if (parser == null)
parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
- var obj = parser.Parse(line);
+ var obj = parser.Parse(line, getOffsetTime());
if (obj != null)
{
- obj.StartTime = getOffsetTime(obj.StartTime);
beatmap.HitObjects.Add(obj);
}
}
private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0);
+ private double getOffsetTime() => 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 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/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 3efaa02a31..509622c2fe 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -60,6 +60,9 @@ namespace osu.Game.Configuration
Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuSetting.ShowStoryboard, true);
+ Set(OsuSetting.BeatmapSkins, true);
+ Set(OsuSetting.BeatmapHitsounds, true);
+
Set(OsuSetting.CursorRotation, true);
Set(OsuSetting.MenuParallax, true);
@@ -133,6 +136,8 @@ namespace osu.Game.Configuration
Skin,
ScreenshotFormat,
ScreenshotCaptureMenuCursor,
- SongSelectRightMouseScroll
+ SongSelectRightMouseScroll,
+ BeatmapSkins,
+ BeatmapHitsounds
}
}
diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
index 6121cd487d..51b9acff44 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 = () =>
@@ -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 (linkArgument != null && 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/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs
index d14080de5b..808ce159bb 100644
--- a/osu.Game/IO/Archives/ArchiveReader.cs
+++ b/osu.Game/IO/Archives/ArchiveReader.cs
@@ -1,14 +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 System.IO;
using osu.Framework.IO.Stores;
namespace osu.Game.IO.Archives
{
- public abstract class ArchiveReader : IDisposable, IResourceStore
+ public abstract class ArchiveReader : IResourceStore
{
///
/// Opens a stream for reading a specific file from this archive.
diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs
index b031791900..37dd77af46 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/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs
index 9026d10334..607e8e5127 100644
--- a/osu.Game/Online/API/Requests/GetUserRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserRequest.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Online.API.Requests
{
public class GetUserRequest : APIRequest
{
- private long? userId;
+ private readonly long? userId;
public GetUserRequest(long? userId = null)
{
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index a3a3d92d98..5cf2cf9b4a 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -152,7 +152,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.
@@ -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.FetchAndShowBeatmap(beatmapId);
+
protected void LoadScore(Score s)
{
scoreLoad?.Cancel();
@@ -189,6 +195,8 @@ namespace osu.Game
return;
}
+ Ruleset.Value = s.Ruleset;
+
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap);
Beatmap.Value.Mods.Value = s.Mods;
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index c3e3881a28..6d1b2dfda2 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -193,6 +193,20 @@ namespace osu.Game
FileStore.Cleanup();
AddInternal(api);
+
+ 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);
}
private void runMigrations()
@@ -222,20 +236,6 @@ namespace osu.Game
{
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);
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..096f7bb63c 100644
--- a/osu.Game/Overlays/BeatmapSetOverlay.cs
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -17,29 +17,45 @@ 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
{
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 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 +104,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)
{
@@ -130,7 +125,8 @@ namespace osu.Game.Overlays
{
base.PopOut();
header.Details.StopPreview();
- FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out);
+
+ FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null);
}
protected override bool OnClick(InputState state)
@@ -139,17 +135,31 @@ namespace osu.Game.Overlays
return true;
}
- public void ShowBeatmapSet(int beatmapSetId)
+ public void FetchAndShowBeatmap(int beatmapId)
{
- // 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(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);
+ Show();
+ }
+
+ public void FetchAndShowBeatmapSet(int beatmapSetId)
+ {
+ 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/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/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();
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index ec0e45d5ca..4c411b3210 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,
@@ -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.AddLink($@"{user.PostCount} forum posts", url: $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: 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_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);
if (user.Statistics != 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/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index 95abc4edb3..9edd0f1f34 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -19,6 +19,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
public abstract class ConvertHitObjectParser : HitObjectParser
{
public override HitObject Parse(string text)
+ {
+ return Parse(text, 0);
+ }
+
+ public HitObject Parse(string text, double offset)
{
try
{
@@ -146,7 +151,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
}
else if ((type & ConvertHitObjectType.Spinner) > 0)
{
- result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture));
+ result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + offset);
if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo);
@@ -164,13 +169,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
}
- result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime);
+ result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime + offset);
}
if (result == null)
throw new InvalidOperationException($@"Unknown hit object type {type}.");
- result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);
+ result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + offset;
result.Samples = convertSoundType(soundType, bankInfo);
return result;
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 2365582645..35a91275a7 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,24 +23,45 @@ 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;
+ // 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)
+ {
+ // 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)(hitObjectLength * length.Y);
+ break;
+ case ScrollingDirection.Left:
+ case ScrollingDirection.Right:
+ obj.Width = (float)(hitObjectLength * length.X);
+ break;
+ }
+ }
if (obj.HasNestedHitObjects)
{
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
- ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
+
+ // Nested hitobjects don't need to scroll, but they do need accurate positions
+ 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)
{
- var controlPoint = controlPointAt(obj.HitObject.StartTime);
-
- var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange;
+ var position = hitObjectPositionAt(obj, currentTime, timeRange);
switch (direction)
{
@@ -59,7 +81,28 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
}
}
+ ///
+ /// 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) / timeRange * controlPointAt(obj.HitObject.StartTime).Multiplier;
+
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)
diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs
index 708a2f173b..e353c07e9f 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,12 +51,14 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
if (obj.HasNestedHitObjects)
{
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
- ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
+
+ // Nested hitobjects don't need to scroll, but they do need accurate positions
+ 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);
@@ -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;
diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs
index e7d3690f59..5e9863f642 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 ea1d85bb5b..adb749b492 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -178,7 +178,7 @@ namespace osu.Game.Screens.Edit
}
currentScreen.Beatmap.BindTo(Beatmap);
- screenContainer.Add(currentScreen);
+ LoadComponentAsync(currentScreen, screenContainer.Add);
}
protected override bool OnWheel(InputState state)
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,
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs
index 6d75b8dc15..df2691c28e 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs
@@ -43,6 +43,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
public override void Add(HitObjectMask drawable)
{
+ if (drawable == null) throw new ArgumentNullException(nameof(drawable));
+
base.Add(drawable);
drawable.Selected += onMaskSelected;
@@ -53,6 +55,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
public override bool Remove(HitObjectMask drawable)
{
+ if (drawable == null) throw new ArgumentNullException(nameof(drawable));
+
var result = base.Remove(drawable);
if (result)
diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs
index c62a622c3c..4de76e530a 100644
--- a/osu.Game/Screens/Menu/Intro.cs
+++ b/osu.Game/Screens/Menu/Intro.cs
@@ -39,6 +39,7 @@ namespace osu.Game.Screens.Menu
private Bindable menuVoice;
private Bindable menuMusic;
private Track track;
+ private WorkingBeatmap beatmap;
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
@@ -69,9 +70,8 @@ namespace osu.Game.Screens.Menu
}
}
- Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
-
- track = Beatmap.Value.Track;
+ beatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
+ track = beatmap.Track;
welcome = audio.Sample.Get(@"welcome");
seeya = audio.Sample.Get(@"seeya");
@@ -81,6 +81,8 @@ namespace osu.Game.Screens.Menu
{
base.OnEntering(last);
+ Game.Beatmap.Value = beatmap;
+
if (menuVoice)
welcome.Play();
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 ca22b274a4..56fbd7b6e7 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -159,14 +159,14 @@ namespace osu.Game.Screens.Play
loadTask = null;
- if (!Push(player))
- Exit();
+ //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 (player.LoadedBeatmapSuccessfully)
+ Push(player);
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;
- }
+ Exit();
});
}, 500);
}
diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
index 92d069a224..439e344020 100644
--- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
+++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
@@ -15,6 +15,8 @@ namespace osu.Game.Screens.Play.PlayerSettings
private readonly PlayerSliderBar dimSliderBar;
private readonly PlayerSliderBar blurSliderBar;
private readonly PlayerCheckbox showStoryboardToggle;
+ private readonly PlayerCheckbox beatmapSkinsToggle;
+ private readonly PlayerCheckbox beatmapHitsoundsToggle;
public VisualSettings()
{
@@ -34,7 +36,9 @@ namespace osu.Game.Screens.Play.PlayerSettings
{
Text = "Toggles:"
},
- showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" }
+ showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" },
+ beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" },
+ beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }
};
}
@@ -44,6 +48,8 @@ namespace osu.Game.Screens.Play.PlayerSettings
dimSliderBar.Bindable = config.GetBindable(OsuSetting.DimLevel);
blurSliderBar.Bindable = config.GetBindable(OsuSetting.BlurLevel);
showStoryboardToggle.Bindable = config.GetBindable(OsuSetting.ShowStoryboard);
+ beatmapSkinsToggle.Bindable = config.GetBindable(OsuSetting.BeatmapSkins);
+ beatmapHitsoundsToggle.Bindable = config.GetBindable(OsuSetting.BeatmapHitsounds);
}
}
}
diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs
index 9d92439a4b..42d8af07b9 100644
--- a/osu.Game/Screens/Ranking/ResultsPageScore.cs
+++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs
@@ -223,11 +223,11 @@ namespace osu.Game.Screens.Ranking
private class DateTimeDisplay : Container
{
- private DateTime datetime;
+ private readonly DateTime date;
- public DateTimeDisplay(DateTime datetime)
+ public DateTimeDisplay(DateTime date)
{
- this.datetime = datetime;
+ this.date = date;
AutoSizeAxes = Axes.Y;
@@ -251,7 +251,7 @@ namespace osu.Game.Screens.Ranking
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
- Text = datetime.ToShortDateString(),
+ Text = date.ToShortDateString(),
Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Colour = Color4.White,
},
@@ -259,7 +259,7 @@ namespace osu.Game.Screens.Ranking
{
Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight,
- Text = datetime.ToShortTimeString(),
+ Text = date.ToShortTimeString(),
Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Colour = Color4.White,
}
diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
index 7ea6e7c82b..2118cfdc78 100644
--- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
+++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
@@ -16,11 +16,22 @@ namespace osu.Game.Screens.Select.Carousel
protected List InternalChildren = new List();
+ ///
+ /// Used to assign a monotonically increasing ID to children as they are added. This member is
+ /// incremented whenever a child is added.
+ ///
+ private ulong currentChildID;
+
public override List Drawables
{
get
{
var drawables = base.Drawables;
+
+ // if we are explicitly not present, don't ever present children.
+ // without this check, children drawables can potentially be presented without their group header.
+ if (DrawableRepresentation.Value?.IsPresent == false) return drawables;
+
foreach (var c in InternalChildren)
drawables.AddRange(c.Drawables);
return drawables;
@@ -39,6 +50,7 @@ namespace osu.Game.Screens.Select.Carousel
public virtual void AddChild(CarouselItem i)
{
i.State.ValueChanged += v => ChildItemStateChanged(i, v);
+ i.ChildID = ++currentChildID;
InternalChildren.Add(i);
}
diff --git a/osu.Game/Screens/Select/Carousel/CarouselItem.cs b/osu.Game/Screens/Select/Carousel/CarouselItem.cs
index 2efc928984..c0781f09c0 100644
--- a/osu.Game/Screens/Select/Carousel/CarouselItem.cs
+++ b/osu.Game/Screens/Select/Carousel/CarouselItem.cs
@@ -24,16 +24,18 @@ namespace osu.Game.Screens.Select.Carousel
{
var items = new List();
- var self = drawableRepresentation.Value;
+ var self = DrawableRepresentation.Value;
if (self?.IsPresent == true) items.Add(self);
return items;
}
}
+ private int creationOrder;
+
protected CarouselItem()
{
- drawableRepresentation = new Lazy(CreateDrawableRepresentation);
+ DrawableRepresentation = new Lazy(CreateDrawableRepresentation);
Filtered.ValueChanged += v =>
{
@@ -42,15 +44,23 @@ namespace osu.Game.Screens.Select.Carousel
};
}
- private readonly Lazy drawableRepresentation;
+ protected readonly Lazy DrawableRepresentation;
+ ///
+ /// Used as a default sort method for s of differing types.
+ ///
+ internal ulong ChildID;
+
+ ///
+ /// Create a fresh drawable version of this item. If you wish to consume the current representation, use instead.
+ ///
protected abstract DrawableCarouselItem CreateDrawableRepresentation();
public virtual void Filter(FilterCriteria criteria)
{
}
- public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => GetHashCode().CompareTo(other.GetHashCode());
+ public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => ChildID.CompareTo(other.ChildID);
}
public enum CarouselItemState
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
index cda8a549da..f39952dc31 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?.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/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs
index 0a05137a87..45c6ec80aa 100644
--- a/osu.Game/Skinning/LegacySkin.cs
+++ b/osu.Game/Skinning/LegacySkin.cs
@@ -113,6 +113,31 @@ namespace osu.Game.Skinning
string path = getPathForFile(name);
return path == null ? null : underlyingStore.Get(path);
}
+
+ #region IDisposable Support
+
+ private bool isDisposed;
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!isDisposed)
+ {
+ isDisposed = true;
+ }
+ }
+
+ ~LegacySkinResourceStore()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
}
}
}
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);
}
}
}
diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs
index bfbb1719df..ad6a033936 100644
--- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs
+++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs
@@ -4,9 +4,11 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
+using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
+using osu.Game.Configuration;
namespace osu.Game.Skinning
{
@@ -14,17 +16,37 @@ namespace osu.Game.Skinning
{
public event Action SourceChanged;
- public Drawable GetDrawableComponent(string componentName) => source.GetDrawableComponent(componentName) ?? fallbackSource?.GetDrawableComponent(componentName);
+ private Bindable beatmapSkins = new Bindable();
+ private Bindable beatmapHitsounds = new Bindable();
- public Texture GetTexture(string componentName) => source.GetTexture(componentName) ?? fallbackSource.GetTexture(componentName);
+ public Drawable GetDrawableComponent(string componentName)
+ {
+ Drawable sourceDrawable;
+ if (beatmapSkins && (sourceDrawable = source.GetDrawableComponent(componentName)) != null)
+ return sourceDrawable;
+ return fallbackSource?.GetDrawableComponent(componentName);
+ }
- public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName);
+ public Texture GetTexture(string componentName)
+ {
+ Texture sourceTexture;
+ if (beatmapSkins && (sourceTexture = source.GetTexture(componentName)) != null)
+ return sourceTexture;
+ return fallbackSource.GetTexture(componentName);
+ }
+
+ public SampleChannel GetSample(string sampleName)
+ {
+ SampleChannel sourceChannel;
+ if (beatmapHitsounds && (sourceChannel = source.GetSample(sampleName)) != null)
+ return sourceChannel;
+ return fallbackSource?.GetSample(sampleName);
+ }
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)
+ if ((source as Skin)?.Configuration is TConfiguration conf)
val = query?.Invoke(conf);
return val ?? fallbackSource?.GetValue(query);
@@ -33,8 +55,7 @@ namespace osu.Game.Skinning
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)
+ if ((source as Skin)?.Configuration is TConfiguration conf)
val = query?.Invoke(conf);
return val ?? fallbackSource?.GetValue(query);
@@ -60,6 +81,18 @@ namespace osu.Game.Skinning
return dependencies;
}
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ beatmapSkins = config.GetBindable(OsuSetting.BeatmapSkins);
+ beatmapSkins.ValueChanged += val => onSourceChanged();
+ beatmapSkins.TriggerChange();
+
+ beatmapHitsounds = config.GetBindable(OsuSetting.BeatmapHitsounds);
+ beatmapHitsounds.ValueChanged += val => onSourceChanged();
+ beatmapHitsounds.TriggerChange();
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs
index 02f425c296..2b677f1f42 100644
--- a/osu.Game/Tests/Visual/OsuTestCase.cs
+++ b/osu.Game/Tests/Visual/OsuTestCase.cs
@@ -15,18 +15,14 @@ namespace osu.Game.Tests.Visual
{
protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location;
- private readonly TestCaseTestRunner.TestRunner runner;
+ private TestCaseTestRunner.TestRunner runner;
- public OsuTestCaseTestRunner()
+ protected override void LoadAsyncComplete()
{
- runner = new TestCaseTestRunner.TestRunner();
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- Add(runner);
+ // this has to be run here rather than LoadComplete because
+ // TestCase.cs is checking the IsLoaded state (on another thread) and expects
+ // the runner to be loaded at that point.
+ Add(runner = new TestCaseTestRunner.TestRunner());
}
public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test);
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),
+ })
+ );
+ }
}
}
}
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;
diff --git a/osu.Game/Users/UserStatus.cs b/osu.Game/Users/UserStatus.cs
index 22bc9ed1a0..1584605166 100644
--- a/osu.Game/Users/UserStatus.cs
+++ b/osu.Game/Users/UserStatus.cs
@@ -12,12 +12,13 @@ namespace osu.Game.Users
public abstract Color4 GetAppropriateColour(OsuColour colours);
}
- public abstract class UserStatusAvailable : UserStatus
+ public class UserStatusOnline : UserStatus
{
+ public override string Message => @"Online";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.BlueDarker;
}
- public abstract class UserStatusBusy : UserStatus
+ public abstract class UserStatusBusy : UserStatusOnline
{
public override Color4 GetAppropriateColour(OsuColour colours) => colours.YellowDark;
}
@@ -28,17 +29,12 @@ namespace osu.Game.Users
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray7;
}
- public class UserStatusOnline : UserStatusAvailable
- {
- public override string Message => @"Online";
- }
-
- public class UserStatusSpectating : UserStatusAvailable
+ public class UserStatusSpectating : UserStatusOnline
{
public override string Message => @"Spectating a game";
}
- public class UserStatusInLobby : UserStatusAvailable
+ public class UserStatusInLobby : UserStatusOnline
{
public override string Message => @"in Multiplayer Lobby";
}
@@ -53,13 +49,13 @@ namespace osu.Game.Users
public override string Message => @"Multiplaying";
}
- public class UserStatusModding : UserStatus
+ public class UserStatusModding : UserStatusOnline
{
public override string Message => @"Modding a map";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark;
}
- public class UserStatusDoNotDisturb : UserStatus
+ public class UserStatusDoNotDisturb : UserStatusBusy
{
public override string Message => @"Do not disturb";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark;