diff --git a/.gitmodules b/.gitmodules
index ee1cc80880..f1c4f5d172 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "osu-framework"]
- path = osu-framework
- url = https://github.com/ppy/osu-framework
[submodule "osu-resources"]
path = osu-resources
- url = https://github.com/ppy/osu-resources
+ url = https://github.com/ppy/osu-resources
\ No newline at end of file
diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_1_.xml
similarity index 82%
rename from .idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml
rename to .idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_1_.xml
index 08b4e38667..2d3a848922 100644
--- a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml
+++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_1_.xml
@@ -1,6 +1,6 @@
-
-
+
+
@@ -12,7 +12,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml b/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_1_.xml
similarity index 82%
rename from .idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml
rename to .idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_1_.xml
index 2f5c137631..36efe211c6 100644
--- a/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml
+++ b/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_1_.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 df5b11f63a..b9bb75d5bb 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -22,7 +22,7 @@
},
"type": "mono",
"request": "launch",
- "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe",
+ "program": "${workspaceRoot}/osu.Game.Tests/bin/Release/net471/osu.Game.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
@@ -58,54 +58,54 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Debug, netcoreapp2.0)",
+ "name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.0/osu.Game.Tests.dll"
+ "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, dotnet)",
+ "preLaunchTask": "Build tests (Debug, dotnet)",
"env": {},
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.0)",
+ "name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.0/osu.Game.Tests.dll"
+ "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.1/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, dotnet)",
+ "preLaunchTask": "Build tests (Release, dotnet)",
"env": {},
"console": "internalConsole"
},
{
- "name": "osu! (Debug, netcoreapp2.0)",
+ "name": "osu! (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll",
+ "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll",
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug, dotnet)",
+ "preLaunchTask": "Build osu! (Debug, dotnet)",
"env": {},
"console": "internalConsole"
},
{
- "name": "osu! (Release, netcoreapp2.0)",
+ "name": "osu! (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll",
+ "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll",
],
"cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release, dotnet)",
+ "preLaunchTask": "Build osu! (Release, dotnet)",
"env": {},
"console": "internalConsole"
}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 7144a584f3..bebad750ca 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -31,14 +31,14 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Build (Debug, dotnet)",
+ "label": "Build osu! (Debug, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Desktop",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -47,14 +47,47 @@
"problemMatcher": "$msCompile"
},
{
- "label": "Build (Release, dotnet)",
+ "label": "Build osu! (Release, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Desktop",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
+ "/p:Configuration=Release",
+ "/p:GenerateFullPaths=true",
+ "/m",
+ "/verbosity:m"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Build tests (Debug, dotnet)",
+ "type": "shell",
+ "command": "dotnet",
+ "args": [
+ "build",
+ "--no-restore",
+ "osu.Game.Tests",
+ "/p:TargetFramework=netcoreapp2.1",
+ "/p:GenerateFullPaths=true",
+ "/m",
+ "/verbosity:m"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Build tests (Release, dotnet)",
+ "type": "shell",
+ "command": "dotnet",
+ "args": [
+ "build",
+ "--no-restore",
+ "osu.Game.Tests",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -73,7 +106,7 @@
"problemMatcher": []
},
{
- "label": "Restore (netcoreapp2.0)",
+ "label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"args": [
diff --git a/appveyor.yml b/appveyor.yml
index 69bc762f4c..ac6d6ebff8 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,11 +2,6 @@ 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
diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml
index cadebd9d11..0247714cdf 100644
--- a/appveyor_deploy.yml
+++ b/appveyor_deploy.yml
@@ -1,29 +1,24 @@
-branches:
- only:
- - release
-skip_tags: true
-skip_branch_with_pr: true
clone_depth: 1
-version: '{branch}-{build}'
+version: '{build}'
+skip_non_tags: true
image: Visual Studio 2017
-configuration: Debug
-cache:
- - packages -> **\packages.config
install:
- - cmd: git submodule update --init --recursive --depth=5
+ - git clone https://github.com/ppy/osu-deploy
before_build:
+ - ps: if($env:appveyor_repo_tag -eq 'True') { Update-AppveyorBuild -Version $env:appveyor_repo_tag_name }
+ - cmd: git submodule update --init --recursive --depth=5
- cmd: nuget restore -verbosity quiet
-build:
- project: osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
- verbosity: minimal
-after_build:
+build_script:
- 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%
+ - cd osu-deploy
+ - nuget restore -verbosity quiet
+ - msbuild osu.Desktop.Deploy.csproj
+ - cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\net471\osu.Desktop.Deploy.exe.config
+ - cd bin\Debug\net471\
+ - osu.Desktop.Deploy.exe %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
environment:
TargetFramework: net471
decode_secret:
@@ -31,4 +26,7 @@ environment:
code_signing_password:
secure: 34tLNqvjmmZEi97MLKfrnQ==
artifacts:
- - path: 'Releases\*'
\ No newline at end of file
+ - path: 'Releases\*'
+deploy:
+ - provider: Environment
+ name: github
\ No newline at end of file
diff --git a/osu-framework b/osu-framework
deleted file mode 160000
index fac688633b..0000000000
--- a/osu-framework
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit fac688633b8fcf34ae5d0514c26b03e217161eb4
diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config
deleted file mode 100644
index 9ec53d5a31..0000000000
--- a/osu.Desktop.Deploy/App.config
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/osu.Desktop.Deploy/GitHubObject.cs b/osu.Desktop.Deploy/GitHubObject.cs
deleted file mode 100644
index 02ff16fa69..0000000000
--- a/osu.Desktop.Deploy/GitHubObject.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using Newtonsoft.Json;
-
-namespace osu.Desktop.Deploy
-{
- public class GitHubObject
- {
- [JsonProperty(@"id")]
- public int Id;
-
- [JsonProperty(@"name")]
- public string Name;
- }
-}
diff --git a/osu.Desktop.Deploy/GitHubRelease.cs b/osu.Desktop.Deploy/GitHubRelease.cs
deleted file mode 100644
index faf312d4f7..0000000000
--- a/osu.Desktop.Deploy/GitHubRelease.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using Newtonsoft.Json;
-
-namespace osu.Desktop.Deploy
-{
- public class GitHubRelease
- {
- [JsonProperty(@"id")]
- public int Id;
-
- [JsonProperty(@"tag_name")]
- public string TagName => $"v{Name}";
-
- [JsonProperty(@"name")]
- public string Name;
-
- [JsonProperty(@"draft")]
- public bool Draft;
-
- [JsonProperty(@"prerelease")]
- public bool PreRelease;
-
- [JsonProperty(@"upload_url")]
- public string UploadUrl;
- }
-}
diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs
deleted file mode 100644
index a1c2a8aef2..0000000000
--- a/osu.Desktop.Deploy/Program.cs
+++ /dev/null
@@ -1,471 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-using System.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;
-using WebRequest = osu.Framework.IO.Network.WebRequest;
-
-namespace osu.Desktop.Deploy
-{
- internal static class Program
- {
- private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
- private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe");
- private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.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"];
- public static string ReleasesFolder = ConfigurationManager.AppSettings["ReleasesFolder"];
- public static string GitHubAccessToken = ConfigurationManager.AppSettings["GitHubAccessToken"];
- public static string GitHubUsername = ConfigurationManager.AppSettings["GitHubUsername"];
- public static string GitHubRepoName = ConfigurationManager.AppSettings["GitHubRepoName"];
- public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"];
- public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"];
- public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"];
- public static string TargetNames = ConfigurationManager.AppSettings["TargetName"];
- public static string PackageName = ConfigurationManager.AppSettings["PackageName"];
- public static string IconName = ConfigurationManager.AppSettings["IconName"];
- public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"];
-
- public static string GitHubApiEndpoint => $"https://api.github.com/repos/{GitHubUsername}/{GitHubRepoName}/releases";
- public static string GitHubReleasePage => $"https://github.com/{GitHubUsername}/{GitHubRepoName}/releases";
-
- ///
- /// How many previous build deltas we want to keep when publishing.
- ///
- private const int keep_delta_count = 4;
-
- private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\"";
-
- private static string homeDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
- private static string codeSigningCertPath => Path.Combine(homeDir, CodeSigningCertificate);
- private static string solutionPath => Environment.CurrentDirectory;
- private static string stagingPath => Path.Combine(solutionPath, StagingFolder);
- private static string iconPath => Path.Combine(solutionPath, ProjectName, IconName);
-
- private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg";
- private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg";
-
- private static readonly Stopwatch sw = new Stopwatch();
-
- private static string codeSigningPassword;
-
- private static bool interactive;
-
- public static void Main(string[] args)
- {
- interactive = args.Length == 0;
-
- displayHeader();
-
- findSolutionPath();
-
- if (!Directory.Exists(ReleasesFolder))
- {
- write("WARNING: No release directory found. Make sure you want this!", ConsoleColor.Yellow);
- Directory.CreateDirectory(ReleasesFolder);
- }
-
- checkGitHubReleases();
-
- refreshDirectory(StagingFolder);
-
- //increment build number until we have a unique one.
- string verBase = DateTime.Now.ToString("yyyy.Mdd.");
- int increment = 0;
- while (Directory.GetFiles(ReleasesFolder, $"*{verBase}{increment}*").Any())
- increment++;
-
- string version = $"{verBase}{increment}";
-
- Console.ForegroundColor = ConsoleColor.White;
- Console.Write($"Ready to deploy {version}!");
- pauseIfInteractive();
-
- sw.Start();
-
- if (!string.IsNullOrEmpty(CodeSigningCertificate))
- {
- Console.Write("Enter code signing password: ");
- codeSigningPassword = args.Length > 0 ? args[0] : readLineMasked();
- }
-
- write("Updating AssemblyInfo...");
- updateCsprojVersion(version);
- updateAppveyorVersion(version);
-
- write("Running build process...");
- foreach (string targetName in TargetNames.Split(','))
- runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln");
-
- write("Creating NuGet deployment package...");
- runCommand(nugetPath, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}");
-
- //prune once before checking for files so we can avoid erroring on files which aren't even needed for this build.
- pruneReleases();
-
- checkReleaseFiles();
-
- write("Running squirrel build...");
- runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --framework-version=net471 --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi");
-
- //prune again to clean up before upload.
- pruneReleases();
-
- //rename setup to install.
- File.Copy(Path.Combine(ReleasesFolder, "Setup.exe"), Path.Combine(ReleasesFolder, "install.exe"), true);
- File.Delete(Path.Combine(ReleasesFolder, "Setup.exe"));
-
- uploadBuild(version);
-
- //reset assemblyinfo.
- updateCsprojVersion("0.0.0");
-
- write("Done!", ConsoleColor.White);
- pauseIfInteractive();
- }
-
- private static void displayHeader()
- {
- Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine();
- Console.WriteLine(" Please note that OSU! and PPY are registered trademarks and as such covered by trademark law.");
- Console.WriteLine(" Do not distribute builds of this project publicly that make use of these.");
- Console.ResetColor();
- Console.WriteLine();
- }
-
- ///
- /// Ensure we have all the files in the release directory which are expected to be there.
- /// This should have been accounted for in earlier steps, and just serves as a verification step.
- ///
- private static void checkReleaseFiles()
- {
- if (!canGitHub) return;
-
- var releaseLines = getReleaseLines();
-
- //ensure we have all files necessary
- foreach (var l in releaseLines)
- if (!File.Exists(Path.Combine(ReleasesFolder, l.Filename)))
- error($"Local file missing {l.Filename}");
- }
-
- private static IEnumerable getReleaseLines() => File.ReadAllLines(Path.Combine(ReleasesFolder, "RELEASES")).Select(l => new ReleaseLine(l));
-
- private static void pruneReleases()
- {
- if (!canGitHub) return;
-
- write("Pruning RELEASES...");
-
- var releaseLines = getReleaseLines().ToList();
-
- var fulls = releaseLines.Where(l => l.Filename.Contains("-full")).Reverse().Skip(1);
-
- //remove any FULL releases (except most recent)
- foreach (var l in fulls)
- {
- write($"- Removing old release {l.Filename}", ConsoleColor.Yellow);
- File.Delete(Path.Combine(ReleasesFolder, l.Filename));
- releaseLines.Remove(l);
- }
-
- //remove excess deltas
- var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray();
- if (deltas.Length > keep_delta_count)
- {
- foreach (var l in deltas.Take(deltas.Length - keep_delta_count))
- {
- write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow);
- File.Delete(Path.Combine(ReleasesFolder, l.Filename));
- releaseLines.Remove(l);
- }
- }
-
- var lines = new List();
- releaseLines.ForEach(l => lines.Add(l.ToString()));
- File.WriteAllLines(Path.Combine(ReleasesFolder, "RELEASES"), lines);
- }
-
- private static void uploadBuild(string version)
- {
- if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate))
- return;
-
- write("Publishing to GitHub...");
-
- write($"- Creating release {version}...", ConsoleColor.Yellow);
- var req = new JsonWebRequest($"{GitHubApiEndpoint}")
- {
- Method = HttpMethod.POST,
- };
- req.AddRaw(JsonConvert.SerializeObject(new GitHubRelease
- {
- Name = version,
- Draft = true,
- PreRelease = true
- }));
- req.AuthenticatedBlockingPerform();
-
- var assetUploadUrl = req.ResponseObject.UploadUrl.Replace("{?name,label}", "?name={0}");
- foreach (var a in Directory.GetFiles(ReleasesFolder).Reverse()) //reverse to upload RELEASES first.
- {
- write($"- Adding asset {a}...", ConsoleColor.Yellow);
- var upload = new WebRequest(assetUploadUrl, Path.GetFileName(a))
- {
- Method = HttpMethod.POST,
- Timeout = 240000,
- ContentType = "application/octet-stream",
- };
-
- upload.AddRaw(File.ReadAllBytes(a));
- upload.AuthenticatedBlockingPerform();
- }
-
- openGitHubReleasePage();
- }
-
- private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
-
- private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken);
-
- private static void checkGitHubReleases()
- {
- if (!canGitHub) return;
-
- write("Checking GitHub releases...");
- var req = new JsonWebRequest>($"{GitHubApiEndpoint}");
- req.AuthenticatedBlockingPerform();
-
- var lastRelease = req.ResponseObject.FirstOrDefault();
-
- if (lastRelease == null)
- return;
-
- if (lastRelease.Draft)
- {
- openGitHubReleasePage();
- error("There's a pending draft release! You probably don't want to push a build with this present.");
- }
-
- //there's a previous release for this project.
- var assetReq = new JsonWebRequest>($"{GitHubApiEndpoint}/{lastRelease.Id}/assets");
- assetReq.AuthenticatedBlockingPerform();
- var assets = assetReq.ResponseObject;
-
- //make sure our RELEASES file is the same as the last build on the server.
- var releaseAsset = assets.FirstOrDefault(a => a.Name == "RELEASES");
-
- //if we don't have a RELEASES asset then the previous release likely wasn't a Squirrel one.
- if (releaseAsset == null) return;
-
- write($"Last GitHub release was {lastRelease.Name}.");
-
- bool requireDownload = false;
-
- if (!File.Exists(Path.Combine(ReleasesFolder, nupkgDistroFilename(lastRelease.Name))))
- {
- write("Last version's package not found locally.", ConsoleColor.Red);
- requireDownload = true;
- }
- else
- {
- var lastReleases = new RawFileWebRequest($"{GitHubApiEndpoint}/assets/{releaseAsset.Id}");
- lastReleases.AuthenticatedBlockingPerform();
- if (File.ReadAllText(Path.Combine(ReleasesFolder, "RELEASES")) != lastReleases.ResponseString)
- {
- write("Server's RELEASES differed from ours.", ConsoleColor.Red);
- requireDownload = true;
- }
- }
-
- if (!requireDownload) return;
-
- write("Refreshing local releases directory...");
- refreshDirectory(ReleasesFolder);
-
- foreach (var a in assets)
- {
- if (a.Name.EndsWith(".exe")) continue;
-
- write($"- Downloading {a.Name}...", ConsoleColor.Yellow);
- new FileWebRequest(Path.Combine(ReleasesFolder, a.Name), $"{GitHubApiEndpoint}/assets/{a.Id}").AuthenticatedBlockingPerform();
- }
- }
-
- private static void refreshDirectory(string directory)
- {
- if (Directory.Exists(directory))
- Directory.Delete(directory, true);
- Directory.CreateDirectory(directory);
- }
-
- private static void updateCsprojVersion(string version)
- {
- var toUpdate = new[] { "", "" };
- string file = Path.Combine(ProjectName, $"{ProjectName}.csproj");
-
- var l1 = File.ReadAllLines(file);
- List l2 = new List();
- foreach (var l in l1)
- {
- string line = l;
-
- foreach (var tag in toUpdate)
- {
- int startIndex = l.IndexOf(tag, StringComparison.InvariantCulture);
- if (startIndex == -1)
- continue;
- startIndex += tag.Length;
-
- int endIndex = l.IndexOf("<", startIndex, StringComparison.InvariantCulture);
- line = $"{l.Substring(0, startIndex)}{version}{l.Substring(endIndex)}";
- }
-
- l2.Add(line);
- }
-
- File.WriteAllLines(file, l2);
- }
-
- ///
- /// Find the base path of the active solution (git checkout location)
- ///
- private static void findSolutionPath()
- {
- string path = Path.GetDirectoryName(Environment.CommandLine.Replace("\"", "").Trim());
-
- if (string.IsNullOrEmpty(path))
- path = Environment.CurrentDirectory;
-
- while (!File.Exists(Path.Combine(path, $"{SolutionName}.sln")))
- path = path.Remove(path.LastIndexOf(Path.DirectorySeparatorChar));
- path += Path.DirectorySeparatorChar;
-
- Environment.CurrentDirectory = path;
- }
-
- private static bool runCommand(string command, string args)
- {
- var psi = new ProcessStartInfo(command, args)
- {
- WorkingDirectory = solutionPath,
- CreateNoWindow = true,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden
- };
-
- Process p = Process.Start(psi);
- if (p == null) return false;
-
- string output = p.StandardOutput.ReadToEnd();
- output += p.StandardError.ReadToEnd();
-
- if (p.ExitCode == 0) return true;
-
- write(output);
- error($"Command {command} {args} failed!");
- return false;
- }
-
- private static string readLineMasked()
- {
- var fg = Console.ForegroundColor;
- Console.ForegroundColor = Console.BackgroundColor;
- var ret = Console.ReadLine();
- Console.ForegroundColor = fg;
-
- return ret;
- }
-
- private static void error(string message)
- {
- Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine($"FATAL ERROR: {message}");
-
- 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)
- {
- Console.ForegroundColor = ConsoleColor.Green;
- Console.Write(sw.ElapsedMilliseconds.ToString().PadRight(8));
- }
- Console.ForegroundColor = col;
- Console.WriteLine(message);
- }
-
- public static void AuthenticatedBlockingPerform(this WebRequest r)
- {
- r.AddHeader("Authorization", $"token {GitHubAccessToken}");
- r.Perform();
- }
- }
-
- internal class RawFileWebRequest : WebRequest
- {
- public RawFileWebRequest(string url) : base(url)
- {
- }
-
- protected override string Accept => "application/octet-stream";
- }
-
- internal class ReleaseLine
- {
- public string Hash;
- public string Filename;
- public int Filesize;
-
- public ReleaseLine(string line)
- {
- var split = line.Split(' ');
- Hash = split[0];
- Filename = split[1];
- Filesize = int.Parse(split[2]);
- }
-
- public override string ToString() => $"{Hash} {Filename} {Filesize}";
- }
-}
diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
deleted file mode 100644
index 063fb89918..0000000000
--- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- net471
- Exe
- AnyCPU
- true
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index a495d7048d..844db4a80f 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -12,6 +12,7 @@ using osu.Framework.Platform;
using osu.Game;
using OpenTK.Input;
using Microsoft.Win32;
+using osu.Framework.Platform.Windows;
namespace osu.Desktop
{
@@ -40,7 +41,7 @@ namespace osu.Desktop
///
/// A method of accessing an osu-stable install in a controlled fashion.
///
- private class StableStorage : DesktopStorage
+ private class StableStorage : WindowsStorage
{
protected override string LocateBasePath()
{
diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index b984c0bbba..26e80b3f48 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -3,7 +3,6 @@
using System.Diagnostics;
using osu.Framework.Allocation;
-using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
@@ -14,6 +13,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
+using osu.Game.Utils;
using OpenTK;
using OpenTK.Graphics;
@@ -118,7 +118,7 @@ namespace osu.Desktop.Overlays
Icon = FontAwesome.fa_check_square;
Activated = delegate
{
- Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
+ Process.Start($"https://osu.ppy.sh/home/changelog/{version}");
return true;
};
}
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 3d64cab84e..766f36fa74 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -1,7 +1,7 @@
-
+
- net471;netcoreapp2.0
+ net471;netcoreapp2.1
WinExe
AnyCPU
true
@@ -20,7 +20,6 @@
osu.Desktop.Program
-
@@ -30,10 +29,10 @@
-
+
-
+
\ 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 eb80f4474c..2a82d65014 100644
--- a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json
+++ b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json
@@ -22,7 +22,7 @@
},
"type": "mono",
"request": "launch",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe",
+ "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Catch.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
@@ -30,12 +30,12 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Debug, netcoreapp2.0)",
+ "name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Catch.Tests.dll"
+ "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, dotnet)",
@@ -43,12 +43,12 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.0)",
+ "name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Catch.Tests.dll"
+ "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, dotnet)",
diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json
index 41ae88f425..6c6d562512 100644
--- a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json
+++ b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json
@@ -40,7 +40,7 @@
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -56,7 +56,7 @@
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -75,7 +75,7 @@
"problemMatcher": []
},
{
- "label": "Restore (netcoreapp2.0)",
+ "label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"args": [
diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs
index f239290ed4..5119260c53 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Tests
Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
{
Anchor = Anchor.CentreLeft,
- Origin = Anchor.BottomLeft
+ Origin = Anchor.TopLeft
},
};
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs
index 275752523d..e77dd76353 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs
@@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Catch.Tests
typeof(DrawableCatchHitObject),
typeof(DrawableFruit),
typeof(DrawableDroplet),
+ typeof(BananaShower),
typeof(Pulp),
};
@@ -53,12 +54,19 @@ namespace osu.Game.Rulesets.Catch.Tests
private DrawableFruit createDrawable(int index)
{
- var fruit = new Fruit
- {
- StartTime = 1000000000000,
- IndexInBeatmap = index,
- Scale = 1.5f,
- };
+ Fruit fruit = index == 5
+ ? new BananaShower.Banana
+ {
+ StartTime = 1000000000000,
+ IndexInBeatmap = index,
+ Scale = 1.5f,
+ }
+ : new Fruit
+ {
+ StartTime = 1000000000000,
+ IndexInBeatmap = index,
+ Scale = 1.5f,
+ };
return new DrawableFruit(fruit)
{
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 3797edde61..93fa2c4d67 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;net471
+ netcoreapp2.1;net471
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
index f40ef67dfb..ad500606ed 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
@@ -28,7 +28,20 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
var endTime = obj as IHasEndTime;
if (positionData == null)
+ {
+ if (endTime != null)
+ {
+ yield return new BananaShower
+ {
+ StartTime = obj.StartTime,
+ Samples = obj.Samples,
+ Duration = endTime.Duration,
+ NewCombo = comboData?.NewCombo ?? false
+ };
+ }
+
yield break;
+ }
if (curveData != null)
{
@@ -48,19 +61,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
yield break;
}
- if (endTime != null)
- {
- yield return new BananaShower
- {
- StartTime = obj.StartTime,
- Samples = obj.Samples,
- Duration = endTime.Duration,
- NewCombo = comboData?.NewCombo ?? false
- };
-
- yield break;
- }
-
yield return new Fruit
{
StartTime = obj.StartTime,
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
index 41792b10a4..4603148114 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
@@ -18,12 +18,19 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
private Circle border;
+ private const float drawable_radius = (float)CatchHitObject.OBJECT_RADIUS * radius_adjust;
+
+ ///
+ /// Because we're adding a border around the fruit, we need to scale down some.
+ ///
+ private const float radius_adjust = 1.1f;
+
public DrawableFruit(Fruit h)
: base(h)
{
Origin = Anchor.Centre;
- Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
+ Size = new Vector2(drawable_radius);
Masking = false;
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
@@ -44,14 +51,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
Hollow = !HitObject.HyperDash,
Type = EdgeEffectType.Glow,
- Radius = 4,
+ Radius = 4 * radius_adjust,
Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f)
},
- Size = new Vector2(Height * 1.5f),
+ Size = new Vector2(Height),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BorderColour = Color4.White,
- BorderThickness = 4f,
+ BorderThickness = 3f * radius_adjust,
Children = new Framework.Graphics.Drawable[]
{
new Box
@@ -82,8 +89,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
private Framework.Graphics.Drawable createPulp(FruitVisualRepresentation representation)
{
- const float large_pulp_3 = 13f;
- const float distance_from_centre_3 = 0.23f;
+ const float large_pulp_3 = 8f * radius_adjust;
+ const float distance_from_centre_3 = 0.15f;
const float large_pulp_4 = large_pulp_3 * 0.925f;
const float distance_from_centre_4 = distance_from_centre_3 / 0.925f;
@@ -106,11 +113,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
new Pulp
{
- Anchor = Anchor.TopCentre,
- Origin = Anchor.BottomCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
- Y = 0.05f,
+ Y = -0.34f,
},
new Pulp
{
@@ -146,11 +151,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
new Pulp
{
- Anchor = Anchor.TopCentre,
- Origin = Anchor.BottomCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
- Y = 0.1f,
+ Y = -0.3f,
},
new Pulp
{
@@ -186,11 +189,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
new Pulp
{
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
- Y = -0.1f,
+ Y = -0.33f,
},
new Pulp
{
@@ -220,10 +221,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
new Pulp
{
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
+ Y = -0.25f,
},
new Pulp
{
@@ -253,16 +253,15 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
new Pulp
{
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
- Y = -0.15f
+ Y = -0.3f
},
new Pulp
{
AccentColour = AccentColour,
- Size = new Vector2(large_pulp_4 * 1.2f, large_pulp_4 * 3),
+ Size = new Vector2(large_pulp_4 * 0.8f, large_pulp_4 * 2.5f),
+ Y = 0.05f,
},
}
};
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
index d17a72a165..250dc8c7f1 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
@@ -29,14 +29,24 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
set
{
accentColour = value;
-
- EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Glow,
- Radius = 8,
- Colour = accentColour.Darken(0.2f).Opacity(0.75f)
- };
+ if (IsLoaded) updateAccentColour();
}
}
+
+ private void updateAccentColour()
+ {
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Radius = Size.X / 2,
+ Colour = accentColour.Darken(0.2f).Opacity(0.75f)
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ updateAccentColour();
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
index 070dc19a6f..52763e09af 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
@@ -32,6 +32,8 @@ namespace osu.Game.Rulesets.Catch.UI
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
+ protected override Vector2 PlayfieldArea => new Vector2(0.86f); // matches stable's vertical offset for catcher plate
+
protected override DrawableHitObject GetVisualRepresentation(CatchHitObject h)
{
switch (h)
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 181536a91e..d8c7b5130d 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
public class CatcherArea : Container
{
- public const float CATCHER_SIZE = 172;
+ public const float CATCHER_SIZE = 84;
protected readonly Catcher MovableCatcher;
@@ -99,8 +99,6 @@ namespace osu.Game.Rulesets.Catch.UI
public class Catcher : Container, IKeyBindingHandler
{
- private Texture texture;
-
private Container caughtFruit;
public Container ExplodingFruitTarget;
@@ -121,10 +119,8 @@ namespace osu.Game.Rulesets.Catch.UI
}
[BackgroundDependencyLoader]
- private void load(TextureStore textures)
+ private void load()
{
- texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
-
Children = new Drawable[]
{
caughtFruit = new Container
@@ -196,13 +192,7 @@ namespace osu.Game.Rulesets.Catch.UI
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
}
- private Sprite createCatcherSprite() => new Sprite
- {
- Size = new Vector2(CATCHER_SIZE),
- FillMode = FillMode.Fill,
- Texture = texture,
- OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly.
- };
+ private Sprite createCatcherSprite() => new CatcherSprite();
///
/// Add a caught fruit to the catcher's stack.
@@ -411,6 +401,23 @@ namespace osu.Game.Rulesets.Catch.UI
f.Expire();
}
}
+
+ private class CatcherSprite : Sprite
+ {
+ public CatcherSprite()
+ {
+ Size = new Vector2(CATCHER_SIZE);
+
+ // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
+ OriginPosition = new Vector2(-0.02f, 0.06f) * CATCHER_SIZE;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
+ }
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json
index fceb403f30..bc41d4ccf9 100644
--- a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json
+++ b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json
@@ -22,7 +22,7 @@
},
"type": "mono",
"request": "launch",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe",
+ "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Mania.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
@@ -30,12 +30,12 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Debug, netcoreapp2.0)",
+ "name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Mania.Tests.dll"
+ "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, dotnet)",
@@ -43,12 +43,12 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.0)",
+ "name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Mania.Tests.dll"
+ "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, dotnet)",
diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json
index b04b068b0d..7fc2f7b2ef 100644
--- a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json
+++ b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json
@@ -40,7 +40,7 @@
"build",
"--no-restore",
"osu.Game.Rulesets.Mania.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -56,7 +56,7 @@
"build",
"--no-restore",
"osu.Game.Rulesets.Mania.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -75,7 +75,7 @@
"problemMatcher": []
},
{
- "label": "Restore (netcoreapp2.0)",
+ "label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"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 e90155568e..77504fdc3c 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;net471
+ netcoreapp2.1;net471
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
index e6e3028d62..93652f7610 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
@@ -105,7 +105,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty
private double computeAccuracyValue(double strainValue)
{
- double hitWindowGreat = (Beatmap.HitObjects.First().HitWindows.Great / 2 - 0.5) / TimeRate;
+ // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
+ double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
if (hitWindowGreat <= 0)
return 0;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
index 7f3985b26d..256811b4c1 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Dual Stages";
public override string ShortenedName => "DS";
public override string Description => @"Double the stages, double the fun!";
- public override double ScoreMultiplier => 0;
+ public override double ScoreMultiplier => 1;
private bool isForCurrentRuleset;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
index 5af898287a..2f951461c3 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string ShortenedName => "RD";
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the keys!";
- public override double ScoreMultiplier => 0;
+ public override double ScoreMultiplier => 1;
public void ApplyToRulesetContainer(RulesetContainer rulesetContainer)
{
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 2f2d1abe6b..93aa57751a 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
public class DrawableHoldNote : DrawableManiaHitObject, IKeyBindingHandler
{
+ public override bool DisplayJudgement => false;
+
public readonly DrawableNote Head;
public readonly DrawableNote Tail;
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 52d514221d..28cd1b6b39 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -213,7 +213,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
- if (!judgement.IsHit)
+ if (!judgement.IsHit || !judgedObject.DisplayJudgement)
return;
explosionContainer.Add(new HitExplosion(judgedObject));
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
index 53631838ba..498bbae17b 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
@@ -173,6 +173,9 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
+ if (!judgedObject.DisplayJudgement)
+ return;
+
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
{
diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json
index 714fb6db6f..13aba025fd 100644
--- a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json
+++ b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json
@@ -22,7 +22,7 @@
},
"type": "mono",
"request": "launch",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe",
+ "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Osu.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
@@ -30,12 +30,12 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Debug, netcoreapp2.0)",
+ "name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Osu.Tests.dll"
+ "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, dotnet)",
@@ -43,12 +43,12 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.0)",
+ "name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Osu.Tests.dll"
+ "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, dotnet)",
diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json
index 657fe07e1a..62cf51382f 100644
--- a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json
+++ b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json
@@ -40,7 +40,7 @@
"build",
"--no-restore",
"osu.Game.Rulesets.Osu.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -56,7 +56,7 @@
"build",
"--no-restore",
"osu.Game.Rulesets.Osu.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -75,7 +75,7 @@
"problemMatcher": []
},
{
- "label": "Restore (netcoreapp2.0)",
+ "label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"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 1695ceacee..c5d9b26145 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;net471
+ netcoreapp2.1;net471
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index eeb776fa6e..57cf962fa7 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -61,20 +61,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (mods.Any(m => !m.Ranked))
return 0;
- // Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done
- // locally for now as doing so would modify animations and other things unexpectedly
- // DO NOT MODIFY THIS
- double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate;
- if (mods.Any(m => m is OsuModHardRock))
- ar = Math.Min(10, ar * 1.4);
- if (mods.Any(m => m is OsuModEasy))
- ar = Math.Max(0, ar / 2);
-
- double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / TimeRate;
- double hitWindowGreat = (Beatmap.HitObjects.First().HitWindows.Great / 2 - 0.5) / TimeRate;
+ // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
+ double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
+ double preEmpt = (int)BeatmapDifficulty.DifficultyRange(Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / TimeRate;
realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5;
- realOverallDifficulty = (80 - 0.5 - hitWindowGreat) / 6;
+ realOverallDifficulty = (80 - hitWindowGreat) / 6;
// Custom multipliers for NoFail and SpunOut.
double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
index 3573c133c2..b2ddd65e38 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
@@ -13,8 +13,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
- public override double ScoreMultiplier => 0;
- public override bool Ranked => false;
+ public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
index e72d667c0b..4220b72b16 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
@@ -7,33 +7,34 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
- public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects
+ public class OsuModHidden : ModHidden
{
public override string Description => @"Play with no approach circles and fading circles/sliders.";
public override double ScoreMultiplier => 1.06;
-
private const double fade_in_duration_multiplier = 0.4;
private const double fade_out_duration_multiplier = 0.3;
- public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ public override void ApplyToDrawableHitObjects(IEnumerable drawables)
{
+ void adjustFadeIn(OsuHitObject h) => h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
+
foreach (var d in drawables.OfType())
{
- d.ApplyCustomUpdateState += ApplyHiddenState;
-
- d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier;
+ adjustFadeIn(d.HitObject);
foreach (var h in d.HitObject.NestedHitObjects.OfType())
- h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
+ adjustFadeIn(h);
}
+
+ base.ApplyToDrawableHitObjects(drawables);
}
- protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
+ protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
{
if (!(drawable is DrawableOsuHitObject d))
return;
diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json
index e1df54e99b..df49e177dc 100644
--- a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json
+++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json
@@ -22,7 +22,7 @@
},
"type": "mono",
"request": "launch",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe",
+ "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Taiko.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
@@ -30,12 +30,12 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Debug, netcoreapp2.0)",
+ "name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Taiko.Tests.dll"
+ "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, dotnet)",
@@ -43,12 +43,12 @@
"console": "internalConsole"
},
{
- "name": "VisualTests (Release, netcoreapp2.0)",
+ "name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
- "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Taiko.Tests.dll"
+ "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, dotnet)",
diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json
index 8bdbcd8e8e..7c8beed00f 100644
--- a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json
+++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json
@@ -40,7 +40,7 @@
"build",
"--no-restore",
"osu.Game.Rulesets.Taiko.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -56,7 +56,7 @@
"build",
"--no-restore",
"osu.Game.Rulesets.Taiko.Tests.csproj",
- "/p:TargetFramework=netcoreapp2.0",
+ "/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -75,7 +75,7 @@
"problemMatcher": []
},
{
- "label": "Restore (netcoreapp2.0)",
+ "label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"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 1221584a2b..dea34d25e7 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;net471
+ netcoreapp2.1;net471
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 9c9cd1f0fb..6b1a25d667 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -94,7 +94,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private double computeAccuracyValue()
{
- double hitWindowGreat = (Beatmap.HitObjects.First().HitWindows.Great / 2 - 0.5) / TimeRate;
+ // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
+ double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
if (hitWindowGreat <= 0)
return 0;
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index f60caf2397..1c9696901c 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -12,6 +12,7 @@ using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
+using SharpCompress.Archives.Zip;
namespace osu.Game.Tests.Beatmaps.IO
{
@@ -77,8 +78,69 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get();
- Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 1);
- Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1);
+ Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
+ Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
+ }
+ finally
+ {
+ host.Exit();
+ }
+ }
+ }
+
+ [Test]
+ public void TestRollbackOnFailure()
+ {
+ //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestRollbackOnFailure"))
+ {
+ try
+ {
+ var osu = loadOsu(host);
+ var manager = osu.Dependencies.Get();
+
+ int fireCount = 0;
+
+ // ReSharper disable once AccessToModifiedClosure
+ manager.ItemAdded += _ => fireCount++;
+ manager.ItemRemoved += _ => fireCount++;
+
+ var imported = loadOszIntoOsu(osu);
+
+ Assert.AreEqual(0, fireCount -= 1);
+
+ imported.Hash += "-changed";
+ manager.Update(imported);
+
+ Assert.AreEqual(0, fireCount -= 2);
+
+ var breakTemp = createTemporaryBeatmap();
+
+ MemoryStream brokenOsu = new MemoryStream(new byte[] { 1, 3, 3, 7 });
+ MemoryStream brokenOsz = new MemoryStream(File.ReadAllBytes(breakTemp));
+
+ File.Delete(breakTemp);
+
+ using (var outStream = File.Open(breakTemp, FileMode.CreateNew))
+ using (var zip = ZipArchive.Open(brokenOsz))
+ {
+ zip.AddEntry("broken.osu", brokenOsu, false);
+ zip.SaveTo(outStream, SharpCompress.Common.CompressionType.Deflate);
+ }
+
+ Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
+ Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
+ Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count);
+
+ // this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu.
+ manager.Import(breakTemp);
+
+ // no events should be fired in the case of a rollback.
+ Assert.AreEqual(0, fireCount);
+
+ Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
+ Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
+ Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count);
}
finally
{
@@ -100,18 +162,17 @@ namespace osu.Game.Tests.Beatmaps.IO
var imported = loadOszIntoOsu(osu);
- //var change = manager.QueryBeatmapSets(_ => true).First();
imported.Hash += "-changed";
manager.Update(imported);
var importedSecondTime = loadOszIntoOsu(osu);
- // check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID != importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID);
- Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 1);
- Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1);
+ // only one beatmap will exist as the online set ID matched, causing purging of the first import.
+ Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
+ Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
}
finally
{
@@ -162,8 +223,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var osu = loadOsu(host);
- var temp = prepareTempCopy(osz_path);
- Assert.IsTrue(File.Exists(temp));
+ var temp = createTemporaryBeatmap();
var importer = new ArchiveImportIPCChannel(client);
if (!importer.ImportAsync(temp).Wait(10000))
@@ -188,8 +248,7 @@ namespace osu.Game.Tests.Beatmaps.IO
try
{
var osu = loadOsu(host);
- var temp = prepareTempCopy(osz_path);
- Assert.IsTrue(File.Exists(temp), "Temporary file copy never substantiated");
+ var temp = createTemporaryBeatmap();
using (File.OpenRead(temp))
osu.Dependencies.Get().Import(temp);
ensureLoaded(osu);
@@ -203,11 +262,17 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
- private BeatmapSetInfo loadOszIntoOsu(OsuGameBase osu)
+ private string createTemporaryBeatmap()
{
- var temp = prepareTempCopy(osz_path);
-
+ var temp = Path.GetTempFileName() + ".osz";
+ File.Copy(osz_path, temp, true);
Assert.IsTrue(File.Exists(temp));
+ return temp;
+ }
+
+ private BeatmapSetInfo loadOszIntoOsu(OsuGameBase osu, string path = null)
+ {
+ var temp = path ?? createTemporaryBeatmap();
var manager = osu.Dependencies.Get();
@@ -219,7 +284,7 @@ namespace osu.Game.Tests.Beatmaps.IO
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
- return imported.FirstOrDefault();
+ return imported.LastOrDefault();
}
private void deleteBeatmapSet(BeatmapSetInfo imported, OsuGameBase osu)
@@ -228,16 +293,10 @@ namespace osu.Game.Tests.Beatmaps.IO
manager.Delete(imported);
Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 0);
- Assert.IsTrue(manager.QueryBeatmapSets(_ => true).ToList().Count == 1);
+ Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending);
}
- private string prepareTempCopy(string path)
- {
- var temp = Path.GetTempFileName();
- return new FileInfo(path).CopyTo(temp, true).FullName;
- }
-
private OsuGameBase loadOsu(GameHost host)
{
var osu = new OsuGameBase();
@@ -286,12 +345,12 @@ namespace osu.Game.Tests.Beatmaps.IO
private void waitForOrAssert(Func result, string failureMessage, int timeout = 60000)
{
- Action waitAction = () =>
+ Task task = Task.Run(() =>
{
while (!result()) Thread.Sleep(200);
- };
+ });
- Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), failureMessage);
+ Assert.IsTrue(task.Wait(timeout), failureMessage);
}
}
}
diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
index c6863d1cb5..f9b055ed55 100644
--- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
Assert.AreEqual("Deif", meta.AuthorString);
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
- Assert.AreEqual(164471 + LegacyBeatmapDecoder.UniversalOffset, meta.PreviewTime);
+ Assert.AreEqual(164471, meta.PreviewTime);
Assert.AreEqual(string.Empty, meta.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
Assert.AreEqual("Renatus", meta.Title);
diff --git a/osu.Game.Tests/Visual/TestCaseAutoplay.cs b/osu.Game.Tests/Visual/TestCaseAutoplay.cs
index cecb327b6c..bf7aa725e5 100644
--- a/osu.Game.Tests/Visual/TestCaseAutoplay.cs
+++ b/osu.Game.Tests/Visual/TestCaseAutoplay.cs
@@ -5,6 +5,7 @@ using System.ComponentModel;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
@@ -15,7 +16,20 @@ namespace osu.Game.Tests.Visual
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
- return base.CreatePlayer(beatmap, ruleset);
+ return new ScoreAccessiblePlayer
+ {
+ InitialBeatmap = beatmap,
+ AllowPause = false,
+ AllowLeadIn = false,
+ AllowResults = false,
+ };
+ }
+
+ protected override bool ContinueCondition(Player player) => base.ContinueCondition(player) && ((ScoreAccessiblePlayer)player).ScoreProcessor.TotalScore > 0;
+
+ private class ScoreAccessiblePlayer : Player
+ {
+ public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
index f819e882d9..4679fca855 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
@@ -65,11 +65,7 @@ namespace osu.Game.Tests.Visual
carousel.SelectionChanged = s => currentSelection = s;
- AddStep("Load Beatmaps", () => { carousel.BeatmapSets = beatmapSets; });
-
- bool changed = false;
- carousel.BeatmapSetsChanged = () => changed = true;
- AddUntilStep(() => changed, "Wait for load");
+ loadBeatmaps(beatmapSets);
testTraversal();
testFiltering();
@@ -84,6 +80,17 @@ namespace osu.Game.Tests.Visual
testCarouselRootIsRandom();
}
+ private void loadBeatmaps(List beatmapSets)
+ {
+ bool changed = false;
+ AddStep($"Load {beatmapSets.Count} Beatmaps", () =>
+ {
+ carousel.BeatmapSetsChanged = () => changed = true;
+ carousel.BeatmapSets = beatmapSets;
+ });
+ AddUntilStep(() => changed, "Wait for load");
+ }
+
private void ensureRandomFetchSuccess() =>
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet);
@@ -423,7 +430,7 @@ namespace osu.Game.Tests.Visual
for (int i = 1; i <= 50; i++)
beatmapSets.Add(createTestBeatmapSet(i));
- AddStep("Load 50 Beatmaps", () => { carousel.BeatmapSets = beatmapSets; });
+ loadBeatmaps(beatmapSets);
advanceSelection(direction: 1, diff: false);
checkNonmatchingFilter();
checkNonmatchingFilter();
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
index 0d3e08154f..996c3b8695 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual
AddStep("show", () =>
{
infoWedge.State = Visibility.Visible;
- infoWedge.UpdateBeatmap(beatmap);
+ infoWedge.Beatmap = beatmap;
});
// select part is redundant, but wait for load isn't
@@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual
AddStep($"select {b.Metadata.Title} beatmap", () =>
{
infoBefore = infoWedge.Info;
- infoWedge.UpdateBeatmap(beatmap.Value = new TestWorkingBeatmap(b));
+ infoWedge.Beatmap = beatmap.Value = new TestWorkingBeatmap(b);
});
AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load");
@@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual
AddStep("select null beatmap", () =>
{
beatmap.Value = beatmap.Default;
- infoWedge.UpdateBeatmap(beatmap);
+ infoWedge.Beatmap = beatmap;
});
}
diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs
index 977f241f7a..0aa8e9691e 100644
--- a/osu.Game.Tests/Visual/TestCaseCursors.cs
+++ b/osu.Game.Tests/Visual/TestCaseCursors.cs
@@ -19,12 +19,12 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseCursors : ManualInputManagerTestCase
{
- private readonly CursorOverrideContainer cursorOverrideContainer;
+ private readonly MenuCursorContainer menuCursorContainer;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];
public TestCaseCursors()
{
- Child = cursorOverrideContainer = new CursorOverrideContainer
+ Child = menuCursorContainer = new MenuCursorContainer
{
RelativeSizeAxes = Axes.Both,
Children = new[]
@@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual
AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor));
AddStep("Move out", moveOut);
AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor));
- AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor));
+ AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor));
}
///
@@ -112,11 +112,11 @@ namespace osu.Game.Tests.Visual
AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3]));
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor));
AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor));
- AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor));
- AddAssert("Check global cursor at mouse", () => checkAtMouse(cursorOverrideContainer.Cursor));
+ AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor));
+ AddAssert("Check global cursor at mouse", () => checkAtMouse(menuCursorContainer.Cursor));
AddStep("Move out", moveOut);
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor));
- AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor));
+ AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor));
}
///
diff --git a/osu.Game.Tests/Visual/TestCaseExternalLinkButton.cs b/osu.Game.Tests/Visual/TestCaseExternalLinkButton.cs
new file mode 100644
index 0000000000..7d8535f428
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseExternalLinkButton.cs
@@ -0,0 +1,23 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Graphics.UserInterface;
+using OpenTK;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestCaseExternalLinkButton : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[] { typeof(ExternalLinkButton) };
+
+ public TestCaseExternalLinkButton()
+ {
+ Child = new ExternalLinkButton("https://osu.ppy.sh/home")
+ {
+ Size = new Vector2(50)
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs b/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs
new file mode 100644
index 0000000000..600784f8db
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs
@@ -0,0 +1,115 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Screens;
+using osu.Game.Screens.Menu;
+using OpenTK.Graphics;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseLoaderAnimation : OsuTestCase
+ {
+ private TestLoader loader;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // required to preload the logo in a headless run (so it doesn't delay the loading itself).
+ Add(new OsuLogo());
+
+ bool logoVisible = false;
+ AddStep("almost instant display", () => Child = loader = new TestLoader(250));
+ AddUntilStep(() =>
+ {
+ logoVisible = loader.Logo?.Alpha > 0;
+ return loader.Logo != null && loader.ScreenLoaded;
+ }, "loaded");
+ AddAssert("logo not visible", () => !logoVisible);
+
+ AddStep("short load", () => Child = loader = new TestLoader(800));
+ AddUntilStep(() =>
+ {
+ logoVisible = loader.Logo?.Alpha > 0;
+ return loader.Logo != null && loader.ScreenLoaded;
+ }, "loaded");
+ AddAssert("logo visible", () => logoVisible);
+ AddUntilStep(() => loader.Logo?.Alpha == 0, "logo gone");
+
+ AddStep("longer load", () => Child = loader = new TestLoader(1400));
+ AddUntilStep(() =>
+ {
+ logoVisible = loader.Logo?.Alpha > 0;
+ return loader.Logo != null && loader.ScreenLoaded;
+ }, "loaded");
+ AddAssert("logo visible", () => logoVisible);
+ AddUntilStep(() => loader.Logo?.Alpha == 0, "logo gone");
+ }
+
+ private class TestLoader : Loader
+ {
+ private readonly double delay;
+
+ public OsuLogo Logo;
+ private TestScreen screen;
+
+ public bool ScreenLoaded => screen.IsCurrentScreen;
+
+ public TestLoader(double delay)
+ {
+ this.delay = delay;
+ }
+
+ protected override void LogoArriving(OsuLogo logo, bool resuming)
+ {
+ Logo = logo;
+ base.LogoArriving(logo, resuming);
+ }
+
+ protected override OsuScreen CreateLoadableScreen() => screen = new TestScreen();
+ protected override ShaderPrecompiler CreateShaderPrecompiler() => new TestShaderPrecompiler(delay);
+
+ private class TestShaderPrecompiler : ShaderPrecompiler
+ {
+ private readonly double delay;
+ private double startTime;
+
+ public TestShaderPrecompiler(double delay)
+ {
+ this.delay = delay;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ startTime = Time.Current;
+ }
+
+ protected override bool AllLoaded => Time.Current > startTime + delay;
+ }
+
+ private class TestScreen : OsuScreen
+ {
+ public TestScreen()
+ {
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.DarkSlateGray,
+ Alpha = 0,
+ };
+ }
+
+ protected override void LogoArriving(OsuLogo logo, bool resuming)
+ {
+ base.LogoArriving(logo, resuming);
+ Child.FadeInFromZero(200);
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseLounge.cs b/osu.Game.Tests/Visual/TestCaseLounge.cs
new file mode 100644
index 0000000000..b96d705d5c
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseLounge.cs
@@ -0,0 +1,211 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Rulesets;
+using osu.Game.Screens.Multi.Components;
+using osu.Game.Screens.Multi.Screens.Lounge;
+using osu.Game.Users;
+using OpenTK.Input;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseLounge : ManualInputManagerTestCase
+ {
+ private TestLounge lounge;
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ lounge = new TestLounge();
+
+ Room[] rooms =
+ {
+ new Room
+ {
+ Name = { Value = @"Just Another Room" },
+ Host = { Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } } },
+ Status = { Value = new RoomStatusPlaying() },
+ Availability = { Value = RoomAvailability.Public },
+ Type = { Value = new GameTypeTagTeam() },
+ Beatmap =
+ {
+ Value = new BeatmapInfo
+ {
+ StarDifficulty = 5.65,
+ Ruleset = rulesets.GetRuleset(0),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Sidetracked Day (Short Ver.)",
+ Artist = @"VINXIS",
+ AuthorString = @"Hobbes2",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/767600/covers/cover.jpg?1526243446",
+ },
+ },
+ },
+ }
+ },
+ MaxParticipants = { Value = 10 },
+ Participants =
+ {
+ Value = new[]
+ {
+ new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } },
+ new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } },
+ new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } },
+ new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } },
+ new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } },
+ new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } },
+ }
+ }
+ },
+ new Room
+ {
+ Name = { Value = @"Not Just Any Room" },
+ Host = { Value = new User { Username = @"Monstrata", Id = 2706438, Country = new Country { FlagName = @"CA" } } },
+ Status = { Value = new RoomStatusOpen() },
+ Availability = { Value = RoomAvailability.FriendsOnly },
+ Type = { Value = new GameTypeTeamVersus() },
+ Beatmap =
+ {
+ Value = new BeatmapInfo
+ {
+ StarDifficulty = 2.73,
+ Ruleset = rulesets.GetRuleset(0),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"lit(var)",
+ Artist = @"kensuke ushio",
+ AuthorString = @"Monstrata",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/623972/covers/cover.jpg?1521167183",
+ },
+ },
+ },
+ }
+ },
+ Participants =
+ {
+ Value = new[]
+ {
+ new User { Username = @"Jeby", Id = 3136279, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 3497 } } },
+ new User { Username = @"DualAkira", Id = 5220933, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 643 } } },
+ new User { Username = @"Datenshi Yohane", Id = 7171857, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 10555 } } },
+ }
+ }
+ },
+ new Room
+ {
+ Name = { Value = @"room THE FINAL" },
+ Host = { Value = new User { Username = @"Delis", Id = 1603923, Country = new Country { FlagName = @"JP" } } },
+ Status = { Value = new RoomStatusPlaying() },
+ Availability = { Value = RoomAvailability.Public },
+ Type = { Value = new GameTypeTagTeam() },
+ Beatmap =
+ {
+ Value = new BeatmapInfo
+ {
+ StarDifficulty = 4.48,
+ Ruleset = rulesets.GetRuleset(3),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"ONIGIRI FREEWAY",
+ Artist = @"OISHII",
+ AuthorString = @"Mentholzzz",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/663098/covers/cover.jpg?1521898837",
+ },
+ },
+ },
+ }
+ },
+ MaxParticipants = { Value = 30 },
+ Participants =
+ {
+ Value = new[]
+ {
+ new User { Username = @"KizuA", Id = 6510442, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 5372 } } },
+ new User { Username = @"Colored", Id = 827563, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 810 } } },
+ new User { Username = @"Beryl", Id = 3817591, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 10096 } } },
+ }
+ }
+ },
+ };
+
+ AddStep(@"show", () => Add(lounge));
+ AddStep(@"set rooms", () => lounge.Rooms = rooms);
+ selectAssert(0);
+ AddStep(@"clear rooms", () => lounge.Rooms = new Room[] {});
+ AddAssert(@"no room selected", () => lounge.SelectedRoom == null);
+ AddStep(@"set rooms", () => lounge.Rooms = rooms);
+ selectAssert(1);
+ AddStep(@"open room 1", () => clickRoom(1));
+ AddStep(@"make lounge current", lounge.MakeCurrent);
+ filterAssert(@"THE FINAL", LoungeTab.Public, 1);
+ filterAssert(string.Empty, LoungeTab.Public, 2);
+ filterAssert(string.Empty, LoungeTab.Private, 1);
+ filterAssert(string.Empty, LoungeTab.Public, 2);
+ filterAssert(@"no matches", LoungeTab.Public, 0);
+ AddStep(@"clear rooms", () => lounge.Rooms = new Room[] {});
+ AddStep(@"set rooms", () => lounge.Rooms = rooms);
+ AddAssert(@"no matches after clear", () => !lounge.ChildRooms.Any());
+ filterAssert(string.Empty, LoungeTab.Public, 2);
+ AddStep(@"exit", lounge.Exit);
+ }
+
+ private void clickRoom(int n)
+ {
+ InputManager.MoveMouseTo(lounge.ChildRooms.ElementAt(n));
+ InputManager.Click(MouseButton.Left);
+ }
+
+ private void selectAssert(int n)
+ {
+ AddStep($@"select room {n}", () => clickRoom(n));
+ AddAssert($@"room {n} selected", () => lounge.SelectedRoom == lounge.ChildRooms.ElementAt(n).Room);
+ }
+
+ private void filterAssert(string filter, LoungeTab tab, int endCount)
+ {
+ AddStep($@"filter '{filter}', {tab}", () => lounge.SetFilter(filter, tab));
+ AddAssert(@"filtered correctly", () => lounge.ChildRooms.Count() == endCount);
+ }
+
+ private class TestLounge : Lounge
+ {
+ public IEnumerable ChildRooms => RoomsContainer.Children.Where(r => r.MatchingFilter);
+ public Room SelectedRoom => Inspector.Room;
+
+ public void SetFilter(string filter, LoungeTab tab)
+ {
+ Filter.Search.Current.Value = filter;
+ Filter.Tabs.Current.Value = tab;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs
index d3d21509fd..3255478bea 100644
--- a/osu.Game.Tests/Visual/TestCaseMods.cs
+++ b/osu.Game.Tests/Visual/TestCaseMods.cs
@@ -39,8 +39,6 @@ namespace osu.Game.Tests.Visual
typeof(SpecialSection),
};
- private const string unranked_suffix = " (Unranked)";
-
private RulesetStore rulesets;
private ModDisplay modDisplay;
private TestModSelectOverlay modSelect;
@@ -121,7 +119,7 @@ namespace osu.Game.Tests.Visual
private void testManiaMods(ManiaRuleset ruleset)
{
- testMultiplierTextUnranked(ruleset.GetModsFor(ModType.Special).First(m => m is ManiaModRandom));
+ testRankedText(ruleset.GetModsFor(ModType.Special).First(m => m is ManiaModRandom));
}
private void testSingleMod(Mod mod)
@@ -198,13 +196,16 @@ namespace osu.Game.Tests.Visual
checkLabelColor(Color4.White);
}
- private void testMultiplierTextUnranked(Mod mod)
+ private void testRankedText(Mod mod)
{
- AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
+ AddWaitStep(1, "wait for fade");
+ AddAssert("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0);
selectNext(mod);
- AddAssert("check for unranked", () => modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
+ AddWaitStep(1, "wait for fade");
+ AddAssert("check for unranked", () => modSelect.UnrankedLabel.Alpha != 0);
selectPrevious(mod);
- AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
+ AddWaitStep(1, "wait for fade");
+ AddAssert("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0);
}
private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1));
@@ -240,6 +241,7 @@ namespace osu.Game.Tests.Visual
}
public new OsuSpriteText MultiplierLabel => base.MultiplierLabel;
+ public new OsuSpriteText UnrankedLabel => base.UnrankedLabel;
public new TriangleButton DeselectAllButton => base.DeselectAllButton;
public new Color4 LowMultiplierColour => base.LowMultiplierColour;
diff --git a/osu.Game.Tests/Visual/TestCaseMultiHeader.cs b/osu.Game.Tests/Visual/TestCaseMultiHeader.cs
index af51a6221f..d27a447077 100644
--- a/osu.Game.Tests/Visual/TestCaseMultiHeader.cs
+++ b/osu.Game.Tests/Visual/TestCaseMultiHeader.cs
@@ -4,7 +4,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Screens.Multi;
-using osu.Game.Screens.Multi.Screens;
+using osu.Game.Screens.Multi.Screens.Lounge;
namespace osu.Game.Tests.Visual
{
@@ -13,14 +13,14 @@ namespace osu.Game.Tests.Visual
{
public TestCaseMultiHeader()
{
- Lobby lobby;
+ Lounge lounge;
Children = new Drawable[]
{
- lobby = new Lobby
+ lounge = new Lounge
{
Padding = new MarginPadding { Top = Header.HEIGHT },
},
- new Header(lobby),
+ new Header(lounge),
};
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseQuitButton.cs b/osu.Game.Tests/Visual/TestCaseQuitButton.cs
new file mode 100644
index 0000000000..a427b7a20a
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseQuitButton.cs
@@ -0,0 +1,53 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Screens.Play.HUD;
+using OpenTK;
+using OpenTK.Input;
+
+namespace osu.Game.Tests.Visual
+{
+ [Description("'Hold to Quit' UI element")]
+ public class TestCaseQuitButton : ManualInputManagerTestCase
+ {
+ private bool exitAction;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ QuitButton quitButton;
+
+ Add(quitButton = new QuitButton
+ {
+ Origin = Anchor.BottomRight,
+ Anchor = Anchor.BottomRight,
+ Action = () => exitAction = true
+ });
+
+ var text = quitButton.Children.OfType().First();
+
+ AddStep("Trigger text fade in", () => InputManager.MoveMouseTo(quitButton));
+ AddUntilStep(() => text.IsPresent && !exitAction, "Text visible");
+ AddStep("Trigger text fade out", () => InputManager.MoveMouseTo(Vector2.One));
+ AddUntilStep(() => !text.IsPresent && !exitAction, "Text is not visible");
+
+ AddStep("Trigger exit action", () =>
+ {
+ exitAction = false;
+ InputManager.MoveMouseTo(quitButton);
+ InputManager.PressButton(MouseButton.Left);
+ });
+
+ AddStep("Early release", () => InputManager.ReleaseButton(MouseButton.Left));
+ AddAssert("action not triggered", () => !exitAction);
+
+ AddStep("Trigger exit action", () => InputManager.PressButton(MouseButton.Left));
+ AddUntilStep(() => exitAction, $"{nameof(quitButton.Action)} was triggered");
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs b/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
index 7a743655f4..83bbbfddd1 100644
--- a/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
+++ b/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
@@ -84,12 +84,9 @@ namespace osu.Game.Tests.Visual
private abstract class TestScreen : OsuScreen
{
- protected abstract string Title { get; }
protected abstract string NextTitle { get; }
protected abstract TestScreen CreateNextScreen();
- public override string ToString() => Title;
-
public TestScreen PushNext()
{
TestScreen screen = CreateNextScreen();
@@ -130,14 +127,14 @@ namespace osu.Game.Tests.Visual
private class TestScreenOne : TestScreen
{
- protected override string Title => @"Screen One";
+ public override string Title => @"Screen One";
protected override string NextTitle => @"Two";
protected override TestScreen CreateNextScreen() => new TestScreenTwo();
}
private class TestScreenTwo : TestScreen
{
- protected override string Title => @"Screen Two";
+ public override string Title => @"Screen Two";
protected override string NextTitle => @"One";
protected override TestScreen CreateNextScreen() => new TestScreenOne();
}
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 057c2c2de1..532915100b 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -2,7 +2,7 @@
WinExe
- netcoreapp2.0;net471
+ netcoreapp2.1;net471
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 36fde8a319..806bcc4132 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps
protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
{
- model.Beatmaps = createBeatmapDifficulties(archive);
+ model.Beatmaps = createBeatmapDifficulties(model, archive);
// remove metadata from difficulties where it matches the set
foreach (BeatmapInfo b in model.Beatmaps)
@@ -107,6 +107,7 @@ namespace osu.Game.Beatmaps
{
Delete(existingOnlineId);
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
+ Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({model.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
}
}
@@ -169,7 +170,7 @@ namespace osu.Game.Beatmaps
{
if (error is OperationCanceledException) return;
- downloadNotification.State = ProgressNotificationState.Completed;
+ downloadNotification.State = ProgressNotificationState.Cancelled;
Logger.Error(error, "Beatmap download failed!");
currentDownloads.Remove(request);
};
@@ -241,7 +242,13 @@ namespace osu.Game.Beatmaps
/// Returns a list of all usable s.
///
/// A list of available .
- public List GetAllUsableBeatmapSets() => beatmaps.ConsumableItems.Where(s => !s.DeletePending && !s.Protected).ToList();
+ public List GetAllUsableBeatmapSets() => GetAllUsableBeatmapSetsEnumerable().ToList();
+
+ ///
+ /// Returns a list of all usable s.
+ ///
+ /// A list of available .
+ public IQueryable GetAllUsableBeatmapSetsEnumerable() => beatmaps.ConsumableItems.Where(s => !s.DeletePending && !s.Protected);
///
/// Perform a lookup query on available s.
@@ -303,7 +310,7 @@ namespace osu.Game.Beatmaps
{
// let's make sure there are actually .osu files to import.
string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
- if (string.IsNullOrEmpty(mapName)) throw new InvalidOperationException("No beatmap files found in the map folder.");
+ if (string.IsNullOrEmpty(mapName)) throw new InvalidOperationException("No beatmap files found in this beatmap archive.");
BeatmapMetadata metadata;
using (var stream = new StreamReader(reader.GetStream(mapName)))
@@ -321,7 +328,7 @@ namespace osu.Game.Beatmaps
///
/// Create all required s for the provided archive.
///
- private List createBeatmapDifficulties(ArchiveReader reader)
+ private List createBeatmapDifficulties(BeatmapSetInfo model, ArchiveReader reader)
{
var beatmapInfos = new List();
@@ -341,6 +348,14 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
+ // ensure we have the same online set ID as the set itself.
+ beatmap.BeatmapInfo.OnlineBeatmapSetID = model.OnlineBeatmapSetID;
+ beatmap.BeatmapInfo.Metadata.OnlineBeatmapSetID = model.OnlineBeatmapSetID;
+
+ // check that no existing beatmap exists that is imported with the same online beatmap ID. if so, give it precedence.
+ if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue && QueryBeatmap(b => b.OnlineBeatmapID.Value == beatmap.BeatmapInfo.OnlineBeatmapID.Value) != null)
+ beatmap.BeatmapInfo.OnlineBeatmapID = null;
+
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
beatmap.BeatmapInfo.Ruleset = ruleset;
diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
index a70caf0207..8f985306e3 100644
--- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
@@ -36,6 +36,11 @@ namespace osu.Game.Beatmaps
///
public bool HasVideo { get; set; }
+ ///
+ /// Whether or not this beatmap set has a storyboard.
+ ///
+ public bool HasStoryboard { get; set; }
+
///
/// The different sizes of cover art for this beatmap set.
///
diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs
new file mode 100644
index 0000000000..1976db907c
--- /dev/null
+++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs
@@ -0,0 +1,79 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using OpenTK.Graphics;
+
+namespace osu.Game.Beatmaps.Drawables
+{
+ public class UpdateableBeatmapSetCover : Container
+ {
+ private Drawable displayedCover;
+
+ private BeatmapSetInfo beatmapSet;
+ public BeatmapSetInfo BeatmapSet
+ {
+ get { return beatmapSet; }
+ set
+ {
+ if (value == beatmapSet) return;
+ beatmapSet = value;
+
+ if (IsLoaded)
+ updateCover();
+ }
+ }
+
+ private BeatmapSetCoverType coverType = BeatmapSetCoverType.Cover;
+ public BeatmapSetCoverType CoverType
+ {
+ get { return coverType; }
+ set
+ {
+ if (value == coverType) return;
+ coverType = value;
+
+ if (IsLoaded)
+ updateCover();
+ }
+ }
+
+ public UpdateableBeatmapSetCover()
+ {
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ updateCover();
+ }
+
+ private void updateCover()
+ {
+ displayedCover?.FadeOut(400);
+ displayedCover?.Expire();
+ displayedCover = null;
+
+ if (beatmapSet != null)
+ {
+ Add(displayedCover = new DelayedLoadWrapper(
+ new BeatmapSetCover(beatmapSet, coverType)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fill,
+ OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
+ })
+ );
+ }
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 2aee419d20..a83ac26fb3 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -8,7 +8,6 @@ using System.Linq;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Framework;
namespace osu.Game.Beatmaps.Formats
{
@@ -28,23 +27,17 @@ namespace osu.Game.Beatmaps.Formats
AddDecoder(@"osu file format v", m => new LegacyBeatmapDecoder(int.Parse(m.Split('v').Last())));
}
- ///
- /// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited.
- /// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
- ///
- public static int UniversalOffset => RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? -22 : 0;
-
///
/// Whether or not beatmap or runtime offsets should be applied. Defaults on; only disable for testing purposes.
///
public bool ApplyOffsets = true;
- private readonly int offset = UniversalOffset;
+ private readonly int offset;
public LegacyBeatmapDecoder(int version = LATEST_VERSION) : base(version)
{
// BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off)
- offset += FormatVersion < 5 ? 24 : 0;
+ offset = FormatVersion < 5 ? 24 : 0;
}
protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap)
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index b3082e49de..597960c352 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -82,6 +82,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential);
+ Set(OsuSetting.IncreaseFirstObjectVisibility, true);
+
// Update
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
@@ -144,6 +146,7 @@ namespace osu.Game.Configuration
ScreenshotCaptureMenuCursor,
SongSelectRightMouseScroll,
BeatmapSkins,
- BeatmapHitsounds
+ BeatmapHitsounds,
+ IncreaseFirstObjectVisibility
}
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index e04559d547..cbf0df3227 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.EntityFrameworkCore;
+using osu.Framework.IO.File;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.IO;
@@ -56,13 +57,49 @@ namespace osu.Game.Database
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private ArchiveImportIPCChannel ipc;
+ private readonly List cachedEvents = new List();
+
+ ///
+ /// Allows delaying of outwards events until an operation is confirmed (at a database level).
+ ///
+ private bool delayingEvents;
+
+ ///
+ /// Begin delaying outwards events.
+ ///
+ private void delayEvents() => delayingEvents = true;
+
+ ///
+ /// Flush delayed events and disable delaying.
+ ///
+ /// Whether the flushed events should be performed.
+ private void flushEvents(bool perform)
+ {
+ if (perform)
+ {
+ foreach (var a in cachedEvents)
+ a.Invoke();
+ }
+
+ cachedEvents.Clear();
+ delayingEvents = false;
+ }
+
+ private void handleEvent(Action a)
+ {
+ if (delayingEvents)
+ cachedEvents.Add(a);
+ else
+ a.Invoke();
+ }
+
protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStore modelStore, IIpcHost importHost = null)
{
ContextFactory = contextFactory;
ModelStore = modelStore;
- ModelStore.ItemAdded += s => ItemAdded?.Invoke(s);
- ModelStore.ItemRemoved += s => ItemRemoved?.Invoke(s);
+ ModelStore.ItemAdded += s => handleEvent(() => ItemAdded?.Invoke(s));
+ ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s));
Files = new FileStore(contextFactory, storage);
@@ -138,24 +175,56 @@ namespace osu.Game.Database
/// The archive to be imported.
public TModel Import(ArchiveReader archive)
{
- using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes.
+ TModel item = null;
+ delayEvents();
+
+ try
{
- // create a new model (don't yet add to database)
- var item = CreateModel(archive);
+ using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes.
+ {
+ try
+ {
+ if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}");
- var existing = CheckForExisting(item);
+ // create a new model (don't yet add to database)
+ item = CreateModel(archive);
- if (existing != null) return existing;
+ var existing = CheckForExisting(item);
- item.Files = createFileInfos(archive, Files);
+ if (existing != null)
+ {
+ Logger.Log($"Found existing {typeof(TModel)} for {archive.Name} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
+ return existing;
+ }
- Populate(item, archive);
+ item.Files = createFileInfos(archive, Files);
- // import to store
- ModelStore.Add(item);
+ Populate(item, archive);
- return item;
+ // import to store
+ ModelStore.Add(item);
+ }
+ catch (Exception e)
+ {
+ write.Errors.Add(e);
+ throw;
+ }
+ }
+
+ Logger.Log($"Import of {archive.Name} successfully completed!", LoggingTarget.Database);
}
+ catch (Exception e)
+ {
+ Logger.Error(e, $"Import of {archive.Name} failed and has been rolled back.", LoggingTarget.Database);
+ item = null;
+ }
+ finally
+ {
+ // we only want to flush events after we've confirmed the write context didn't have any errors.
+ flushEvents(item != null);
+ }
+
+ return item;
}
///
@@ -178,12 +247,8 @@ namespace osu.Game.Database
/// The item to delete.
public void Delete(TModel item)
{
- using (var usage = ContextFactory.GetForWrite())
+ using (ContextFactory.GetForWrite())
{
- var context = usage.Context;
-
- context.ChangeTracker.AutoDetectChangesEnabled = false;
-
// re-fetch the model on the import context.
var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == item.ID);
@@ -191,8 +256,6 @@ namespace osu.Game.Database
if (ModelStore.Delete(foundModel))
Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray());
-
- context.ChangeTracker.AutoDetectChangesEnabled = true;
}
}
@@ -302,7 +365,7 @@ namespace osu.Game.Database
using (Stream s = reader.GetStream(file))
fileInfos.Add(new TFileModel
{
- Filename = file,
+ Filename = FileSafety.PathSanitise(file),
FileInfo = files.Add(s)
});
diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs
index 71960303b5..5160239c38 100644
--- a/osu.Game/Database/DatabaseContextFactory.cs
+++ b/osu.Game/Database/DatabaseContextFactory.cs
@@ -1,7 +1,10 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
+using System.Linq;
using System.Threading;
+using Microsoft.EntityFrameworkCore.Storage;
using osu.Framework.Platform;
namespace osu.Game.Database
@@ -17,8 +20,12 @@ namespace osu.Game.Database
private readonly object writeLock = new object();
private bool currentWriteDidWrite;
+ private bool currentWriteDidError;
+
private int currentWriteUsages;
+ private IDbContextTransaction currentWriteTransaction;
+
public DatabaseContextFactory(GameHost host)
{
this.host = host;
@@ -35,14 +42,41 @@ namespace osu.Game.Database
/// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context).
/// This method may block if a write is already active on a different thread.
///
+ /// Whether to start a transaction for this write.
/// A usage containing a usable context.
- public DatabaseWriteUsage GetForWrite()
+ public DatabaseWriteUsage GetForWrite(bool withTransaction = true)
{
Monitor.Enter(writeLock);
+ OsuDbContext context;
+
+ try
+ {
+ if (currentWriteTransaction == null && withTransaction)
+ {
+ // this mitigates the fact that changes on tracked entities will not be rolled back with the transaction by ensuring write operations are always executed in isolated contexts.
+ // if this results in sub-optimal efficiency, we may need to look into removing Database-level transactions in favour of running SaveChanges where we currently commit the transaction.
+ if (threadContexts.IsValueCreated)
+ recycleThreadContexts();
+
+ context = threadContexts.Value;
+ currentWriteTransaction = context.Database.BeginTransaction();
+ }
+ else
+ {
+ // we want to try-catch the retrieval of the context because it could throw an error (in CreateContext).
+ context = threadContexts.Value;
+ }
+ }
+ catch (Exception e)
+ {
+ // retrieval of a context could trigger a fatal error.
+ Monitor.Exit(writeLock);
+ throw;
+ }
Interlocked.Increment(ref currentWriteUsages);
- return new DatabaseWriteUsage(threadContexts.Value, usageCompleted);
+ return new DatabaseWriteUsage(context, usageCompleted) { IsTransactionLeader = currentWriteTransaction != null && currentWriteUsages == 1 };
}
private void usageCompleted(DatabaseWriteUsage usage)
@@ -52,18 +86,27 @@ namespace osu.Game.Database
try
{
currentWriteDidWrite |= usage.PerformedWrite;
+ currentWriteDidError |= usage.Errors.Any();
- if (usages > 0) return;
-
- if (currentWriteDidWrite)
+ if (usages == 0)
{
- // explicitly dispose to ensure any outstanding flushes happen as soon as possible (and underlying resources are purged).
- usage.Context.Dispose();
+ if (currentWriteDidError)
+ currentWriteTransaction?.Rollback();
+ else
+ currentWriteTransaction?.Commit();
+ if (currentWriteDidWrite || currentWriteDidError)
+ {
+ // explicitly dispose to ensure any outstanding flushes happen as soon as possible (and underlying resources are purged).
+ usage.Context.Dispose();
+
+ // once all writes are complete, we want to refresh thread-specific contexts to make sure they don't have stale local caches.
+ recycleThreadContexts();
+ }
+
+ currentWriteTransaction = null;
currentWriteDidWrite = false;
-
- // once all writes are complete, we want to refresh thread-specific contexts to make sure they don't have stale local caches.
- recycleThreadContexts();
+ currentWriteDidError = false;
}
}
finally
@@ -74,19 +117,18 @@ namespace osu.Game.Database
private void recycleThreadContexts() => threadContexts = new ThreadLocal(CreateContext);
- protected virtual OsuDbContext CreateContext()
+ protected virtual OsuDbContext CreateContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(database_name))
{
- var ctx = new OsuDbContext(host.Storage.GetDatabaseConnectionString(database_name));
- ctx.Database.AutoTransactionsEnabled = false;
-
- return ctx;
- }
+ Database = { AutoTransactionsEnabled = false }
+ };
public void ResetDatabase()
{
lock (writeLock)
{
recycleThreadContexts();
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
host.Storage.DeleteDatabase(database_name);
}
}
diff --git a/osu.Game/Database/DatabaseWriteUsage.cs b/osu.Game/Database/DatabaseWriteUsage.cs
index 7858c1a0d1..64ab24e824 100644
--- a/osu.Game/Database/DatabaseWriteUsage.cs
+++ b/osu.Game/Database/DatabaseWriteUsage.cs
@@ -2,34 +2,50 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using Microsoft.EntityFrameworkCore.Storage;
+using System.Collections.Generic;
namespace osu.Game.Database
{
public class DatabaseWriteUsage : IDisposable
{
public readonly OsuDbContext Context;
- private readonly IDbContextTransaction transaction;
private readonly Action usageCompleted;
public DatabaseWriteUsage(OsuDbContext context, Action onCompleted)
{
Context = context;
- transaction = Context.BeginTransaction();
usageCompleted = onCompleted;
}
public bool PerformedWrite { get; private set; }
private bool isDisposed;
+ public List Errors = new List();
+
+ ///
+ /// Whether this write usage will commit a transaction on completion.
+ /// If false, there is a parent usage responsible for transaction commit.
+ ///
+ public bool IsTransactionLeader = false;
protected void Dispose(bool disposing)
{
if (isDisposed) return;
isDisposed = true;
- PerformedWrite |= Context.SaveChanges(transaction) > 0;
- usageCompleted?.Invoke(this);
+ try
+ {
+ PerformedWrite |= Context.SaveChanges() > 0;
+ }
+ catch (Exception e)
+ {
+ Errors.Add(e);
+ throw;
+ }
+ finally
+ {
+ usageCompleted?.Invoke(this);
+ }
}
public void Dispose()
diff --git a/osu.Game/Database/IDatabaseContextFactory.cs b/osu.Game/Database/IDatabaseContextFactory.cs
index 372e1770e4..d38d15b252 100644
--- a/osu.Game/Database/IDatabaseContextFactory.cs
+++ b/osu.Game/Database/IDatabaseContextFactory.cs
@@ -14,7 +14,8 @@ namespace osu.Game.Database
/// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context).
/// This method may block if a write is already active on a different thread.
///
+ /// Whether to start a transaction for this write.
/// A usage containing a usable context.
- DatabaseWriteUsage GetForWrite();
+ DatabaseWriteUsage GetForWrite(bool withTransaction = true);
}
}
diff --git a/osu.Game/Database/MutableDatabaseBackedStore.cs b/osu.Game/Database/MutableDatabaseBackedStore.cs
index 8569d81f01..69a1f57cc4 100644
--- a/osu.Game/Database/MutableDatabaseBackedStore.cs
+++ b/osu.Game/Database/MutableDatabaseBackedStore.cs
@@ -50,11 +50,10 @@ namespace osu.Game.Database
/// The item to update.
public void Update(T item)
{
- ItemRemoved?.Invoke(item);
-
using (var usage = ContextFactory.GetForWrite())
usage.Context.Update(item);
+ ItemRemoved?.Invoke(item);
ItemAdded?.Invoke(item);
}
diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index 1979ce3648..4b0de57c4c 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -3,7 +3,6 @@
using System;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using osu.Framework.Logging;
@@ -59,11 +58,20 @@ namespace osu.Game.Database
this.connectionString = connectionString;
var connection = Database.GetDbConnection();
- connection.Open();
- using (var cmd = connection.CreateCommand())
+ try
{
- cmd.CommandText = "PRAGMA journal_mode=WAL;";
- cmd.ExecuteNonQuery();
+ connection.Open();
+
+ using (var cmd = connection.CreateCommand())
+ {
+ cmd.CommandText = "PRAGMA journal_mode=WAL;";
+ cmd.ExecuteNonQuery();
+ }
+ }
+ catch (Exception e)
+ {
+ connection.Close();
+ throw;
}
}
@@ -83,8 +91,8 @@ namespace osu.Game.Database
base.OnModelCreating(modelBuilder);
modelBuilder.Entity().HasIndex(b => b.OnlineBeatmapID).IsUnique();
- modelBuilder.Entity().HasIndex(b => b.MD5Hash).IsUnique();
- modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique();
+ modelBuilder.Entity().HasIndex(b => b.MD5Hash);
+ modelBuilder.Entity().HasIndex(b => b.Hash);
modelBuilder.Entity().HasIndex(b => b.OnlineBeatmapSetID).IsUnique();
modelBuilder.Entity().HasIndex(b => b.DeletePending);
@@ -104,19 +112,6 @@ namespace osu.Game.Database
modelBuilder.Entity().HasOne(b => b.BaseDifficulty);
}
- public IDbContextTransaction BeginTransaction()
- {
- // return Database.BeginTransaction();
- return null;
- }
-
- public int SaveChanges(IDbContextTransaction transaction = null)
- {
- var ret = base.SaveChanges();
- if (ret > 0) transaction?.Commit();
- return ret;
- }
-
private class OsuDbLoggerFactory : ILoggerFactory
{
#region Disposal
diff --git a/osu.Game/Database/SingletonContextFactory.cs b/osu.Game/Database/SingletonContextFactory.cs
index 74951e8433..ce3fbf6881 100644
--- a/osu.Game/Database/SingletonContextFactory.cs
+++ b/osu.Game/Database/SingletonContextFactory.cs
@@ -14,6 +14,6 @@ namespace osu.Game.Database
public OsuDbContext Get() => context;
- public DatabaseWriteUsage GetForWrite() => new DatabaseWriteUsage(context, null);
+ public DatabaseWriteUsage GetForWrite(bool withTransaction = true) => new DatabaseWriteUsage(context, null);
}
}
diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs
new file mode 100644
index 0000000000..adfc258f61
--- /dev/null
+++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs
@@ -0,0 +1,52 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+
+namespace osu.Game.Graphics.Containers
+{
+ public abstract class HoldToConfirmContainer : Container
+ {
+ public Action Action;
+
+ private const int activate_delay = 400;
+ private const int fadeout_delay = 200;
+
+ private bool fired;
+ private bool confirming;
+
+ ///
+ /// Whether the overlay should be allowed to return from a fired state.
+ ///
+ protected virtual bool AllowMultipleFires => false;
+
+ public Bindable Progress = new BindableDouble();
+
+ protected void BeginConfirm()
+ {
+ if (confirming || !AllowMultipleFires && fired) return;
+
+ confirming = true;
+
+ this.TransformBindableTo(Progress, 1, activate_delay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm());
+ }
+
+ protected virtual void Confirm()
+ {
+ Action?.Invoke();
+ fired = true;
+ }
+
+ protected void AbortConfirm()
+ {
+ if (!AllowMultipleFires && fired) return;
+
+ confirming = false;
+
+ this.TransformBindableTo(Progress, 0, fadeout_delay, Easing.Out);
+ }
+ }
+}
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index 11a2034a8f..0186a170c9 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using OpenTK;
using osu.Framework.Configuration;
+using osu.Game.Overlays;
namespace osu.Game.Graphics.Containers
{
@@ -16,13 +17,13 @@ namespace osu.Game.Graphics.Containers
private SampleChannel samplePopIn;
private SampleChannel samplePopOut;
- private readonly BindableBool allowOpeningOverlays = new BindableBool(true);
+ protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All);
[BackgroundDependencyLoader(true)]
private void load(OsuGame osuGame, AudioManager audio)
{
if (osuGame != null)
- allowOpeningOverlays.BindTo(osuGame.AllowOpeningOverlays);
+ OverlayActivationMode.BindTo(osuGame.OverlayActivationMode);
samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in");
samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out");
@@ -52,20 +53,18 @@ namespace osu.Game.Graphics.Containers
private void onStateChanged(Visibility visibility)
{
- if (allowOpeningOverlays)
+ switch (visibility)
{
- switch (visibility)
- {
- case Visibility.Visible:
+ case Visibility.Visible:
+ if (OverlayActivationMode != OverlayActivation.Disabled)
samplePopIn?.Play();
- break;
- case Visibility.Hidden:
- samplePopOut?.Play();
- break;
- }
+ else
+ State = Visibility.Hidden;
+ break;
+ case Visibility.Hidden:
+ samplePopOut?.Play();
+ break;
}
- else
- State = Visibility.Hidden;
}
}
}
diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs
similarity index 94%
rename from osu.Game/Graphics/Cursor/CursorOverrideContainer.cs
rename to osu.Game/Graphics/Cursor/MenuCursorContainer.cs
index 1e56cb6052..5823fad93a 100644
--- a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Graphics.Cursor
///
/// A container which provides a which can be overridden by hovered s.
///
- public class CursorOverrideContainer : Container, IProvideCursor
+ public class MenuCursorContainer : Container, IProvideCursor
{
protected override Container Content => content;
private readonly Container content;
@@ -25,7 +25,7 @@ namespace osu.Game.Graphics.Cursor
public CursorContainer Cursor { get; }
public bool ProvidingUserCursor => true;
- public CursorOverrideContainer()
+ public MenuCursorContainer()
{
AddRangeInternal(new Drawable[]
{
diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
new file mode 100644
index 0000000000..77079894cc
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
@@ -0,0 +1,63 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Diagnostics;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Input;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ public class ExternalLinkButton : CompositeDrawable, IHasTooltip
+ {
+ public string Link { get; set; }
+
+ private Color4 hoverColour;
+
+ public ExternalLinkButton(string link = null)
+ {
+ Link = link;
+ Size = new Vector2(12);
+ InternalChild = new SpriteIcon
+ {
+ Icon = FontAwesome.fa_external_link,
+ RelativeSizeAxes = Axes.Both
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ hoverColour = colours.Yellow;
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ InternalChild.FadeColour(hoverColour, 500, Easing.OutQuint);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ InternalChild.FadeColour(Color4.White, 500, Easing.OutQuint);
+ base.OnHoverLost(state);
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ if(Link != null)
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = Link,
+ UseShellExecute = true //see https://github.com/dotnet/corefx/issues/10361
+ });
+ return true;
+ }
+
+ public string TooltipText => "View in browser";
+ }
+}
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index fc14a9c6ba..d015a563f6 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
@@ -6,6 +6,7 @@ using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -157,7 +158,7 @@ namespace osu.Game.Graphics.UserInterface
Margin = new MarginPadding { Top = 5, Bottom = 5 },
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
- Text = (value as Enum)?.GetDescription() ?? value.ToString(),
+ Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetDescription() ?? value.ToString(),
TextSize = 14,
Font = @"Exo2.0-Bold", // Font should only turn bold when active?
},
diff --git a/osu.Game/IO/Archives/LegacyFilesystemReader.cs b/osu.Game/IO/Archives/LegacyFilesystemReader.cs
index 7871f1439a..dcaaf41c77 100644
--- a/osu.Game/IO/Archives/LegacyFilesystemReader.cs
+++ b/osu.Game/IO/Archives/LegacyFilesystemReader.cs
@@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using osu.Framework.IO.File;
namespace osu.Game.IO.Archives
{
@@ -15,19 +14,20 @@ namespace osu.Game.IO.Archives
{
private readonly string path;
- public LegacyFilesystemReader(string path) : base(Path.GetFileName(path))
+ public LegacyFilesystemReader(string path)
+ : base(Path.GetFileName(path))
{
- this.path = path;
+ // re-get full path to standardise with Directory.GetFiles return values below.
+ this.path = Path.GetFullPath(path);
}
public override Stream GetStream(string name) => File.OpenRead(Path.Combine(path, name));
public override void Dispose()
{
- // no-op
}
- public override IEnumerable Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => FileSafety.GetRelativePath(f, path)).ToArray();
+ public override IEnumerable Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => f.Replace(path, string.Empty).Trim(Path.DirectorySeparatorChar)).ToArray();
public override Stream GetUnderlyingStream() => null;
}
diff --git a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs
new file mode 100644
index 0000000000..f28408bfb3
--- /dev/null
+++ b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs
@@ -0,0 +1,377 @@
+//
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using osu.Game.Database;
+using System;
+
+namespace osu.Game.Migrations
+{
+ [DbContext(typeof(OsuDbContext))]
+ [Migration("20180529055154_RemoveUniqueHashConstraints")]
+ partial class RemoveUniqueHashConstraints
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.0.3-rtm-10026");
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ApproachRate");
+
+ b.Property("CircleSize");
+
+ b.Property("DrainRate");
+
+ b.Property("OverallDifficulty");
+
+ b.Property("SliderMultiplier");
+
+ b.Property("SliderTickRate");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapDifficulty");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AudioLeadIn");
+
+ b.Property("BaseDifficultyID");
+
+ b.Property("BeatDivisor");
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("Countdown");
+
+ b.Property("DistanceSpacing");
+
+ b.Property("GridSize");
+
+ b.Property("Hash");
+
+ b.Property("Hidden");
+
+ b.Property("LetterboxInBreaks");
+
+ b.Property("MD5Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapID");
+
+ b.Property("Path");
+
+ b.Property("RulesetID");
+
+ b.Property("SpecialStyle");
+
+ b.Property("StackLeniency");
+
+ b.Property("StarDifficulty");
+
+ b.Property("StoredBookmarks");
+
+ b.Property("TimelineZoom");
+
+ b.Property("Version");
+
+ b.Property("WidescreenStoryboard");
+
+ b.HasKey("ID");
+
+ b.HasIndex("BaseDifficultyID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("Hash");
+
+ b.HasIndex("MD5Hash");
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapID")
+ .IsUnique();
+
+ b.HasIndex("RulesetID");
+
+ b.ToTable("BeatmapInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Artist");
+
+ b.Property("ArtistUnicode");
+
+ b.Property("AudioFile");
+
+ b.Property("AuthorString")
+ .HasColumnName("Author");
+
+ b.Property("BackgroundFile");
+
+ b.Property("PreviewTime");
+
+ b.Property("Source");
+
+ b.Property("Tags");
+
+ b.Property("Title");
+
+ b.Property("TitleUnicode");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapMetadata");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("FileInfoID");
+
+ b.ToTable("BeatmapSetFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapSetID");
+
+ b.Property("Protected");
+
+ b.HasKey("ID");
+
+ b.HasIndex("DeletePending");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapSetID")
+ .IsUnique();
+
+ b.ToTable("BeatmapSetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntKey")
+ .HasColumnName("Key");
+
+ b.Property("RulesetID");
+
+ b.Property("StringValue")
+ .HasColumnName("Value");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("Settings");
+ });
+
+ modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntAction")
+ .HasColumnName("Action");
+
+ b.Property("KeysString")
+ .HasColumnName("Keys");
+
+ b.Property("RulesetID");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("IntAction");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("KeyBinding");
+ });
+
+ modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Hash");
+
+ b.Property("ReferenceCount");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("ReferenceCount");
+
+ b.ToTable("FileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Available");
+
+ b.Property("InstantiationInfo");
+
+ b.Property("Name");
+
+ b.Property("ShortName");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Available");
+
+ b.HasIndex("ShortName")
+ .IsUnique();
+
+ b.ToTable("RulesetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.Property("SkinInfoID");
+
+ b.HasKey("ID");
+
+ b.HasIndex("FileInfoID");
+
+ b.HasIndex("SkinInfoID");
+
+ b.ToTable("SkinFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Creator");
+
+ b.Property("DeletePending");
+
+ b.Property("Name");
+
+ b.HasKey("ID");
+
+ b.ToTable("SkinInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
+ .WithMany()
+ .HasForeignKey("BaseDifficultyID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
+ .WithMany("Beatmaps")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("Beatmaps")
+ .HasForeignKey("MetadataID");
+
+ b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
+ .WithMany()
+ .HasForeignKey("RulesetID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
+ .WithMany("Files")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("BeatmapSets")
+ .HasForeignKey("MetadataID");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Skinning.SkinInfo")
+ .WithMany("Files")
+ .HasForeignKey("SkinInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs
new file mode 100644
index 0000000000..fe8c983a3f
--- /dev/null
+++ b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs
@@ -0,0 +1,53 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+using System;
+using System.Collections.Generic;
+
+namespace osu.Game.Migrations
+{
+ public partial class RemoveUniqueHashConstraints : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropIndex(
+ name: "IX_BeatmapInfo_Hash",
+ table: "BeatmapInfo");
+
+ migrationBuilder.DropIndex(
+ name: "IX_BeatmapInfo_MD5Hash",
+ table: "BeatmapInfo");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_BeatmapInfo_Hash",
+ table: "BeatmapInfo",
+ column: "Hash");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_BeatmapInfo_MD5Hash",
+ table: "BeatmapInfo",
+ column: "MD5Hash");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropIndex(
+ name: "IX_BeatmapInfo_Hash",
+ table: "BeatmapInfo");
+
+ migrationBuilder.DropIndex(
+ name: "IX_BeatmapInfo_MD5Hash",
+ table: "BeatmapInfo");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_BeatmapInfo_Hash",
+ table: "BeatmapInfo",
+ column: "Hash",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_BeatmapInfo_MD5Hash",
+ table: "BeatmapInfo",
+ column: "MD5Hash",
+ unique: true);
+ }
+ }
+}
diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
index 2abbe7785f..d750d50b5b 100644
--- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
+++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
@@ -1,7 +1,11 @@
//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
using osu.Game.Database;
+using System;
namespace osu.Game.Migrations
{
@@ -12,7 +16,7 @@ namespace osu.Game.Migrations
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
+ .HasAnnotation("ProductVersion", "2.0.3-rtm-10026");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
@@ -27,9 +31,9 @@ namespace osu.Game.Migrations
b.Property("OverallDifficulty");
- b.Property("SliderMultiplier");
+ b.Property("SliderMultiplier");
- b.Property("SliderTickRate");
+ b.Property("SliderTickRate");
b.HasKey("ID");
@@ -91,11 +95,9 @@ namespace osu.Game.Migrations
b.HasIndex("BeatmapSetInfoID");
- b.HasIndex("Hash")
- .IsUnique();
+ b.HasIndex("Hash");
- b.HasIndex("MD5Hash")
- .IsUnique();
+ b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs
index 9af142b9e8..dfd181b98a 100644
--- a/osu.Game/Online/API/APIRequest.cs
+++ b/osu.Game/Online/API/APIRequest.cs
@@ -73,6 +73,7 @@ namespace osu.Game.Online.API
throw new TimeoutException(@"API request timeout hit");
WebRequest = CreateWebRequest();
+ WebRequest.Failed += Fail;
WebRequest.AllowRetryOnTimeout = false;
WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}");
diff --git a/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs b/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs
index 2661303652..44c1216959 100644
--- a/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs
+++ b/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs
@@ -30,6 +30,9 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"video")]
private bool hasVideo { get; set; }
+ [JsonProperty(@"storyboard")]
+ private bool hasStoryboard { get; set; }
+
[JsonProperty(@"status")]
private BeatmapSetOnlineStatus status { get; set; }
@@ -65,6 +68,7 @@ namespace osu.Game.Online.API.Requests
BPM = bpm,
Status = status,
HasVideo = hasVideo,
+ HasStoryboard = hasStoryboard,
Submitted = submitted,
Ranked = ranked,
LastUpdated = lastUpdated,
diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs
index f1c23e9e84..b076afbcdb 100644
--- a/osu.Game/Online/Multiplayer/Room.cs
+++ b/osu.Game/Online/Multiplayer/Room.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Collections.Generic;
using osu.Framework.Configuration;
using osu.Game.Beatmaps;
using osu.Game.Users;
@@ -12,9 +13,10 @@ namespace osu.Game.Online.Multiplayer
public Bindable Name = new Bindable();
public Bindable Host = new Bindable();
public Bindable Status = new Bindable();
+ public Bindable Availability = new Bindable();
public Bindable Type = new Bindable();
public Bindable Beatmap = new Bindable();
public Bindable MaxParticipants = new Bindable();
- public Bindable Participants = new Bindable();
+ public Bindable> Participants = new Bindable>();
}
}
diff --git a/osu.Game/Online/Multiplayer/RoomAvailability.cs b/osu.Game/Online/Multiplayer/RoomAvailability.cs
new file mode 100644
index 0000000000..6c154207ff
--- /dev/null
+++ b/osu.Game/Online/Multiplayer/RoomAvailability.cs
@@ -0,0 +1,18 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.ComponentModel;
+
+namespace osu.Game.Online.Multiplayer
+{
+ public enum RoomAvailability
+ {
+ Public,
+
+ [Description(@"Friends Only")]
+ FriendsOnly,
+
+ [Description(@"Invite Only")]
+ InviteOnly,
+ }
+}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index ec93b1ae46..36c76851c6 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -77,8 +77,7 @@ namespace osu.Game
public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight;
- public readonly BindableBool HideOverlaysOnEnter = new BindableBool();
- public readonly BindableBool AllowOpeningOverlays = new BindableBool(true);
+ public readonly Bindable OverlayActivationMode = new Bindable();
private OsuScreen screenStack;
@@ -94,6 +93,8 @@ namespace osu.Game
private SettingsOverlay settings;
+ private readonly List overlays = new List();
+
// todo: move this to SongSelect once Screen has the ability to unsuspend.
public readonly Bindable> SelectedMods = new Bindable>(new List());
@@ -106,6 +107,17 @@ namespace osu.Game
public void ToggleDirect() => direct.ToggleVisibility();
+ ///
+ /// Close all game-wide overlays.
+ ///
+ /// Whether the toolbar should also be hidden.
+ public void CloseAllOverlays(bool toolbar = true)
+ {
+ foreach (var o in overlays)
+ o.State = Visibility.Hidden;
+ if (toolbar) Toolbar.State = Visibility.Hidden;
+ }
+
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
@@ -212,7 +224,7 @@ namespace osu.Game
protected override void LoadComplete()
{
- // this needs to be cached before base.LoadComplete as it is used by CursorOverrideContainer.
+ // this needs to be cached before base.LoadComplete as it is used by MenuCursorContainer.
dependencies.Cache(screenshotManager = new ScreenshotManager());
base.LoadComplete();
@@ -220,7 +232,7 @@ namespace osu.Game
// The next time this is updated is in UpdateAfterChildren, which occurs too late and results
// in the cursor being shown for a few frames during the intro.
// This prevents the cursor from showing until we have a screen with CursorVisible = true
- CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
+ MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
// hook up notifications to components.
SkinManager.PostNotification = n => notifications?.Post(n);
@@ -251,7 +263,7 @@ namespace osu.Game
Depth = -5,
OnHome = delegate
{
- hideAllOverlays();
+ CloseAllOverlays(false);
intro?.ChildScreen?.MakeCurrent();
},
}, overlayContent.Add);
@@ -308,6 +320,8 @@ namespace osu.Game
// ensure only one of these overlays are open at once.
var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
+ overlays.AddRange(singleDisplayOverlays);
+
foreach (var overlay in singleDisplayOverlays)
{
overlay.StateChanged += state =>
@@ -323,6 +337,8 @@ namespace osu.Game
}
var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };
+ overlays.AddRange(singleDisplaySideOverlays);
+
foreach (var overlay in singleDisplaySideOverlays)
{
overlay.StateChanged += state =>
@@ -339,6 +355,8 @@ namespace osu.Game
// eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time.
var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile };
+ overlays.AddRange(informationalOverlays);
+
foreach (var overlay in informationalOverlays)
{
overlay.StateChanged += state =>
@@ -353,6 +371,11 @@ namespace osu.Game
};
}
+ OverlayActivationMode.ValueChanged += v =>
+ {
+ if (v != OverlayActivation.All) CloseAllOverlays();
+ };
+
void updateScreenOffset()
{
float offset = 0;
@@ -367,21 +390,6 @@ namespace osu.Game
settings.StateChanged += _ => updateScreenOffset();
notifications.StateChanged += _ => updateScreenOffset();
-
- notifications.Enabled.BindTo(AllowOpeningOverlays);
-
- HideOverlaysOnEnter.ValueChanged += hide =>
- {
- //central game screen change logic.
- if (hide)
- {
- hideAllOverlays();
- musicController.State = Visibility.Hidden;
- Toolbar.State = Visibility.Hidden;
- }
- else
- Toolbar.State = Visibility.Visible;
- };
}
private void forwardLoggedErrorsToNotifications()
@@ -498,16 +506,6 @@ namespace osu.Game
private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig;
- private void hideAllOverlays()
- {
- settings.State = Visibility.Hidden;
- chat.State = Visibility.Hidden;
- direct.State = Visibility.Hidden;
- social.State = Visibility.Hidden;
- userProfile.State = Visibility.Hidden;
- notifications.State = Visibility.Hidden;
- }
-
protected override bool OnExiting()
{
if (screenStack.ChildScreen == null) return false;
@@ -548,7 +546,7 @@ namespace osu.Game
mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
- CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
+ MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
}
private void screenAdded(Screen newScreen)
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 487cb50c9a..6269fcfa76 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -10,7 +10,6 @@ using System.Reflection;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Configuration;
-using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Stores;
@@ -31,6 +30,7 @@ using osu.Game.IO;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
+using DebugUtils = osu.Game.Utils.DebugUtils;
namespace osu.Game
{
@@ -57,9 +57,7 @@ namespace osu.Game
protected SettingsStore SettingsStore;
- protected CursorOverrideContainer CursorOverrideContainer;
-
- protected override string MainResourceFile => @"osu.Game.Resources.dll";
+ protected MenuCursorContainer MenuCursorContainer;
private Container content;
@@ -100,6 +98,8 @@ namespace osu.Game
[BackgroundDependencyLoader]
private void load()
{
+ Resources.AddStore(new DllResourceStore(@"osu.Game.Resources.dll"));
+
dependencies.Cache(contextFactory = new DatabaseContextFactory(Host));
dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))));
@@ -191,14 +191,14 @@ namespace osu.Game
GlobalActionContainer globalBinding;
- CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both };
- CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this)
+ MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both };
+ MenuCursorContainer.Child = globalBinding = new GlobalActionContainer(this)
{
RelativeSizeAxes = Axes.Both,
- Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both }
+ Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }
};
- base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer });
+ base.Content.Add(new DrawSizePreservingFillContainer { Child = MenuCursorContainer });
KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding);
@@ -208,19 +208,21 @@ namespace osu.Game
{
try
{
- using (var db = contextFactory.GetForWrite())
+ using (var db = contextFactory.GetForWrite(false))
db.Context.Migrate();
}
- catch (MigrationFailedException e)
+ catch (Exception e)
{
Logger.Error(e.InnerException ?? e, "Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database);
// if we failed, let's delete the database and start fresh.
// todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this.
contextFactory.ResetDatabase();
+
Logger.Log("Database purged successfully.", LoggingTarget.Database, LogLevel.Important);
- using (var db = contextFactory.GetForWrite())
+ // only run once more, then hard bail.
+ using (var db = contextFactory.GetForWrite(false))
db.Context.Migrate();
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
index 66e3148065..398518ef2b 100644
--- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
+++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
@@ -49,8 +49,8 @@ namespace osu.Game.Overlays.BeatmapSet
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")
+ new Field("mapped by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"),
+ new Field("submitted on", online.Submitted.ToString(@"MMMM d, yyyy"), @"Exo2.0-Bold")
{
Margin = new MarginPadding { Top = 5 },
},
@@ -58,11 +58,11 @@ namespace osu.Game.Overlays.BeatmapSet
if (online.Ranked.HasValue)
{
- fields.Add(new Field("ranked on ", online.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
+ fields.Add(new Field("ranked on", online.Ranked.Value.ToString(@"MMMM 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"));
+ fields.Add(new Field("last updated on", online.LastUpdated.Value.ToString(@"MMMM d, yyyy"), @"Exo2.0-Bold"));
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs
index 9b25d61f58..89c141ef17 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.Graphics.UserInterface;
using osu.Game.Overlays.BeatmapSet.Buttons;
using OpenTK;
using OpenTK.Graphics;
@@ -25,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapSet
private const float buttons_spacing = 5;
private readonly Box tabsBg;
- private readonly Container coverContainer;
+ private readonly UpdateableBeatmapSetCover cover;
private readonly OsuSpriteText title, artist;
private readonly Container noVideoButtons;
private readonly FillFlowContainer videoButtons;
@@ -35,7 +36,6 @@ namespace osu.Game.Overlays.BeatmapSet
public Details Details;
private BeatmapManager beatmaps;
- private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker;
@@ -61,8 +61,8 @@ namespace osu.Game.Overlays.BeatmapSet
title.Text = BeatmapSet?.Metadata.Title ?? string.Empty;
artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty;
onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
+ cover.BeatmapSet = BeatmapSet;
- cover?.FadeOut(400, Easing.Out);
if (BeatmapSet != null)
{
downloadButtonsContainer.FadeIn(transition_duration);
@@ -70,19 +70,6 @@ namespace osu.Game.Overlays.BeatmapSet
noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
-
- coverContainer.Add(cover = new DelayedLoadWrapper(
- new BeatmapSetCover(BeatmapSet)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fill,
- OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
- }, 300)
- {
- RelativeSizeAxes = Axes.Both,
- });
}
else
{
@@ -93,6 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet
public Header()
{
+ ExternalLinkButton externalLink;
RelativeSizeAxes = Axes.X;
Height = 400;
Masking = true;
@@ -128,12 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- coverContainer = new Container
+ cover = new UpdateableBeatmapSetCover
{
RelativeSizeAxes = Axes.Both,
},
@@ -160,10 +143,24 @@ namespace osu.Game.Overlays.BeatmapSet
Height = 113,
Child = Picker = new BeatmapPicker(),
},
- title = new OsuSpriteText
+ new FillFlowContainer
{
- Font = @"Exo2.0-BoldItalic",
- TextSize = 37,
+ Direction = FillDirection.Horizontal,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ title = new OsuSpriteText
+ {
+ Font = @"Exo2.0-BoldItalic",
+ TextSize = 37,
+ },
+ externalLink = new ExternalLinkButton
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font
+ },
+ }
},
artist = new OsuSpriteText
{
@@ -247,6 +244,7 @@ namespace osu.Game.Overlays.BeatmapSet
};
Picker.Beatmap.ValueChanged += b => Details.Beatmap = b;
+ Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}";
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs
index cd0b7386e8..53216ad666 100644
--- a/osu.Game/Overlays/BeatmapSet/Info.cs
+++ b/osu.Game/Overlays/BeatmapSet/Info.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet
private const float metadata_width = 225;
private const float spacing = 20;
- private readonly MetadataSection description, source, tags;
+ private readonly MetadataSection source, tags;
private readonly Box successRateBackground;
private readonly SuccessRate successRate;
@@ -83,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet
Child = new Container
{
RelativeSizeAxes = Axes.Both,
- Child = description = new MetadataSection("Description"),
+ Child = new MetadataSection("Description"),
},
},
new Container
@@ -135,8 +135,6 @@ namespace osu.Game.Overlays.BeatmapSet
private void load(OsuColour colours)
{
successRateBackground.Colour = colours.GrayE;
- source.TextColour = description.TextColour = colours.Gray5;
- tags.TextColour = colours.BlueDark;
updateDisplay();
}
@@ -195,7 +193,7 @@ namespace osu.Game.Overlays.BeatmapSet
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- header.Colour = colours.Gray5;
+ header.Colour = textFlow.Colour = colours.Gray5;
}
}
}
diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs
index 9715615d14..ed4630a8e7 100644
--- a/osu.Game/Overlays/Direct/DirectGridPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs
@@ -149,7 +149,7 @@ namespace osu.Game.Overlays.Direct
{
new OsuSpriteText
{
- Text = $"from {SetInfo.Metadata.Source}",
+ Text = $"{SetInfo.Metadata.Source}",
TextSize = 14,
Shadow = false,
Colour = colours.Gray5,
@@ -195,18 +195,18 @@ namespace osu.Game.Overlays.Direct
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
},
},
- playButton = new PlayButton(SetInfo)
- {
- Margin = new MarginPadding { Top = 5, Left = 10 },
- Size = new Vector2(30),
- Alpha = 0,
- },
statusContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 5, Left = 5 },
Spacing = new Vector2(5),
},
+ playButton = new PlayButton(SetInfo)
+ {
+ Margin = new MarginPadding { Top = 5, Left = 10 },
+ Size = new Vector2(30),
+ Alpha = 0,
+ },
});
if (SetInfo.OnlineInfo?.HasVideo ?? false)
@@ -214,24 +214,31 @@ namespace osu.Game.Overlays.Direct
statusContainer.Add(new IconPill(FontAwesome.fa_film));
}
+ if (SetInfo.OnlineInfo?.HasStoryboard ?? false)
+ {
+ statusContainer.Add(new IconPill(FontAwesome.fa_image));
+ }
+
statusContainer.Add(new BeatmapSetOnlineStatusPill(12, new MarginPadding { Horizontal = 10, Vertical = 5 })
{
Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None,
});
+
+ PreviewPlaying.ValueChanged += _ => updateStatusContainer();
}
protected override bool OnHover(InputState state)
{
- statusContainer.FadeOut(120, Easing.InOutQuint);
-
+ updateStatusContainer();
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
-
- statusContainer.FadeIn(120, Easing.InOutQuint);
+ updateStatusContainer();
}
+
+ private void updateStatusContainer() => statusContainer.FadeTo(IsHovered || PreviewPlaying ? 0 : 1, 120, Easing.InOutQuint);
}
}
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index cc0123dabc..df784252ce 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -27,8 +27,6 @@ namespace osu.Game.Overlays.Direct
{
public readonly BeatmapSetInfo SetInfo;
- protected Box BlackBackground;
-
private const double hover_transition_time = 400;
private Container content;
@@ -81,12 +79,6 @@ namespace osu.Game.Overlays.Direct
EdgeEffect = edgeEffectNormal,
Children = new[]
{
- // temporary blackness until the actual background loads.
- BlackBackground = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
CreateBackground(),
progressBar = new ProgressBar
{
@@ -215,21 +207,10 @@ namespace osu.Game.Overlays.Direct
return icons;
}
- protected Drawable CreateBackground() => new DelayedLoadWrapper(
- new BeatmapSetCover(SetInfo)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fill,
- OnLoadComplete = d =>
- {
- d.FadeInFromZero(400, Easing.Out);
- BlackBackground.Delay(400).FadeOut();
- },
- }, 300)
+ protected Drawable CreateBackground() => new UpdateableBeatmapSetCover
{
RelativeSizeAxes = Axes.Both,
+ BeatmapSet = SetInfo,
};
public class Statistic : FillFlowContainer
diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs
index 4913b11ae1..131083c6ff 100644
--- a/osu.Game/Overlays/Direct/PlayButton.cs
+++ b/osu.Game/Overlays/Direct/PlayButton.cs
@@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Direct
return;
}
- icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
+ icon.Icon = playing ? FontAwesome.fa_stop : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
if (playing)
diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs
index a0e4bf1a39..7e2f6f5891 100644
--- a/osu.Game/Overlays/HoldToConfirmOverlay.cs
+++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs
@@ -1,11 +1,10 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.Containers;
using OpenTK.Graphics;
namespace osu.Game.Overlays
@@ -14,22 +13,10 @@ namespace osu.Game.Overlays
/// An overlay which will display a black screen that dims over a period before confirming an exit action.
/// Action is BYO (derived class will need to call and from a user event).
///
- public abstract class HoldToConfirmOverlay : Container
+ public abstract class HoldToConfirmOverlay : HoldToConfirmContainer
{
- public Action Action;
-
private Box overlay;
- private const int activate_delay = 400;
- private const int fadeout_delay = 200;
-
- private bool fired;
-
- ///
- /// Whether the overlay should be allowed to return from a fired state.
- ///
- protected virtual bool AllowMultipleFires => false;
-
[BackgroundDependencyLoader]
private void load()
{
@@ -45,22 +32,8 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.Both,
}
};
- }
- protected void BeginConfirm()
- {
- if (!AllowMultipleFires && fired) return;
- overlay.FadeIn(activate_delay * (1 - overlay.Alpha), Easing.Out).OnComplete(_ =>
- {
- Action?.Invoke();
- fired = true;
- });
- }
-
- protected void AbortConfirm()
- {
- if (!AllowMultipleFires && fired) return;
- overlay.FadeOut(fadeout_delay, Easing.Out);
+ Progress.ValueChanged += v => overlay.Alpha = (float)v;
}
}
}
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
index 7406a9ec4f..ffe1560627 100644
--- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
@@ -180,7 +180,7 @@ namespace osu.Game.Overlays.KeyBinding
return true;
}
- protected override bool OnWheel(InputState state)
+ protected override bool OnScroll(InputState state)
{
if (HasFocus)
{
@@ -192,7 +192,7 @@ namespace osu.Game.Overlays.KeyBinding
}
}
- return base.OnWheel(state);
+ return base.OnScroll(state);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index 632c00d1fd..f1624721da 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Mods
protected Color4 LowMultiplierColour, HighMultiplierColour;
protected readonly TriangleButton DeselectAllButton;
- protected readonly OsuSpriteText MultiplierLabel;
+ protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel;
private readonly FillFlowContainer footerContainer;
protected override bool BlockPassThroughKeyboard => false;
@@ -58,6 +58,7 @@ namespace osu.Game.Overlays.Mods
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
+ UnrankedLabel.Colour = colours.Blue;
if (osu != null)
Ruleset.BindTo(osu.Ruleset);
@@ -99,15 +100,14 @@ namespace osu.Game.Overlays.Mods
}
MultiplierLabel.Text = $"{multiplier:N2}x";
- if (!ranked)
- MultiplierLabel.Text += " (Unranked)";
-
if (multiplier > 1.0)
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
else if (multiplier < 1.0)
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
else
MultiplierLabel.FadeColour(Color4.White, 200);
+
+ UnrankedLabel.FadeTo(ranked ? 0 : 1, 200);
}
protected override void PopOut()
@@ -352,23 +352,33 @@ namespace osu.Game.Overlays.Mods
},
new OsuSpriteText
{
- Text = @"Score Multiplier: ",
+ Text = @"Score Multiplier:",
TextSize = 30,
- Shadow = true,
Margin = new MarginPadding
{
- Top = 5
+ Top = 5,
+ Right = 10
}
},
MultiplierLabel = new OsuSpriteText
{
Font = @"Exo2.0-Bold",
TextSize = 30,
- Shadow = true,
Margin = new MarginPadding
{
Top = 5
}
+ },
+ UnrankedLabel = new OsuSpriteText
+ {
+ Font = @"Exo2.0-Bold",
+ Text = @"(Unranked)",
+ TextSize = 30,
+ Margin = new MarginPadding
+ {
+ Top = 5,
+ Left = 10
+ }
}
}
}
diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs
index 8c8ff89420..d4bc8c5b27 100644
--- a/osu.Game/Overlays/Music/PlaylistList.cs
+++ b/osu.Game/Overlays/Music/PlaylistList.cs
@@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Music
private void updateSelectedSet()
{
foreach (PlaylistItem s in items.Children)
- s.Selected = s.BeatmapSetInfo.ID == beatmapBacking.Value.BeatmapSetInfo.ID;
+ s.Selected = s.BeatmapSetInfo.ID == beatmapBacking.Value.BeatmapSetInfo?.ID;
}
public string SearchTerm
diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs
index 76c2222f8b..085a44ecba 100644
--- a/osu.Game/Overlays/Music/PlaylistOverlay.cs
+++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs
@@ -79,7 +79,10 @@ namespace osu.Game.Overlays.Music
{
BeatmapInfo beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
if (beatmap != null)
+ {
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(beatmap);
+ beatmapBacking.Value.Track.Restart();
+ }
};
}
@@ -109,6 +112,7 @@ namespace osu.Game.Overlays.Music
}
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(set.Beatmaps.First());
+ beatmapBacking.Value.Track.Restart();
}
}
diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs
index 09b6022ac5..3dc8f5ec15 100644
--- a/osu.Game/Overlays/NotificationOverlay.cs
+++ b/osu.Game/Overlays/NotificationOverlay.cs
@@ -22,11 +22,6 @@ namespace osu.Game.Overlays
public const float TRANSITION_LENGTH = 600;
- ///
- /// Whether posted notifications should be processed.
- ///
- public readonly BindableBool Enabled = new BindableBool(true);
-
private FlowContainer sections;
///
@@ -34,27 +29,6 @@ namespace osu.Game.Overlays
///
public Func GetToolbarHeight;
- public NotificationOverlay()
- {
- ScheduledDelegate notificationsEnabler = null;
- Enabled.ValueChanged += v =>
- {
- if (!IsLoaded)
- {
- processingPosts = v;
- return;
- }
-
- notificationsEnabler?.Cancel();
-
- if (v)
- // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
- notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000);
- else
- processingPosts = false;
- };
- }
-
[BackgroundDependencyLoader]
private void load()
{
@@ -103,6 +77,29 @@ namespace osu.Game.Overlays
};
}
+ private ScheduledDelegate notificationsEnabler;
+ private void updateProcessingMode()
+ {
+ bool enabled = OverlayActivationMode == OverlayActivation.All || State == Visibility.Visible;
+
+ notificationsEnabler?.Cancel();
+
+ if (enabled)
+ // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
+ notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State == Visibility.Visible ? 0 : 1000);
+ else
+ processingPosts = false;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ StateChanged += _ => updateProcessingMode();
+ OverlayActivationMode.ValueChanged += _ => updateProcessingMode();
+ OverlayActivationMode.TriggerChange();
+ }
+
private int totalCount => sections.Select(c => c.DisplayedCount).Sum();
private int unreadCount => sections.Select(c => c.UnreadCount).Sum();
diff --git a/osu.Game/Overlays/OverlayActivation.cs b/osu.Game/Overlays/OverlayActivation.cs
new file mode 100644
index 0000000000..da4e153ce9
--- /dev/null
+++ b/osu.Game/Overlays/OverlayActivation.cs
@@ -0,0 +1,12 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Overlays
+{
+ public enum OverlayActivation
+ {
+ Disabled,
+ UserTriggered,
+ All
+ }
+}
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 4c411b3210..067144c26e 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using System.Diagnostics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
@@ -10,13 +9,13 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
@@ -105,11 +104,28 @@ namespace osu.Game.Overlays.Profile
Y = -75,
Size = new Vector2(25, 25)
},
- new ProfileLink(user)
+ new FillFlowContainer
{
+ Direction = FillDirection.Horizontal,
+ AutoSizeAxes = Axes.Both,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -48,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = user.Username,
+ Font = @"Exo2.0-RegularItalic",
+ TextSize = 30,
+ },
+ new ExternalLinkButton($@"https://osu.ppy.sh/users/{user.Id}")
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Margin = new MarginPadding { Left = 3, Bottom = 3 }, //To better lineup with the font
+ },
+ }
},
countryFlag = new DrawableFlag(user.Country)
{
@@ -455,28 +471,6 @@ namespace osu.Game.Overlays.Profile
infoTextRight.NewLine();
}
- private class ProfileLink : OsuHoverContainer, IHasTooltip
- {
- public string TooltipText => "View Profile in Browser";
-
- public override bool HandleMouseInput => true;
-
- public ProfileLink(User user)
- {
- Action = () => Process.Start($@"https://osu.ppy.sh/users/{user.Id}");
-
- AutoSizeAxes = Axes.Both;
-
- Child = new OsuSpriteText
- {
- Text = user.Username,
- Font = @"Exo2.0-RegularItalic",
- TextSize = 30,
- };
- }
- }
-
-
private class GradeBadge : Container
{
private const float width = 50;
diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs
index be8e09105b..0a2b2fe121 100644
--- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs
+++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs
@@ -24,19 +24,13 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
this.playCount = playCount;
}
- protected override Drawable CreateLeftVisual() => new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List)
+ protected override Drawable CreateLeftVisual() => new UpdateableBeatmapSetCover
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- FillMode = FillMode.Fit,
- RelativeSizeAxes = Axes.Both,
- OnLoadComplete = d => d.FadeInFromZero(500, Easing.OutQuint)
- })
- {
- Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
- RelativeSizeAxes = Axes.None,
+ Origin = Anchor.CentreLeft,
Size = new Vector2(80, 50),
+ BeatmapSet = beatmap.BeatmapSet,
+ CoverType = BeatmapSetCoverType.List,
};
[BackgroundDependencyLoader(true)]
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs
new file mode 100644
index 0000000000..a9cefa81da
--- /dev/null
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Game.Configuration;
+
+namespace osu.Game.Overlays.Settings.Sections.Gameplay
+{
+ public class ModsSettings : SettingsSubsection
+ {
+ protected override string Header => "Mods";
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ Children = new[]
+ {
+ new SettingsCheckbox
+ {
+ LabelText = "Increase visibility of first object with \"Hidden\" mod",
+ Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility)
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
index 3851a73901..8add0b01ec 100644
--- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
+++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
@@ -21,7 +21,8 @@ namespace osu.Game.Overlays.Settings.Sections
{
new GeneralSettings(),
new SongSelectSettings(),
- new ScrollingSettings()
+ new ScrollingSettings(),
+ new ModsSettings(),
};
}
diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs
index 900f03fe7b..909fc20446 100644
--- a/osu.Game/Overlays/Settings/SettingsFooter.cs
+++ b/osu.Game/Overlays/Settings/SettingsFooter.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
-using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
@@ -12,6 +11,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using OpenTK;
using OpenTK.Graphics;
+using DebugUtils = osu.Game.Utils.DebugUtils;
namespace osu.Game.Overlays.Settings
{
diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs
index 424a457110..48d0674b3d 100644
--- a/osu.Game/Overlays/Toolbar/Toolbar.cs
+++ b/osu.Game/Overlays/Toolbar/Toolbar.cs
@@ -10,6 +10,8 @@ using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
namespace osu.Game.Overlays.Toolbar
{
@@ -29,6 +31,8 @@ namespace osu.Game.Overlays.Toolbar
private const float alpha_hovering = 0.8f;
private const float alpha_normal = 0.6f;
+ private readonly Bindable overlayActivationMode = new Bindable(OverlayActivation.All);
+
public Toolbar()
{
Children = new Drawable[]
@@ -76,6 +80,19 @@ namespace osu.Game.Overlays.Toolbar
Size = new Vector2(1, HEIGHT);
}
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuGame osuGame)
+ {
+ if (osuGame != null)
+ overlayActivationMode.BindTo(osuGame.OverlayActivationMode);
+
+ StateChanged += visibility =>
+ {
+ if (overlayActivationMode == OverlayActivation.Disabled)
+ State = Visibility.Hidden;
+ };
+ }
+
public class ToolbarBackground : Container
{
private readonly Box solidBackground;
diff --git a/osu.Game/Rulesets/Mods/IReadFromConfig.cs b/osu.Game/Rulesets/Mods/IReadFromConfig.cs
new file mode 100644
index 0000000000..93c9ae0c34
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/IReadFromConfig.cs
@@ -0,0 +1,15 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Configuration;
+
+namespace osu.Game.Rulesets.Mods
+{
+ ///
+ /// An interface for mods that require reading access to the osu! configuration.
+ ///
+ public interface IReadFromConfig
+ {
+ void ReadFromConfig(OsuConfigManager config);
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs
index 75f37b993d..7058d1bed6 100644
--- a/osu.Game/Rulesets/Mods/ModAutoplay.cs
+++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mods
public override string ShortenedName => "AT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
public override string Description => "Watch a perfect automated play through the song.";
- public override double ScoreMultiplier => 0;
+ public override double ScoreMultiplier => 1;
public bool AllowFail => false;
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
}
diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs
index b489a665d9..45da628ce8 100644
--- a/osu.Game/Rulesets/Mods/ModHidden.cs
+++ b/osu.Game/Rulesets/Mods/ModHidden.cs
@@ -1,16 +1,37 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Configuration;
+using osu.Game.Configuration;
using osu.Game.Graphics;
+using osu.Game.Rulesets.Objects.Drawables;
+using System.Collections.Generic;
+using System.Linq;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModHidden : Mod
+ public abstract class ModHidden : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
{
public override string Name => "Hidden";
public override string ShortenedName => "HD";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => true;
+
+ protected Bindable IncreaseFirstObjectVisibility = new Bindable();
+
+ public void ReadFromConfig(OsuConfigManager config)
+ {
+ IncreaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
+ }
+
+ public virtual void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ // todo: fix ordering of objects so we don't have to do this (#2740).
+ foreach (var d in drawables.Reverse().Skip(IncreaseFirstObjectVisibility ? 1 : 0))
+ d.ApplyCustomUpdateState += ApplyHiddenState;
+ }
+
+ protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) { }
}
}
diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs
index e4137254e6..0bd9becd78 100644
--- a/osu.Game/Rulesets/Mods/ModRelax.cs
+++ b/osu.Game/Rulesets/Mods/ModRelax.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Relax";
public override string ShortenedName => "RX";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax;
- public override double ScoreMultiplier => 0;
+ public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };
}
}
diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs
index 1847b63658..8d267f48e9 100644
--- a/osu.Game/Rulesets/RulesetStore.cs
+++ b/osu.Game/Rulesets/RulesetStore.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
+using osu.Framework.Logging;
using osu.Game.Database;
namespace osu.Game.Rulesets
@@ -114,8 +115,9 @@ namespace osu.Game.Rulesets
var assembly = Assembly.LoadFrom(file);
loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset)));
}
- catch (Exception)
+ catch (Exception e)
{
+ Logger.Error(e, "Failed to load ruleset");
}
}
}
diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index e42e74c245..384b71cccc 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -57,6 +57,7 @@ namespace osu.Game.Rulesets.UI
public abstract IEnumerable Objects { get; }
private readonly Lazy playfield;
+
///
/// The playfield.
///
@@ -130,7 +131,6 @@ namespace osu.Game.Rulesets.UI
HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null;
}
-
///
/// Creates the cursor. May be null if the doesn't provide a custom cursor.
///
@@ -194,6 +194,7 @@ namespace osu.Game.Rulesets.UI
protected override Container Content => content;
private Container content;
+ private IEnumerable mods;
///
/// Whether to assume the beatmap passed into this is for the current ruleset.
@@ -216,13 +217,10 @@ namespace osu.Game.Rulesets.UI
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
-
- // Add mods, should always be the last thing applied to give full control to mods
- applyMods(Mods);
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(OsuConfigManager config)
{
KeyBindingInputManager.Add(content = new Container
{
@@ -235,6 +233,9 @@ namespace osu.Game.Rulesets.UI
if (Cursor != null)
KeyBindingInputManager.Add(Cursor);
+ // Apply mods
+ applyMods(Mods, config);
+
loadObjects();
}
@@ -242,13 +243,16 @@ namespace osu.Game.Rulesets.UI
/// Applies the active mods to this RulesetContainer.
///
///
- private void applyMods(IEnumerable mods)
+ private void applyMods(IEnumerable mods, OsuConfigManager config)
{
if (mods == null)
return;
foreach (var mod in mods.OfType>())
mod.ApplyToRulesetContainer(this);
+
+ foreach (var mod in mods.OfType())
+ mod.ReadFromConfig(config);
}
public override void SetReplay(Replay replay)
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index 58a66a5224..b35616985a 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -121,8 +121,6 @@ namespace osu.Game.Rulesets.UI
///
private bool validState;
- protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
-
private bool isAttached => replayInputHandler != null && !UseParentState;
private const int max_catch_up_updates_per_frame = 50;
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index b657fe5597..b71d3aee18 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Screens.Edit
{
// TODO: should probably be done at a RulesetContainer level to share logic with Player.
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
- clock = new EditorClock(Beatmap.Value.Beatmap.ControlPointInfo, beatDivisor) { IsCoupled = false };
+ clock = new EditorClock(Beatmap, beatDivisor) { IsCoupled = false };
clock.ChangeSource(sourceClock);
dependencies.CacheAs(clock);
@@ -182,9 +182,9 @@ namespace osu.Game.Screens.Edit
LoadComponentAsync(currentScreen, screenContainer.Add);
}
- protected override bool OnWheel(InputState state)
+ protected override bool OnScroll(InputState state)
{
- if (state.Mouse.WheelDelta > 0)
+ if (state.Mouse.ScrollDelta.X + state.Mouse.ScrollDelta.Y > 0)
clock.SeekBackward(true);
else
clock.SeekForward(true);
diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs
index 67025f0620..72fb91e7df 100644
--- a/osu.Game/Screens/Edit/EditorClock.cs
+++ b/osu.Game/Screens/Edit/EditorClock.cs
@@ -3,10 +3,13 @@
using System;
using System.Linq;
+using osu.Framework.Configuration;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Screens.Edit.Screens.Compose;
+using OpenTK;
namespace osu.Game.Screens.Edit
{
@@ -15,15 +18,26 @@ namespace osu.Game.Screens.Edit
///
public class EditorClock : DecoupleableInterpolatingFramedClock
{
+ public readonly double TrackLength;
+
public ControlPointInfo ControlPointInfo;
private readonly BindableBeatDivisor beatDivisor;
- public EditorClock(ControlPointInfo controlPointInfo, BindableBeatDivisor beatDivisor)
+ public EditorClock(Bindable beatmap, BindableBeatDivisor beatDivisor)
+ {
+ this.beatDivisor = beatDivisor;
+
+ ControlPointInfo = beatmap.Value.Beatmap.ControlPointInfo;
+ TrackLength = beatmap.Value.Track.Length;
+ }
+
+ public EditorClock(ControlPointInfo controlPointInfo, double trackLength, BindableBeatDivisor beatDivisor)
{
this.beatDivisor = beatDivisor;
ControlPointInfo = controlPointInfo;
+ TrackLength = trackLength;
}
///
@@ -111,6 +125,8 @@ namespace osu.Game.Screens.Edit
if (seekTime > nextTimingPoint?.Time)
seekTime = nextTimingPoint.Time;
+ // Ensure the sought point is within the boundaries
+ seekTime = MathHelper.Clamp(seekTime, 0, TrackLength);
Seek(seekTime);
}
}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs
index 83aa86ba61..2902e74e00 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs
@@ -123,15 +123,15 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
///
private float? localZoomTarget;
- protected override bool OnWheel(InputState state)
+ protected override bool OnScroll(InputState state)
{
if (!state.Keyboard.ControlPressed)
- return base.OnWheel(state);
+ return base.OnScroll(state);
relativeContentZoomTarget = Content.ToLocalSpace(state.Mouse.NativeState.Position).X / Content.DrawSize.X;
localZoomTarget = ToLocalSpace(state.Mouse.NativeState.Position).X;
- Zoom += state.Mouse.WheelDelta;
+ Zoom += state.Mouse.ScrollDelta.Y;
return true;
}
diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs
index fb5c5ca84b..c3b3e747fd 100644
--- a/osu.Game/Screens/Loader.cs
+++ b/osu.Game/Screens/Loader.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
@@ -30,43 +29,48 @@ namespace osu.Game.Screens
{
base.LogoArriving(logo, resuming);
+ logo.BeatMatching = false;
logo.Triangles = false;
logo.Origin = Anchor.BottomRight;
logo.Anchor = Anchor.BottomRight;
logo.Position = new Vector2(-40);
logo.Scale = new Vector2(0.2f);
- logo.FadeInFromZero(5000, Easing.OutQuint);
- }
-
- private OsuScreen loadScreen;
- private ShaderPrecompiler precompiler;
-
- protected override void OnEntering(Screen last)
- {
- base.OnEntering(last);
-
- LoadComponentAsync(precompiler = new ShaderPrecompiler(loadIfReady), Add);
- LoadComponentAsync(loadScreen = showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(), s => loadIfReady());
- }
-
- private void loadIfReady()
- {
- if (ChildScreen == loadScreen) return;
-
- if (loadScreen.LoadState != LoadState.Ready)
- return;
-
- if (!precompiler.FinishedCompiling)
- return;
-
- Push(loadScreen);
+ logo.Delay(500).FadeInFromZero(1000, Easing.OutQuint);
}
protected override void LogoSuspending(OsuLogo logo)
{
base.LogoSuspending(logo);
- logo.FadeOut(100);
+ logo.FadeOut(logo.Alpha * 400);
+ }
+
+ private OsuScreen loadableScreen;
+ private ShaderPrecompiler precompiler;
+
+ protected virtual OsuScreen CreateLoadableScreen() => showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro();
+
+ protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler();
+
+ protected override void OnEntering(Screen last)
+ {
+ base.OnEntering(last);
+
+ LoadComponentAsync(precompiler = CreateShaderPrecompiler(), Add);
+ LoadComponentAsync(loadableScreen = CreateLoadableScreen());
+
+ checkIfLoaded();
+ }
+
+ private void checkIfLoaded()
+ {
+ if (loadableScreen.LoadState != LoadState.Ready || !precompiler.FinishedCompiling)
+ {
+ Schedule(checkIfLoaded);
+ return;
+ }
+
+ Push(loadableScreen);
}
[BackgroundDependencyLoader]
@@ -80,16 +84,10 @@ namespace osu.Game.Screens
///
public class ShaderPrecompiler : Drawable
{
- private readonly Action onLoaded;
private readonly List loadTargets = new List();
public bool FinishedCompiling { get; private set; }
- public ShaderPrecompiler(Action onLoaded)
- {
- this.onLoaded = onLoaded;
- }
-
[BackgroundDependencyLoader]
private void load(ShaderManager manager)
{
@@ -103,16 +101,17 @@ namespace osu.Game.Screens
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE));
}
+ protected virtual bool AllLoaded => loadTargets.All(s => s.Loaded);
+
protected override void Update()
{
base.Update();
// if our target is null we are done.
- if (loadTargets.All(s => s.Loaded))
+ if (AllLoaded)
{
FinishedCompiling = true;
Expire();
- onLoaded?.Invoke();
}
}
}
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index d1d388ae1f..42e25aad43 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -8,7 +8,6 @@ using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
-using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -17,6 +16,7 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Input.Bindings;
+using osu.Game.Overlays;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
@@ -27,9 +27,6 @@ namespace osu.Game.Screens.Menu
{
public event Action StateChanged;
- private readonly BindableBool hideOverlaysOnEnter = new BindableBool();
- private readonly BindableBool allowOpeningOverlays = new BindableBool();
-
public Action OnEdit;
public Action OnExit;
public Action OnDirect;
@@ -133,15 +130,12 @@ namespace osu.Game.Screens.Menu
buttonFlow.AddRange(buttonsTopLevel);
}
+ private OsuGame game;
+
[BackgroundDependencyLoader(true)]
private void load(AudioManager audio, OsuGame game)
{
- if (game != null)
- {
- hideOverlaysOnEnter.BindTo(game.HideOverlaysOnEnter);
- allowOpeningOverlays.BindTo(game.AllowOpeningOverlays);
- }
-
+ this.game = game;
sampleBack = audio.Sample.Get(@"Menu/button-back-select");
}
@@ -154,6 +148,8 @@ namespace osu.Game.Screens.Menu
case Key.Space:
logo?.TriggerOnClick(state);
return true;
+ case Key.Escape:
+ return goBack();
}
return false;
@@ -164,17 +160,22 @@ namespace osu.Game.Screens.Menu
switch (action)
{
case GlobalAction.Back:
- switch (State)
- {
- case MenuState.TopLevel:
- State = MenuState.Initial;
- return true;
- case MenuState.Play:
- backButton.TriggerOnClick();
- return true;
- default:
- return false;
- }
+ return goBack();
+ default:
+ return false;
+ }
+ }
+
+ private bool goBack()
+ {
+ switch (State)
+ {
+ case MenuState.TopLevel:
+ State = MenuState.Initial;
+ return true;
+ case MenuState.Play:
+ backButton.TriggerOnClick();
+ return true;
default:
return false;
}
@@ -247,9 +248,6 @@ namespace osu.Game.Screens.Menu
backButton.ContractStyle = 0;
settingsButton.ContractStyle = 0;
- if (state == MenuState.TopLevel)
- buttonArea.FinishTransforms(true);
-
updateLogoState(lastState);
using (buttonArea.BeginDelayedSequence(lastState == MenuState.Initial ? 150 : 0, true))
@@ -318,48 +316,60 @@ namespace osu.Game.Screens.Menu
{
if (logo == null) return;
- logoDelayedAction?.Cancel();
-
switch (state)
{
case MenuState.Exit:
case MenuState.Initial:
- logoTracking = false;
-
+ logoDelayedAction?.Cancel();
logoDelayedAction = Scheduler.AddDelayed(() =>
- {
- logo.ClearTransforms(targetMember: nameof(Position));
- logo.RelativePositionAxes = Axes.Both;
+ {
+ logoTracking = false;
- logo.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo);
- logo.ScaleTo(1, 800, Easing.OutExpo);
- }, 150);
+ if (game != null)
+ game.OverlayActivationMode.Value = state == MenuState.Exit ? OverlayActivation.Disabled : OverlayActivation.UserTriggered;
+ logo.ClearTransforms(targetMember: nameof(Position));
+ logo.RelativePositionAxes = Axes.Both;
+
+ logo.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo);
+ logo.ScaleTo(1, 800, Easing.OutExpo);
+ }, buttonArea.Alpha * 150);
break;
case MenuState.TopLevel:
case MenuState.Play:
- logo.ClearTransforms(targetMember: nameof(Position));
- logo.RelativePositionAxes = Axes.None;
-
switch (lastState)
{
case MenuState.TopLevel: // coming from toplevel to play
+ break;
case MenuState.Initial:
- logoTracking = false;
- logo.ScaleTo(0.5f, 200, Easing.In);
+ logo.ClearTransforms(targetMember: nameof(Position));
+ logo.RelativePositionAxes = Axes.None;
+
+ bool impact = logo.Scale.X > 0.6f;
+
+ if (lastState == MenuState.Initial)
+ logo.ScaleTo(0.5f, 200, Easing.In);
logo.MoveTo(logoTrackingPosition, lastState == MenuState.EnteringMode ? 0 : 200, Easing.In);
+ logoDelayedAction?.Cancel();
logoDelayedAction = Scheduler.AddDelayed(() =>
{
logoTracking = true;
- logo.Impact();
- hideOverlaysOnEnter.Value = false;
- allowOpeningOverlays.Value = true;
+ if (impact)
+ logo.Impact();
+
+ if (game != null)
+ {
+ game.OverlayActivationMode.Value = OverlayActivation.All;
+ game.Toolbar.State = Visibility.Visible;
+ }
}, 200);
break;
default:
+ logo.ClearTransforms(targetMember: nameof(Position));
+ logo.RelativePositionAxes = Axes.None;
logoTracking = true;
logo.ScaleTo(0.5f, 200, Easing.OutQuint);
break;
diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs
index 9a671cf780..0c70dbf570 100644
--- a/osu.Game/Screens/Menu/Disclaimer.cs
+++ b/osu.Game/Screens/Menu/Disclaimer.cs
@@ -9,6 +9,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
+using osu.Game.Overlays;
namespace osu.Game.Screens.Menu
{
@@ -19,6 +20,7 @@ namespace osu.Game.Screens.Menu
private Color4 iconColour;
protected override bool HideOverlaysOnEnter => true;
+ protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
public override bool CursorVisible => false;
diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs
index c174e2d470..c5bd345a31 100644
--- a/osu.Game/Screens/Menu/Intro.cs
+++ b/osu.Game/Screens/Menu/Intro.cs
@@ -15,6 +15,7 @@ using osu.Game.IO.Archives;
using osu.Game.Screens.Backgrounds;
using OpenTK;
using OpenTK.Graphics;
+using osu.Game.Overlays;
namespace osu.Game.Screens.Menu
{
@@ -32,7 +33,7 @@ namespace osu.Game.Screens.Menu
private SampleChannel seeya;
protected override bool HideOverlaysOnEnter => true;
- protected override bool AllowOpeningOverlays => false;
+ protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
public override bool CursorVisible => false;
@@ -79,31 +80,6 @@ namespace osu.Game.Screens.Menu
seeya = audio.Sample.Get(@"seeya");
}
- protected override void OnEntering(Screen last)
- {
- base.OnEntering(last);
-
- Game.Beatmap.Value = beatmap;
-
- if (menuVoice)
- welcome.Play();
-
- Scheduler.AddDelayed(delegate
- {
- // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu.
- if (menuMusic)
- track.Start();
-
- LoadComponentAsync(mainMenu = new MainMenu());
-
- Scheduler.AddDelayed(delegate
- {
- DidLoadMenu = true;
- Push(mainMenu);
- }, delay_step_one);
- }, delay_step_two);
- }
-
private const double delay_step_one = 2300;
private const double delay_step_two = 600;
@@ -113,6 +89,29 @@ namespace osu.Game.Screens.Menu
{
base.LogoArriving(logo, resuming);
+ if (!resuming)
+ {
+ Game.Beatmap.Value = beatmap;
+
+ if (menuVoice)
+ welcome.Play();
+
+ Scheduler.AddDelayed(delegate
+ {
+ // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu.
+ if (menuMusic)
+ track.Start();
+
+ LoadComponentAsync(mainMenu = new MainMenu());
+
+ Scheduler.AddDelayed(delegate
+ {
+ DidLoadMenu = true;
+ Push(mainMenu);
+ }, delay_step_one);
+ }, delay_step_two);
+ }
+
logo.RelativePositionAxes = Axes.Both;
logo.Colour = Color4.White;
logo.Ripple = false;
diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs
index d5f3b11467..cbdd8b4e8b 100644
--- a/osu.Game/Screens/Menu/MainMenu.cs
+++ b/osu.Game/Screens/Menu/MainMenu.cs
@@ -25,7 +25,6 @@ namespace osu.Game.Screens.Menu
private readonly ButtonSystem buttons;
protected override bool HideOverlaysOnEnter => buttons.State == MenuState.Initial;
- protected override bool AllowOpeningOverlays => buttons.State != MenuState.Initial;
protected override bool AllowBackButton => buttons.State != MenuState.Initial;
diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs
index fae3e72552..c321c98b24 100644
--- a/osu.Game/Screens/Menu/MenuSideFlashes.cs
+++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs
@@ -24,8 +24,8 @@ namespace osu.Game.Screens.Menu
private readonly Bindable beatmap = new Bindable();
- private readonly Box leftBox;
- private readonly Box rightBox;
+ private Box leftBox;
+ private Box rightBox;
private const float amplitude_dead_zone = 0.25f;
private const float alpha_multiplier = (1 - amplitude_dead_zone) / 0.55f;
@@ -42,27 +42,6 @@ namespace osu.Game.Screens.Menu
RelativeSizeAxes = Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- Children = new Drawable[]
- {
- leftBox = new Box
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- RelativeSizeAxes = Axes.Y,
- Width = box_width,
- Alpha = 0,
- Blending = BlendingMode.Additive,
- },
- rightBox = new Box
- {
- Anchor = Anchor.CentreRight,
- Origin = Anchor.CentreRight,
- RelativeSizeAxes = Axes.Y,
- Width = box_width,
- Alpha = 0,
- Blending = BlendingMode.Additive,
- }
- };
}
[BackgroundDependencyLoader]
@@ -72,10 +51,34 @@ namespace osu.Game.Screens.Menu
// linear colour looks better in this case, so let's use it for now.
Color4 gradientDark = colours.Blue.Opacity(0).ToLinear();
- Color4 gradientLight = colours.Blue.Opacity(0.3f).ToLinear();
+ Color4 gradientLight = colours.Blue.Opacity(0.6f).ToLinear();
- leftBox.Colour = ColourInfo.GradientHorizontal(gradientLight, gradientDark);
- rightBox.Colour = ColourInfo.GradientHorizontal(gradientDark, gradientLight);
+ Children = new Drawable[]
+ {
+ leftBox = new Box
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.Y,
+ Width = box_width * 2,
+ // align off-screen to make sure our edges don't become visible during parallax.
+ X = -box_width,
+ Alpha = 0,
+ Blending = BlendingMode.Additive,
+ Colour = ColourInfo.GradientHorizontal(gradientLight, gradientDark)
+ },
+ rightBox = new Box
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ RelativeSizeAxes = Axes.Y,
+ Width = box_width * 2,
+ X = box_width,
+ Alpha = 0,
+ Blending = BlendingMode.Additive,
+ Colour = ColourInfo.GradientHorizontal(gradientDark, gradientLight)
+ }
+ };
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs
index 42a8dbd5da..16482b0e48 100644
--- a/osu.Game/Screens/Menu/OsuLogo.cs
+++ b/osu.Game/Screens/Menu/OsuLogo.cs
@@ -64,6 +64,8 @@ namespace osu.Game.Screens.Menu
set { colourAndTriangles.FadeTo(value ? 1 : 0, transition_length, Easing.OutQuint); }
}
+ public bool BeatMatching = true;
+
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => logoContainer.ReceiveMouseInputAt(screenSpacePos);
public bool Ripple
@@ -264,6 +266,8 @@ namespace osu.Game.Screens.Menu
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
+ if (!BeatMatching) return;
+
lastBeatIndex = beatIndex;
var beatLength = timingPoint.BeatLength;
diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs
new file mode 100644
index 0000000000..42863754c5
--- /dev/null
+++ b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs
@@ -0,0 +1,79 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Localisation;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Screens.Multi.Components
+{
+ public class BeatmapTitle : FillFlowContainer
+ {
+ private readonly OsuSpriteText beatmapTitle, beatmapDash, beatmapArtist;
+
+ private LocalisationEngine localisation;
+
+ public float TextSize
+ {
+ set { beatmapTitle.TextSize = beatmapDash.TextSize = beatmapArtist.TextSize = value; }
+ }
+
+ private BeatmapInfo beatmap;
+
+ public BeatmapInfo Beatmap
+ {
+ set
+ {
+ if (value == beatmap) return;
+ beatmap = value;
+
+ if (IsLoaded)
+ updateText();
+ }
+ }
+
+ public BeatmapTitle()
+ {
+ AutoSizeAxes = Axes.Both;
+ Direction = FillDirection.Horizontal;
+
+ Children = new[]
+ {
+ beatmapTitle = new OsuSpriteText { Font = @"Exo2.0-BoldItalic", },
+ beatmapDash = new OsuSpriteText { Font = @"Exo2.0-BoldItalic", },
+ beatmapArtist = new OsuSpriteText { Font = @"Exo2.0-RegularItalic", },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(LocalisationEngine localisation)
+ {
+ this.localisation = localisation;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ updateText();
+ }
+
+ private void updateText()
+ {
+ if (beatmap == null)
+ {
+ beatmapTitle.Current = beatmapArtist.Current = null;
+ beatmapTitle.Text = "Changing map";
+ beatmapDash.Text = beatmapArtist.Text = string.Empty;
+ }
+ else
+ {
+ beatmapTitle.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title);
+ beatmapDash.Text = @" - ";
+ beatmapArtist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs
new file mode 100644
index 0000000000..78ffe01ef0
--- /dev/null
+++ b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs
@@ -0,0 +1,70 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Online.Multiplayer;
+using OpenTK;
+
+namespace osu.Game.Screens.Multi.Components
+{
+ public class BeatmapTypeInfo : FillFlowContainer
+ {
+ private readonly ModeTypeInfo modeTypeInfo;
+ private readonly BeatmapTitle beatmapTitle;
+ private readonly OsuSpriteText beatmapAuthor;
+
+ public BeatmapInfo Beatmap
+ {
+ set
+ {
+ modeTypeInfo.Beatmap = beatmapTitle.Beatmap = value;
+ beatmapAuthor.Text = value == null ? string.Empty : $"mapped by {value.Metadata.Author}";
+ }
+ }
+
+ public GameType Type
+ {
+ set { modeTypeInfo.Type = value; }
+ }
+
+ public BeatmapTypeInfo()
+ {
+ AutoSizeAxes = Axes.Both;
+ Direction = FillDirection.Horizontal;
+ LayoutDuration = 100;
+ Spacing = new Vector2(5f, 0f);
+
+ Children = new Drawable[]
+ {
+ modeTypeInfo = new ModeTypeInfo(),
+ new Container
+ {
+ AutoSizeAxes = Axes.X,
+ Height = 30,
+ Margin = new MarginPadding { Left = 5 },
+ Children = new Drawable[]
+ {
+ beatmapTitle = new BeatmapTitle(),
+ beatmapAuthor = new OsuSpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ TextSize = 14,
+ },
+ },
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ beatmapAuthor.Colour = colours.Gray9;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Components/DrawableRoom.cs
index 88a253d719..54bd0ae7cc 100644
--- a/osu.Game/Screens/Multi/Components/DrawableRoom.cs
+++ b/osu.Game/Screens/Multi/Components/DrawableRoom.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Collections.Generic;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
@@ -9,7 +10,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Localisation;
+using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
@@ -23,11 +24,11 @@ using OpenTK.Graphics;
namespace osu.Game.Screens.Multi.Components
{
- public class DrawableRoom : OsuClickableContainer, IStateful
+ public class DrawableRoom : OsuClickableContainer, IStateful, IFilterable
{
+ public const float SELECTION_BORDER_WIDTH = 4;
private const float corner_radius = 5;
- private const float selection_border_width = 4;
- private const float transition_duration = 100;
+ private const float transition_duration = 60;
private const float content_padding = 10;
private const float height = 100;
private const float side_strip_width = 5;
@@ -40,7 +41,7 @@ namespace osu.Game.Screens.Multi.Components
private readonly Bindable statusBind = new Bindable();
private readonly Bindable typeBind = new Bindable();
private readonly Bindable beatmapBind = new Bindable();
- private readonly Bindable participantsBind = new Bindable();
+ private readonly Bindable> participantsBind = new Bindable>();
public readonly Room Room;
@@ -62,6 +63,30 @@ namespace osu.Game.Screens.Multi.Components
}
}
+ public IEnumerable FilterTerms => new[] { Room.Name.Value };
+
+ private bool matchingFilter;
+ public bool MatchingFilter
+ {
+ get { return matchingFilter; }
+ set
+ {
+ matchingFilter = value;
+ this.FadeTo(MatchingFilter ? 1 : 0, 200);
+ }
+ }
+
+ private Action action;
+ public new Action Action
+ {
+ get { return action; }
+ set
+ {
+ action = value;
+ Enabled.Value = action != null;
+ }
+ }
+
public event Action StateChanged;
public DrawableRoom(Room room)
@@ -69,8 +94,8 @@ namespace osu.Game.Screens.Multi.Components
Room = room;
RelativeSizeAxes = Axes.X;
- Height = height + selection_border_width * 2;
- CornerRadius = corner_radius + selection_border_width / 2;
+ Height = height + SELECTION_BORDER_WIDTH * 2;
+ CornerRadius = corner_radius + SELECTION_BORDER_WIDTH / 2;
Masking = true;
// create selectionBox here so State can be set before being loaded
@@ -79,17 +104,16 @@ namespace osu.Game.Screens.Multi.Components
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
};
-
- Action += () => State = SelectionState.Selected;
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours, LocalisationEngine localisation)
+ private void load(OsuColour colours)
{
Box sideStrip;
- Container coverContainer;
- OsuSpriteText name, status, beatmapTitle, beatmapDash, beatmapArtist;
+ UpdateableBeatmapSetCover cover;
+ OsuSpriteText name, status;
ParticipantInfo participantInfo;
+ BeatmapTitle beatmapTitle;
ModeTypeInfo modeTypeInfo;
Children = new Drawable[]
@@ -98,7 +122,7 @@ namespace osu.Game.Screens.Multi.Components
new Container
{
RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding(selection_border_width),
+ Padding = new MarginPadding(SELECTION_BORDER_WIDTH),
Child = new Container
{
RelativeSizeAxes = Axes.Both,
@@ -122,24 +146,12 @@ namespace osu.Game.Screens.Multi.Components
RelativeSizeAxes = Axes.Y,
Width = side_strip_width,
},
- new Container
+ cover = new UpdateableBeatmapSetCover
{
Width = cover_width,
RelativeSizeAxes = Axes.Y,
Masking = true,
Margin = new MarginPadding { Left = side_strip_width },
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- coverContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- },
- },
},
new Container
{
@@ -181,30 +193,10 @@ namespace osu.Game.Screens.Multi.Components
TextSize = 14,
Font = @"Exo2.0-Bold",
},
- new FillFlowContainer
+ beatmapTitle = new BeatmapTitle
{
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Colour = colours.Gray9,
- Direction = FillDirection.Horizontal,
- Children = new[]
- {
- beatmapTitle = new OsuSpriteText
- {
- TextSize = 14,
- Font = @"Exo2.0-BoldItalic",
- },
- beatmapDash = new OsuSpriteText
- {
- TextSize = 14,
- Font = @"Exo2.0-BoldItalic",
- },
- beatmapArtist = new OsuSpriteText
- {
- TextSize = 14,
- Font = @"Exo2.0-RegularItalic",
- },
- },
+ TextSize = 14,
+ Colour = colours.Gray9
},
},
},
@@ -230,39 +222,14 @@ namespace osu.Game.Screens.Multi.Components
status.Text = s.Message;
foreach (Drawable d in new Drawable[] { selectionBox, sideStrip, status })
- d.FadeColour(s.GetAppropriateColour(colours), 100);
+ d.FadeColour(s.GetAppropriateColour(colours), transition_duration);
};
beatmapBind.ValueChanged += b =>
{
+ cover.BeatmapSet = b?.BeatmapSet;
+ beatmapTitle.Beatmap = b;
modeTypeInfo.Beatmap = b;
-
- if (b != null)
- {
- coverContainer.FadeIn(transition_duration);
-
- LoadComponentAsync(new BeatmapSetCover(b.BeatmapSet)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- FillMode = FillMode.Fill,
- OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
- }, coverContainer.Add);
-
- beatmapTitle.Current = localisation.GetUnicodePreference(b.Metadata.TitleUnicode, b.Metadata.Title);
- beatmapDash.Text = @" - ";
- beatmapArtist.Current = localisation.GetUnicodePreference(b.Metadata.ArtistUnicode, b.Metadata.Artist);
- }
- else
- {
- coverContainer.FadeOut(transition_duration);
-
- beatmapTitle.Current = null;
- beatmapArtist.Current = null;
-
- beatmapTitle.Text = "Changing map";
- beatmapDash.Text = beatmapArtist.Text = string.Empty;
- }
};
nameBind.BindTo(Room.Name);
@@ -272,5 +239,22 @@ namespace osu.Game.Screens.Multi.Components
beatmapBind.BindTo(Room.Beatmap);
participantsBind.BindTo(Room.Participants);
}
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ this.FadeInFromZero(transition_duration);
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ if (Enabled.Value)
+ {
+ Action?.Invoke(this);
+ State = SelectionState.Selected;
+ }
+
+ return true;
+ }
}
}
diff --git a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs
index e3aba685a7..e2d0268090 100644
--- a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs
+++ b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs
@@ -64,6 +64,7 @@ namespace osu.Game.Screens.Multi.Components
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5f, 0f),
+ LayoutDuration = 100,
Children = new[]
{
rulesetContainer = new Container
diff --git a/osu.Game/Screens/Multi/Components/ParticipantCount.cs b/osu.Game/Screens/Multi/Components/ParticipantCount.cs
new file mode 100644
index 0000000000..e7183cbd92
--- /dev/null
+++ b/osu.Game/Screens/Multi/Components/ParticipantCount.cs
@@ -0,0 +1,79 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Screens.Multi.Components
+{
+ public class ParticipantCount : FillFlowContainer
+ {
+ private const float text_size = 30;
+ private const float transition_duration = 100;
+
+ private readonly OsuSpriteText count, slash, maxText;
+
+ public int Count
+ {
+ set => count.Text = value.ToString();
+ }
+
+ private int? max;
+ public int? Max
+ {
+ get => max;
+ set
+ {
+ if (value == max) return;
+ max = value;
+
+ updateMax();
+ }
+ }
+
+ public ParticipantCount()
+ {
+ AutoSizeAxes = Axes.Both;
+ Direction = FillDirection.Horizontal;
+ LayoutDuration = transition_duration;
+
+ Children = new[]
+ {
+ count = new OsuSpriteText
+ {
+ TextSize = text_size,
+ Font = @"Exo2.0-Bold"
+ },
+ slash = new OsuSpriteText
+ {
+ Text = @"/",
+ TextSize = text_size,
+ Font = @"Exo2.0-Light"
+ },
+ maxText = new OsuSpriteText
+ {
+ TextSize = text_size,
+ Font = @"Exo2.0-Light"
+ },
+ };
+
+ updateMax();
+ }
+
+ private void updateMax()
+ {
+ if (Max == null)
+ {
+ slash.FadeOut(transition_duration);
+ maxText.FadeOut(transition_duration);
+ }
+ else
+ {
+ slash.FadeIn(transition_duration);
+ maxText.Text = Max.ToString();
+ maxText.FadeIn(transition_duration);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Components/RoomInspector.cs
index 3bd054b042..22bca1efc7 100644
--- a/osu.Game/Screens/Multi/Components/RoomInspector.cs
+++ b/osu.Game/Screens/Multi/Components/RoomInspector.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
@@ -10,7 +11,6 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
@@ -34,13 +34,15 @@ namespace osu.Game.Screens.Multi.Components
private readonly Bindable typeBind = new Bindable();
private readonly Bindable beatmapBind = new Bindable();
private readonly Bindable maxParticipantsBind = new Bindable();
- private readonly Bindable participantsBind = new Bindable();
+ private readonly Bindable> participantsBind = new Bindable>();
private OsuColour colours;
private Box statusStrip;
- private Container coverContainer;
- private FillFlowContainer topFlow, participantsFlow, participantNumbersFlow, infoPanelFlow;
+ private UpdateableBeatmapSetCover cover;
+ private ParticipantCount participantCount;
+ private FillFlowContainer topFlow, participantsFlow;
private OsuSpriteText name, status;
+ private BeatmapTypeInfo beatmapTypeInfo;
private ScrollContainer participantsScroll;
private ParticipantInfo participantInfo;
@@ -77,13 +79,10 @@ namespace osu.Game.Screens.Multi.Components
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours, LocalisationEngine localisation)
+ private void load(OsuColour colours)
{
this.colours = colours;
- ModeTypeInfo modeTypeInfo;
- OsuSpriteText participants, participantsSlash, maxParticipants, beatmapTitle, beatmapDash, beatmapArtist, beatmapAuthor;
-
Children = new Drawable[]
{
new Box
@@ -105,21 +104,9 @@ namespace osu.Game.Screens.Multi.Components
Masking = true,
Children = new Drawable[]
{
- new Container
+ cover = new UpdateableBeatmapSetCover
{
RelativeSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- coverContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- },
- },
},
new Box
{
@@ -132,32 +119,10 @@ namespace osu.Game.Screens.Multi.Components
Padding = new MarginPadding(20),
Children = new Drawable[]
{
- participantNumbersFlow = new FillFlowContainer
+ participantCount = new ParticipantCount
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- LayoutDuration = transition_duration,
- Children = new[]
- {
- participants = new OsuSpriteText
- {
- TextSize = 30,
- Font = @"Exo2.0-Bold"
- },
- participantsSlash = new OsuSpriteText
- {
- Text = @"/",
- TextSize = 30,
- Font = @"Exo2.0-Light"
- },
- maxParticipants = new OsuSpriteText
- {
- TextSize = 30,
- Font = @"Exo2.0-Light"
- },
- },
},
name = new OsuSpriteText
{
@@ -200,54 +165,7 @@ namespace osu.Game.Screens.Multi.Components
TextSize = 14,
Font = @"Exo2.0-Bold",
},
- infoPanelFlow = new FillFlowContainer
- {
- AutoSizeAxes = Axes.X,
- Height = 30,
- Direction = FillDirection.Horizontal,
- LayoutDuration = transition_duration,
- Spacing = new Vector2(5f, 0f),
- Children = new Drawable[]
- {
- modeTypeInfo = new ModeTypeInfo(),
- new Container
- {
- AutoSizeAxes = Axes.X,
- RelativeSizeAxes = Axes.Y,
- Margin = new MarginPadding { Left = 5 },
- Children = new[]
- {
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Children = new[]
- {
- beatmapTitle = new OsuSpriteText
- {
- Font = @"Exo2.0-BoldItalic",
- },
- beatmapDash = new OsuSpriteText
- {
- Font = @"Exo2.0-BoldItalic",
- },
- beatmapArtist = new OsuSpriteText
- {
- Font = @"Exo2.0-RegularItalic",
- },
- },
- },
- beatmapAuthor = new OsuSpriteText
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- TextSize = 14,
- Colour = colours.Gray9,
- },
- },
- },
- },
- },
+ beatmapTypeInfo = new BeatmapTypeInfo(),
},
},
},
@@ -285,61 +203,19 @@ namespace osu.Game.Screens.Multi.Components
nameBind.ValueChanged += n => name.Text = n;
hostBind.ValueChanged += h => participantInfo.Host = h;
- typeBind.ValueChanged += t => modeTypeInfo.Type = t;
+ typeBind.ValueChanged += t => beatmapTypeInfo.Type = t;
+ maxParticipantsBind.ValueChanged += m => participantCount.Max = m;
statusBind.ValueChanged += displayStatus;
beatmapBind.ValueChanged += b =>
{
- modeTypeInfo.Beatmap = b;
-
- if (b != null)
- {
- coverContainer.FadeIn(transition_duration);
-
- LoadComponentAsync(new BeatmapSetCover(b.BeatmapSet)
- {
- RelativeSizeAxes = Axes.Both,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- FillMode = FillMode.Fill,
- OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
- }, coverContainer.Add);
-
- beatmapTitle.Current = localisation.GetUnicodePreference(b.Metadata.TitleUnicode, b.Metadata.Title);
- beatmapDash.Text = @" - ";
- beatmapArtist.Current = localisation.GetUnicodePreference(b.Metadata.ArtistUnicode, b.Metadata.Artist);
- beatmapAuthor.Text = $"mapped by {b.Metadata.Author}";
- }
- else
- {
- coverContainer.FadeOut(transition_duration);
-
- beatmapTitle.Current = null;
- beatmapArtist.Current = null;
-
- beatmapTitle.Text = "Changing map";
- beatmapDash.Text = beatmapArtist.Text = beatmapAuthor.Text = string.Empty;
- }
- };
-
- maxParticipantsBind.ValueChanged += m =>
- {
- if (m == null)
- {
- participantsSlash.FadeOut(transition_duration);
- maxParticipants.FadeOut(transition_duration);
- }
- else
- {
- participantsSlash.FadeIn(transition_duration);
- maxParticipants.FadeIn(transition_duration);
- maxParticipants.Text = m.ToString();
- }
+ cover.BeatmapSet = b?.BeatmapSet;
+ beatmapTypeInfo.Beatmap = b;
};
participantsBind.ValueChanged += p =>
{
- participants.Text = p.Length.ToString();
+ participantCount.Count = p.Count();
participantInfo.Participants = p;
participantsFlow.ChildrenEnumerable = p.Select(u => new UserTile(u));
};
@@ -367,10 +243,10 @@ namespace osu.Game.Screens.Multi.Components
{
if (Room == null)
{
- coverContainer.FadeOut(transition_duration);
+ cover.BeatmapSet = null;
participantsFlow.FadeOut(transition_duration);
- participantNumbersFlow.FadeOut(transition_duration);
- infoPanelFlow.FadeOut(transition_duration);
+ participantCount.FadeOut(transition_duration);
+ beatmapTypeInfo.FadeOut(transition_duration);
name.FadeOut(transition_duration);
participantInfo.FadeOut(transition_duration);
@@ -379,8 +255,8 @@ namespace osu.Game.Screens.Multi.Components
else
{
participantsFlow.FadeIn(transition_duration);
- participantNumbersFlow.FadeIn(transition_duration);
- infoPanelFlow.FadeIn(transition_duration);
+ participantCount.FadeIn(transition_duration);
+ beatmapTypeInfo.FadeIn(transition_duration);
name.FadeIn(transition_duration);
participantInfo.FadeIn(transition_duration);
diff --git a/osu.Game/Screens/Multi/Header.cs b/osu.Game/Screens/Multi/Header.cs
index db8898495f..46610a36d8 100644
--- a/osu.Game/Screens/Multi/Header.cs
+++ b/osu.Game/Screens/Multi/Header.cs
@@ -10,6 +10,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.SearchableList;
+using osu.Game.Screens.Multi.Screens;
using OpenTK;
using OpenTK.Graphics;
@@ -19,7 +20,7 @@ namespace osu.Game.Screens.Multi
{
public const float HEIGHT = 121;
- private readonly OsuSpriteText screenTitle;
+ private readonly OsuSpriteText screenType;
private readonly HeaderBreadcrumbControl breadcrumbs;
public Header(Screen initialScreen)
@@ -66,7 +67,7 @@ namespace osu.Game.Screens.Multi
Text = "multiplayer ",
TextSize = 25,
},
- screenTitle = new OsuSpriteText
+ screenType = new OsuSpriteText
{
TextSize = 25,
Font = @"Exo2.0-Light",
@@ -85,14 +86,14 @@ namespace osu.Game.Screens.Multi
},
};
- breadcrumbs.Current.ValueChanged += s => screenTitle.Text = s.ToString();
+ breadcrumbs.Current.ValueChanged += s => screenType.Text = ((MultiplayerScreen)s).Type.ToLower();
breadcrumbs.Current.TriggerChange();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- screenTitle.Colour = colours.Yellow;
+ screenType.Colour = colours.Yellow;
breadcrumbs.StripColour = colours.Green;
}
diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs
index b3d393209c..7822fa68dc 100644
--- a/osu.Game/Screens/Multi/Multiplayer.cs
+++ b/osu.Game/Screens/Multi/Multiplayer.cs
@@ -8,7 +8,8 @@ using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
-using osu.Game.Screens.Multi.Screens;
+using osu.Game.Screens.Menu;
+using osu.Game.Screens.Multi.Screens.Lounge;
namespace osu.Game.Screens.Multi
{
@@ -25,7 +26,7 @@ namespace osu.Game.Screens.Multi
RelativeSizeAxes = Axes.Both,
};
- Lobby lobby;
+ Lounge lounge;
Children = new Drawable[]
{
new Container
@@ -52,12 +53,12 @@ namespace osu.Game.Screens.Multi
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = Header.HEIGHT },
- Child = lobby = new Lobby(),
+ Child = lounge = new Lounge(),
},
- new Header(lobby),
+ new Header(lounge),
};
- lobby.Exited += s => Exit();
+ lounge.Exited += s => Exit();
}
protected override void OnEntering(Screen last)
@@ -84,6 +85,13 @@ namespace osu.Game.Screens.Multi
waves.Hide();
}
+ protected override void LogoExiting(OsuLogo logo)
+ {
+ // the wave overlay transition takes longer than expected to run.
+ logo.Delay(WaveContainer.DISAPPEAR_DURATION / 2).FadeOut();
+ base.LogoExiting(logo);
+ }
+
private class MultiplayerWaveContainer : WaveContainer
{
protected override bool StartHidden => true;
diff --git a/osu.Game/Screens/Multi/Screens/Lobby.cs b/osu.Game/Screens/Multi/Screens/Lobby.cs
deleted file mode 100644
index dcda40e0d7..0000000000
--- a/osu.Game/Screens/Multi/Screens/Lobby.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-
-namespace osu.Game.Screens.Multi.Screens
-{
- public class Lobby : ScreenWhiteBox
- {
- protected override IEnumerable PossibleChildren => new[] {
- typeof(MatchCreate),
- typeof(Match)
- };
- }
-}
diff --git a/osu.Game/Screens/Multi/Screens/Lounge/FilterControl.cs b/osu.Game/Screens/Multi/Screens/Lounge/FilterControl.cs
new file mode 100644
index 0000000000..421c89479a
--- /dev/null
+++ b/osu.Game/Screens/Multi/Screens/Lounge/FilterControl.cs
@@ -0,0 +1,27 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Graphics;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Overlays.SearchableList;
+using OpenTK.Graphics;
+
+namespace osu.Game.Screens.Multi.Screens.Lounge
+{
+ public class FilterControl : SearchableListFilterControl
+ {
+ protected override Color4 BackgroundColour => OsuColour.FromHex(@"362e42");
+ protected override LoungeTab DefaultTab => LoungeTab.Public;
+
+ public FilterControl()
+ {
+ DisplayStyleControl.Hide();
+ }
+ }
+
+ public enum LoungeTab
+ {
+ Public = RoomAvailability.Public,
+ Private = RoomAvailability.FriendsOnly,
+ }
+}
diff --git a/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs b/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs
new file mode 100644
index 0000000000..016babcaa5
--- /dev/null
+++ b/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs
@@ -0,0 +1,191 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Input;
+using osu.Framework.Screens;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Overlays.SearchableList;
+using osu.Game.Screens.Multi.Components;
+using OpenTK;
+
+namespace osu.Game.Screens.Multi.Screens.Lounge
+{
+ public class Lounge : MultiplayerScreen
+ {
+ private readonly Container content;
+ private readonly SearchContainer search;
+
+ protected readonly FilterControl Filter;
+ protected readonly FillFlowContainer RoomsContainer;
+ protected readonly RoomInspector Inspector;
+
+ public override string Title => "Lounge";
+
+ protected override Container TransitionContent => content;
+
+ private IEnumerable rooms;
+ public IEnumerable Rooms
+ {
+ get { return rooms; }
+ set
+ {
+ if (Equals(value, rooms)) return;
+ rooms = value;
+
+ var enumerable = rooms.ToList();
+
+ RoomsContainer.Children = enumerable.Select(r => new DrawableRoom(r)
+ {
+ Action = didSelect,
+ }).ToList();
+
+ if (!enumerable.Contains(Inspector.Room))
+ Inspector.Room = null;
+
+ filterRooms();
+ }
+ }
+
+ public Lounge()
+ {
+ Children = new Drawable[]
+ {
+ Filter = new FilterControl(),
+ content = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new ScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.55f,
+ Padding = new MarginPadding
+ {
+ Vertical = 35 - DrawableRoom.SELECTION_BORDER_WIDTH,
+ Right = 20 - DrawableRoom.SELECTION_BORDER_WIDTH
+ },
+ Child = search = new SearchContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Child = RoomsContainer = new RoomsFilterContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(10 - DrawableRoom.SELECTION_BORDER_WIDTH * 2),
+ },
+ },
+ },
+ Inspector = new RoomInspector
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.45f,
+ },
+ },
+ },
+ };
+
+ Filter.Search.Current.ValueChanged += s => filterRooms();
+ Filter.Tabs.Current.ValueChanged += t => filterRooms();
+ Filter.Search.Exit += Exit;
+ }
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ content.Padding = new MarginPadding
+ {
+ Top = Filter.DrawHeight,
+ Left = SearchableListOverlay.WIDTH_PADDING - DrawableRoom.SELECTION_BORDER_WIDTH,
+ Right = SearchableListOverlay.WIDTH_PADDING,
+ };
+ }
+
+ protected override void OnFocus(InputState state)
+ {
+ GetContainingInputManager().ChangeFocus(Filter.Search);
+ }
+
+ protected override void OnEntering(Screen last)
+ {
+ base.OnEntering(last);
+ Filter.Search.HoldFocus = true;
+ }
+
+ protected override bool OnExiting(Screen next)
+ {
+ Filter.Search.HoldFocus = false;
+ return base.OnExiting(next);
+ }
+
+ protected override void OnResuming(Screen last)
+ {
+ base.OnResuming(last);
+ Filter.Search.HoldFocus = true;
+ }
+
+ protected override void OnSuspending(Screen next)
+ {
+ base.OnSuspending(next);
+ Filter.Search.HoldFocus = false;
+ }
+
+ private void filterRooms()
+ {
+ search.SearchTerm = Filter.Search.Current.Value ?? string.Empty;
+
+ foreach (DrawableRoom r in RoomsContainer.Children)
+ {
+ r.MatchingFilter = r.MatchingFilter &&
+ r.Room.Availability.Value == (RoomAvailability)Filter.Tabs.Current.Value;
+ }
+ }
+
+ private void didSelect(DrawableRoom room)
+ {
+ RoomsContainer.Children.ForEach(c =>
+ {
+ if (c != room)
+ c.State = SelectionState.NotSelected;
+ });
+
+ Inspector.Room = room.Room;
+
+ // open the room if its selected and is clicked again
+ if (room.State == SelectionState.Selected)
+ Push(new Match());
+ }
+
+ private class RoomsFilterContainer : FillFlowContainer, IHasFilterableChildren
+ {
+ public IEnumerable FilterTerms => new string[] { };
+ public IEnumerable FilterableChildren => Children;
+
+ public bool MatchingFilter
+ {
+ set
+ {
+ if (value)
+ InvalidateLayout();
+ }
+ }
+
+ public RoomsFilterContainer()
+ {
+ LayoutDuration = 200;
+ LayoutEasing = Easing.OutQuint;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Screens/MultiplayerScreen.cs b/osu.Game/Screens/Multi/Screens/MultiplayerScreen.cs
new file mode 100644
index 0000000000..fa9b40684c
--- /dev/null
+++ b/osu.Game/Screens/Multi/Screens/MultiplayerScreen.cs
@@ -0,0 +1,57 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Screens;
+using osu.Game.Graphics.Containers;
+
+namespace osu.Game.Screens.Multi.Screens
+{
+ public abstract class MultiplayerScreen : OsuScreen
+ {
+ private const Easing in_easing = Easing.OutQuint;
+ private const Easing out_easing = Easing.InSine;
+
+ protected virtual Container TransitionContent => Content;
+
+ ///
+ /// The type to display in the title of the .
+ ///
+ public virtual string Type => Title;
+
+ protected override void OnEntering(Screen last)
+ {
+ base.OnEntering(last);
+
+ TransitionContent.MoveToX(200);
+
+ TransitionContent.FadeInFromZero(WaveContainer.APPEAR_DURATION, in_easing);
+ TransitionContent.MoveToX(0, WaveContainer.APPEAR_DURATION, in_easing);
+ }
+
+ protected override bool OnExiting(Screen next)
+ {
+ Content.FadeOut(WaveContainer.DISAPPEAR_DURATION, out_easing);
+ TransitionContent.MoveToX(200, WaveContainer.DISAPPEAR_DURATION, out_easing);
+
+ return base.OnExiting(next);
+ }
+
+ protected override void OnResuming(Screen last)
+ {
+ base.OnResuming(last);
+
+ Content.FadeIn(WaveContainer.APPEAR_DURATION, in_easing);
+ TransitionContent.MoveToX(0, WaveContainer.APPEAR_DURATION, in_easing);
+ }
+
+ protected override void OnSuspending(Screen next)
+ {
+ base.OnSuspending(next);
+
+ Content.FadeOut(WaveContainer.DISAPPEAR_DURATION, out_easing);
+ TransitionContent.MoveToX(-200, WaveContainer.DISAPPEAR_DURATION, out_easing);
+ }
+ }
+}
diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs
index 4b1562291b..d98aac8f84 100644
--- a/osu.Game/Screens/OsuScreen.cs
+++ b/osu.Game/Screens/OsuScreen.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using Microsoft.EntityFrameworkCore.Internal;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
@@ -17,13 +18,22 @@ using osu.Game.Rulesets;
using osu.Game.Screens.Menu;
using OpenTK;
using OpenTK.Input;
+using osu.Game.Overlays;
+using osu.Framework.Graphics.Containers;
namespace osu.Game.Screens
{
- public abstract class OsuScreen : Screen, IKeyBindingHandler
+ public abstract class OsuScreen : Screen, IKeyBindingHandler, IHasDescription
{
public BackgroundScreen Background { get; private set; }
+ ///
+ /// A user-facing title for this screen.
+ ///
+ public virtual string Title => GetType().ShortDisplayName();
+
+ public string Description => Title;
+
protected virtual bool AllowBackButton => true;
///
@@ -32,19 +42,19 @@ namespace osu.Game.Screens
///
protected virtual BackgroundScreen CreateBackground() => null;
- private readonly BindableBool hideOverlaysOnEnter = new BindableBool();
+ private Action updateOverlayStates;
///
- /// Whether overlays should be hidden when this screen is entered or resumed.
+ /// Whether all overlays should be hidden when this screen is entered or resumed.
///
- protected virtual bool HideOverlaysOnEnter => hideOverlaysOnEnter;
+ protected virtual bool HideOverlaysOnEnter => false;
- private readonly BindableBool allowOpeningOverlays = new BindableBool();
+ protected readonly Bindable OverlayActivationMode = new Bindable();
///
- /// Whether overlays should be able to be opened while this screen is active.
+ /// Whether overlays should be able to be opened once this screen is entered or resumed.
///
- protected virtual bool AllowOpeningOverlays => allowOpeningOverlays;
+ protected virtual OverlayActivation InitialOverlayActivationMode => OverlayActivation.All;
///
/// Whether this allows the cursor to be displayed.
@@ -95,8 +105,15 @@ namespace osu.Game.Screens
if (osuGame != null)
{
Ruleset.BindTo(osuGame.Ruleset);
- hideOverlaysOnEnter.BindTo(osuGame.HideOverlaysOnEnter);
- allowOpeningOverlays.BindTo(osuGame.AllowOpeningOverlays);
+ OverlayActivationMode.BindTo(osuGame.OverlayActivationMode);
+
+ updateOverlayStates = () =>
+ {
+ if (HideOverlaysOnEnter)
+ osuGame.CloseAllOverlays();
+ else
+ osuGame.Toolbar.State = Visibility.Visible;
+ };
}
sampleExit = audio.Sample.Get(@"UI/screen-back");
@@ -217,19 +234,24 @@ namespace osu.Game.Screens
logo.Anchor = Anchor.TopLeft;
logo.Origin = Anchor.Centre;
logo.RelativePositionAxes = Axes.None;
+ logo.BeatMatching = true;
logo.Triangles = true;
logo.Ripple = true;
}
private void applyArrivingDefaults(bool isResuming)
{
- logo.AppendAnimatingAction(() => LogoArriving(logo, isResuming), true);
+ logo.AppendAnimatingAction(() =>
+ {
+ if (IsCurrentScreen) LogoArriving(logo, isResuming);
+ }, true);
if (backgroundParallaxContainer != null)
backgroundParallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * BackgroundParallaxAmount;
- hideOverlaysOnEnter.Value = HideOverlaysOnEnter;
- allowOpeningOverlays.Value = AllowOpeningOverlays;
+ OverlayActivationMode.Value = InitialOverlayActivationMode;
+
+ updateOverlayStates?.Invoke();
}
private void onExitingLogo()
diff --git a/osu.Game/Screens/Play/HUD/QuitButton.cs b/osu.Game/Screens/Play/HUD/QuitButton.cs
new file mode 100644
index 0000000000..d0aa0dad92
--- /dev/null
+++ b/osu.Game/Screens/Play/HUD/QuitButton.cs
@@ -0,0 +1,198 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input;
+using osu.Framework.MathUtils;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using OpenTK;
+
+namespace osu.Game.Screens.Play.HUD
+{
+ public class QuitButton : FillFlowContainer
+ {
+ public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
+
+ private readonly Button button;
+
+ public Action Action
+ {
+ set => button.Action = value;
+ }
+
+ private readonly OsuSpriteText text;
+
+ public QuitButton()
+ {
+ Direction = FillDirection.Horizontal;
+ Spacing = new Vector2(20, 0);
+ Margin = new MarginPadding(10);
+ Children = new Drawable[]
+ {
+ text = new OsuSpriteText
+ {
+ Text = "hold for menu",
+ Font = @"Exo2.0-Bold",
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft
+ },
+ button = new Button
+ {
+ HoverGained = () => text.FadeIn(500, Easing.OutQuint),
+ HoverLost = () => text.FadeOut(500, Easing.OutQuint)
+ }
+ };
+ AutoSizeAxes = Axes.Both;
+ }
+
+ protected override void LoadComplete()
+ {
+ text.FadeInFromZero(500, Easing.OutQuint).Delay(1500).FadeOut(500, Easing.OutQuint);
+ base.LoadComplete();
+ }
+
+ private float positionalAdjust;
+
+ protected override bool OnMouseMove(InputState state)
+ {
+ positionalAdjust = Vector2.Distance(state.Mouse.NativeState.Position, button.ScreenSpaceDrawQuad.Centre) / 200;
+ return base.OnMouseMove(state);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (text.Alpha > 0 || button.Progress.Value > 0 || button.IsHovered)
+ Alpha = 1;
+ else
+ Alpha = Interpolation.ValueAt(
+ MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000),
+ Alpha, MathHelper.Clamp(1 - positionalAdjust, 0.04f, 1), 0, 200, Easing.OutQuint);
+ }
+
+ private class Button : HoldToConfirmContainer
+ {
+ private SpriteIcon icon;
+ private CircularProgress circularProgress;
+ private Circle overlayCircle;
+
+ protected override bool AllowMultipleFires => true;
+
+ public Action HoverGained;
+ public Action HoverLost;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Size = new Vector2(60);
+
+ Child = new CircularContainer
+ {
+ Masking = true,
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.Gray1,
+ Alpha = 0.5f,
+ },
+ circularProgress = new CircularProgress
+ {
+ RelativeSizeAxes = Axes.Both,
+ InnerRadius = 1
+ },
+ overlayCircle = new Circle
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.Gray1,
+ Size = new Vector2(0.9f),
+ },
+ icon = new SpriteIcon
+ {
+ Shadow = false,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(15),
+ Icon = FontAwesome.fa_close
+ },
+ }
+ };
+
+ bind();
+ }
+
+ private void bind()
+ {
+ circularProgress.Current.BindTo(Progress);
+ Progress.ValueChanged += v => icon.Scale = new Vector2(1 + (float)v * 0.2f);
+ }
+
+ private bool pendingAnimation;
+
+ protected override void Confirm()
+ {
+ base.Confirm();
+
+ // temporarily unbind as to not look weird if releasing during confirm animation (can see the unwind of progress).
+ Progress.UnbindAll();
+
+ // avoid starting a new confirm call until we finish animating.
+ pendingAnimation = true;
+
+ Progress.Value = 0;
+
+ overlayCircle.ScaleTo(0, 100)
+ .Then().FadeOut().ScaleTo(1).FadeIn(500)
+ .OnComplete(a =>
+ {
+ icon.ScaleTo(1, 100);
+ circularProgress.FadeOut(100).OnComplete(_ =>
+ {
+ bind();
+
+ circularProgress.FadeIn();
+ pendingAnimation = false;
+ });
+ });
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ HoverGained?.Invoke();
+ return true;
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ HoverLost?.Invoke();
+ base.OnHoverLost(state);
+ }
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ if (!pendingAnimation && state.Mouse.Buttons.Count == 1)
+ BeginConfirm();
+ return true;
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ if (state.Mouse.Buttons.Count == 0)
+ AbortConfirm();
+ return true;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index 36d8bb75c0..f920b20649 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -34,6 +34,7 @@ namespace osu.Game.Screens.Play
public readonly HealthDisplay HealthDisplay;
public readonly SongProgress Progress;
public readonly ModDisplay ModDisplay;
+ public readonly QuitButton HoldToQuit;
public readonly PlayerSettingsOverlay PlayerSettingsOverlay;
private Bindable showHud;
@@ -51,14 +52,26 @@ namespace osu.Game.Screens.Play
Children = new Drawable[]
{
- KeyCounter = CreateKeyCounter(),
ComboCounter = CreateComboCounter(),
ScoreCounter = CreateScoreCounter(),
AccuracyCounter = CreateAccuracyCounter(),
HealthDisplay = CreateHealthDisplay(),
Progress = CreateProgress(),
ModDisplay = CreateModsContainer(),
- PlayerSettingsOverlay = CreatePlayerSettingsOverlay()
+ PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
+ new FillFlowContainer
+ {
+ Anchor = Anchor.BottomRight,
+ Origin = Anchor.BottomRight,
+ Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y),
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ KeyCounter = CreateKeyCounter(),
+ HoldToQuit = CreateQuitButton(),
+ }
+ }
}
});
@@ -187,7 +200,6 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding(10),
- Y = -TwoLayerButton.SIZE_RETRACTED.Y,
};
protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
@@ -205,6 +217,12 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.X,
};
+ protected virtual QuitButton CreateQuitButton() => new QuitButton
+ {
+ Anchor = Anchor.BottomRight,
+ Origin = Anchor.BottomRight,
+ };
+
protected virtual ModDisplay CreateModsContainer() => new ModDisplay
{
Anchor = Anchor.TopRight,
diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs
index bd334ad2e2..6262f71ddc 100644
--- a/osu.Game/Screens/Play/PauseContainer.cs
+++ b/osu.Game/Screens/Play/PauseContainer.cs
@@ -44,13 +44,18 @@ namespace osu.Game.Screens.Play
public Action OnResume;
public Action OnPause;
- private readonly IAdjustableClock adjustableClock;
private readonly FramedClock framedClock;
+ private readonly DecoupleableInterpolatingFramedClock decoupledClock;
- public PauseContainer(FramedClock framedClock, IAdjustableClock adjustableClock)
+ ///
+ /// Creates a new .
+ ///
+ /// The gameplay clock. This is the clock that will process frames.
+ /// The seekable clock. This is the clock that will be paused and resumed.
+ public PauseContainer(FramedClock framedClock, DecoupleableInterpolatingFramedClock decoupledClock)
{
this.framedClock = framedClock;
- this.adjustableClock = adjustableClock;
+ this.decoupledClock = decoupledClock;
RelativeSizeAxes = Axes.Both;
@@ -80,7 +85,7 @@ namespace osu.Game.Screens.Play
if (IsPaused) return;
// stop the seekable clock (stops the audio eventually)
- adjustableClock.Stop();
+ decoupledClock.Stop();
IsPaused = true;
OnPause?.Invoke();
@@ -97,10 +102,10 @@ namespace osu.Game.Screens.Play
IsResuming = false;
lastPauseActionTime = Time.Current;
- // seek back to the time of the framed clock.
- // this accounts for the audio clock potentially taking time to enter a completely stopped state.
- adjustableClock.Seek(framedClock.CurrentTime);
- adjustableClock.Start();
+ // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time
+ // This accounts for the audio clock source potentially taking time to enter a completely stopped state
+ decoupledClock.Seek(decoupledClock.CurrentTime);
+ decoupledClock.Start();
OnResume?.Invoke();
pauseOverlay.Hide();
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index ec7a99145e..9832c430be 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
@@ -21,6 +22,7 @@ using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
+using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
@@ -37,6 +39,8 @@ namespace osu.Game.Screens.Play
protected override bool HideOverlaysOnEnter => true;
+ protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered;
+
public Action RestartRequested;
public bool HasFailed { get; private set; }
@@ -70,7 +74,7 @@ namespace osu.Game.Screens.Play
private SampleChannel sampleRestart;
- private ScoreProcessor scoreProcessor;
+ protected ScoreProcessor ScoreProcessor;
protected RulesetContainer RulesetContainer;
private HUDOverlay hudOverlay;
@@ -143,13 +147,17 @@ namespace osu.Game.Screens.Play
adjustableClock.ProcessFrame();
+ // Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited.
+ // This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
+ var platformOffsetClock = new FramedOffsetClock(adjustableClock) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 22 : 0 };
+
// the final usable gameplay clock with user-set offsets applied.
- var offsetClock = new FramedOffsetClock(adjustableClock);
+ var offsetClock = new FramedOffsetClock(platformOffsetClock);
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
userAudioOffset.TriggerChange();
- scoreProcessor = RulesetContainer.CreateScoreProcessor();
+ ScoreProcessor = RulesetContainer.CreateScoreProcessor();
Children = new Drawable[]
{
@@ -176,21 +184,21 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.Both,
Child = RulesetContainer
},
- new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
+ new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
ProcessCustomClock = false,
Breaks = beatmap.Breaks
},
- hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
+ RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
+ hudOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
{
Clock = Clock, // hud overlay doesn't want to use the audio clock directly
ProcessCustomClock = false,
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
- RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
new SkipOverlay(firstObjectTime)
{
Clock = Clock, // skip button doesn't want to use the audio clock directly
@@ -219,15 +227,17 @@ namespace osu.Game.Screens.Play
}
};
+ hudOverlay.HoldToQuit.Action = Exit;
+
if (ShowStoryboard)
initializeStoryboard(false);
// Bind ScoreProcessor to ourselves
- scoreProcessor.AllJudged += onCompletion;
- scoreProcessor.Failed += onFail;
+ ScoreProcessor.AllJudged += onCompletion;
+ ScoreProcessor.Failed += onFail;
foreach (var mod in Beatmap.Value.Mods.Value.OfType())
- mod.ApplyToScoreProcessor(scoreProcessor);
+ mod.ApplyToScoreProcessor(ScoreProcessor);
}
private void applyRateFromMods()
@@ -252,7 +262,7 @@ namespace osu.Game.Screens.Play
private void onCompletion()
{
// Only show the completion screen if the player hasn't failed
- if (scoreProcessor.HasFailed || onCompletionEvent != null)
+ if (ScoreProcessor.HasFailed || onCompletionEvent != null)
return;
ValidForResume = false;
@@ -270,7 +280,7 @@ namespace osu.Game.Screens.Play
Beatmap = Beatmap.Value.BeatmapInfo,
Ruleset = ruleset
};
- scoreProcessor.PopulateScore(score);
+ ScoreProcessor.PopulateScore(score);
score.User = RulesetContainer.Replay?.User ?? api.LocalUser.Value;
Push(new Results(score));
});
@@ -362,7 +372,7 @@ namespace osu.Game.Screens.Play
Background?.FadeTo(1f, fade_out_duration);
}
- protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused;
+ protected override bool OnScroll(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused;
private void initializeStoryboard(bool asyncLoad)
{
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 734837a4f1..2d5bc889c3 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -7,15 +7,15 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
+using osu.Framework.Localisation;
using osu.Framework.Screens;
+using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
-using OpenTK;
-using osu.Framework.Localisation;
-using osu.Framework.Threading;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play.PlayerSettings;
+using OpenTK;
namespace osu.Game.Screens.Play
{
@@ -51,11 +51,19 @@ namespace osu.Game.Screens.Play
Origin = Anchor.Centre,
});
- Add(new VisualSettings
+ Add(new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
- Margin = new MarginPadding(25)
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 20),
+ Margin = new MarginPadding(25),
+ Children = new PlayerSettingsGroup[]
+ {
+ new VisualSettings(),
+ new InputSettings()
+ }
});
loadTask = LoadComponentAsync(player);
@@ -201,8 +209,11 @@ namespace osu.Game.Screens.Play
{
base.Dispose(isDisposing);
- // if the player never got pushed, we should explicitly dispose it.
- loadTask?.ContinueWith(_ => player.Dispose());
+ if (isDisposing)
+ {
+ // if the player never got pushed, we should explicitly dispose it.
+ loadTask?.ContinueWith(_ => player.Dispose());
+ }
}
private class BeatmapMetadataDisplay : Container
diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs
new file mode 100644
index 0000000000..755ba468cc
--- /dev/null
+++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Configuration;
+
+namespace osu.Game.Screens.Play.PlayerSettings
+{
+ public class InputSettings : PlayerSettingsGroup
+ {
+ protected override string Title => "Input settings";
+
+ private readonly PlayerCheckbox mouseButtonsCheckbox;
+
+ public InputSettings()
+ {
+ Children = new Drawable[]
+ {
+ mouseButtonsCheckbox = new PlayerCheckbox
+ {
+ LabelText = "Disable mouse buttons"
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config) => mouseButtonsCheckbox.Bindable = config.GetBindable(OsuSetting.MouseDisableButtons);
+ }
+}
diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs
index 0ffcdf94a6..cff3eca895 100644
--- a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs
+++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs
@@ -50,11 +50,11 @@ namespace osu.Game.Screens.Play.PlayerSettings
content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
}
- button.FadeColour(expanded ? buttonActiveColour : Color4.White, 200, Easing.OutQuint);
+ updateExpanded();
}
}
- private Color4 buttonActiveColour;
+ private Color4 expandedColour;
protected PlayerSettingsGroup()
{
@@ -130,9 +130,13 @@ namespace osu.Game.Screens.Play.PlayerSettings
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- button.Colour = buttonActiveColour = colours.Yellow;
+ expandedColour = colours.Yellow;
+
+ updateExpanded();
}
+ private void updateExpanded() => button.FadeColour(expanded ? expandedColour : Color4.White, 200, Easing.InOutQuint);
+
protected override Container Content => content;
protected override bool OnHover(InputState state) => true;
diff --git a/osu.Game/Screens/Play/SongProgressInfo.cs b/osu.Game/Screens/Play/SongProgressInfo.cs
index b79c212ade..3156a646db 100644
--- a/osu.Game/Screens/Play/SongProgressInfo.cs
+++ b/osu.Game/Screens/Play/SongProgressInfo.cs
@@ -92,6 +92,6 @@ namespace osu.Game.Screens.Play
}
}
- private string formatTime(TimeSpan timeSpan) => $"{(timeSpan < TimeSpan.Zero ? "-" : "")}{timeSpan.Duration().TotalMinutes:N0}:{timeSpan.Duration().Seconds:D2}";
+ private string formatTime(TimeSpan timeSpan) => $"{(timeSpan < TimeSpan.Zero ? "-" : "")}{Math.Floor(timeSpan.Duration().TotalMinutes)}:{timeSpan.Duration().Seconds:D2}";
}
}
diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs
index 32161a0b8e..7cbd2e4403 100644
--- a/osu.Game/Screens/Ranking/Results.cs
+++ b/osu.Game/Screens/Ranking/Results.cs
@@ -57,6 +57,7 @@ namespace osu.Game.Screens.Ranking
{
base.OnEntering(last);
(Background as BackgroundScreenBeatmap)?.BlurTo(background_blur, 2500, Easing.OutQuint);
+ Background.ScaleTo(1.1f, transition_time, Easing.OutQuint);
allCircles.ForEach(c =>
{
@@ -102,6 +103,8 @@ namespace osu.Game.Screens.Ranking
c.ScaleTo(0, transition_time, Easing.OutSine);
});
+ Background.ScaleTo(1f, transition_time / 4, Easing.OutQuint);
+
Content.FadeOut(transition_time / 4);
return base.OnExiting(next);
@@ -160,7 +163,6 @@ namespace osu.Game.Screens.Ranking
{
RelativeSizeAxes = Axes.Both,
ParallaxAmount = 0.01f,
- Scale = new Vector2(1 / circle_outer_scale / overscan),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index ca36f94eda..f1bd2b945f 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -6,7 +6,6 @@ using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System.Linq;
@@ -120,14 +119,8 @@ namespace osu.Game.Screens.Select
Margin = new MarginPadding { Top = spacing * 2 },
Children = new[]
{
- description = new MetadataSection("Description")
- {
- TextColour = Color4.White.Opacity(0.75f),
- },
- source = new MetadataSection("Source")
- {
- TextColour = Color4.White.Opacity(0.75f),
- },
+ description = new MetadataSection("Description"),
+ source = new MetadataSection("Source"),
tags = new MetadataSection("Tags"),
},
},
@@ -164,10 +157,9 @@ namespace osu.Game.Screens.Select
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours, APIAccess api)
+ private void load(APIAccess api)
{
this.api = api;
- tags.TextColour = colours.Yellow;
}
protected override void UpdateAfterChildren()
@@ -364,7 +356,7 @@ namespace osu.Game.Screens.Select
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
- Colour = textFlow.Colour,
+ Colour = Color4.White.Opacity(0.75f),
Text = text
}, loaded =>
{
@@ -375,12 +367,6 @@ namespace osu.Game.Screens.Select
this.FadeIn(transition_duration);
});
}
-
- public Color4 TextColour
- {
- get { return textFlow.Colour; }
- set { textFlow.Colour = value; }
- }
}
private class DimmedLoadingAnimation : VisibilityContainer
diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index 97f6371cb2..7950018554 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Screens.Select
{
if (osuGame != null)
ruleset.BindTo(osuGame.Ruleset);
- ruleset.ValueChanged += updateRuleset;
+ ruleset.ValueChanged += _ => updateDisplay();
}
protected override bool BlockPassThroughMouse => false;
@@ -78,66 +78,76 @@ namespace osu.Game.Screens.Select
private WorkingBeatmap beatmap;
- public void UpdateBeatmap(WorkingBeatmap beatmap)
+ public WorkingBeatmap Beatmap
{
- this.beatmap = beatmap;
- loadBeatmap();
+ get => beatmap;
+ set
+ {
+ if (beatmap == value) return;
+
+ beatmap = value;
+ updateDisplay();
+ }
}
- private void updateRuleset(RulesetInfo ruleset) => loadBeatmap();
+ private BufferedWedgeInfo loadingInfo;
- private void loadBeatmap()
+ private void updateDisplay()
{
- void updateState()
+ void removeOldInfo()
{
State = beatmap == null ? Visibility.Hidden : Visibility.Visible;
Info?.FadeOut(250);
Info?.Expire();
+ Info = null;
}
if (beatmap == null)
{
- updateState();
+ removeOldInfo();
return;
}
- LoadComponentAsync(new BufferedWedgeInfo(beatmap, ruleset.Value)
+ LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value)
{
Shear = -Shear,
- Depth = Info?.Depth + 1 ?? 0,
- }, newInfo =>
+ Depth = Info?.Depth + 1 ?? 0
+ }, loaded =>
{
- updateState();
- Add(Info = newInfo);
+ // ensure we are the most recent loaded wedge.
+ if (loaded != loadingInfo) return;
+
+ removeOldInfo();
+ Add(Info = loaded);
});
}
public class BufferedWedgeInfo : BufferedContainer
{
- private readonly WorkingBeatmap working;
public OsuSpriteText VersionLabel { get; private set; }
public OsuSpriteText TitleLabel { get; private set; }
public OsuSpriteText ArtistLabel { get; private set; }
public FillFlowContainer MapperContainer { get; private set; }
public FillFlowContainer InfoLabelContainer { get; private set; }
+
private UnicodeBindableString titleBinding;
private UnicodeBindableString artistBinding;
+ private readonly WorkingBeatmap beatmap;
private readonly RulesetInfo ruleset;
- public BufferedWedgeInfo(WorkingBeatmap working, RulesetInfo userRuleset)
+ public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset)
{
- this.working = working;
-
- ruleset = userRuleset ?? working.BeatmapInfo.Ruleset;
+ this.beatmap = beatmap;
+ ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset;
}
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
- var beatmapInfo = working.BeatmapInfo;
- var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
+ var beatmapInfo = beatmap.BeatmapInfo;
+ var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
PixelSnapping = true;
CacheDrawnFrameBuffer = true;
@@ -165,7 +175,7 @@ namespace osu.Game.Screens.Select
Children = new[]
{
// Zoomed-in and cropped beatmap background
- new BeatmapBackgroundSprite(working)
+ new BeatmapBackgroundSprite(beatmap)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@@ -248,27 +258,27 @@ namespace osu.Game.Screens.Select
private InfoLabel[] getInfoLabels()
{
- var beatmap = working.Beatmap;
+ var b = beatmap.Beatmap;
List labels = new List();
- if (beatmap?.HitObjects?.Any() == true)
+ if (b?.HitObjects?.Any() == true)
{
- HitObject lastObject = beatmap.HitObjects.LastOrDefault();
+ HitObject lastObject = b.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "Length",
Icon = FontAwesome.fa_clock_o,
- Content = TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
+ Content = TimeSpan.FromMilliseconds(endTime - b.HitObjects.First().StartTime).ToString(@"m\:ss"),
}));
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "BPM",
Icon = FontAwesome.fa_circle,
- Content = getBPMRange(beatmap),
+ Content = getBPMRange(b),
}));
IBeatmap playableBeatmap;
@@ -276,12 +286,12 @@ namespace osu.Game.Screens.Select
try
{
// Try to get the beatmap with the user's ruleset
- playableBeatmap = working.GetPlayableBeatmap(ruleset);
+ playableBeatmap = beatmap.GetPlayableBeatmap(ruleset);
}
catch (BeatmapInvalidForRulesetException)
{
// Can't be converted to the user's ruleset, so use the beatmap's own ruleset
- playableBeatmap = working.GetPlayableBeatmap(working.BeatmapInfo.Ruleset);
+ playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset);
}
labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)));
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
index f39952dc31..c5996327b9 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Screens.Select.Carousel
if (songSelect != null)
{
- startRequested = songSelect.FinaliseSelection;
+ startRequested = b => songSelect.FinaliseSelection(b);
editRequested = songSelect.Edit;
}
diff --git a/osu.Game/Screens/Select/EditSongSelect.cs b/osu.Game/Screens/Select/EditSongSelect.cs
index bca009e2c1..e1d71fdd05 100644
--- a/osu.Game/Screens/Select/EditSongSelect.cs
+++ b/osu.Game/Screens/Select/EditSongSelect.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Screens.Select
{
protected override bool ShowFooter => false;
- protected override bool OnSelectionFinalised()
+ protected override bool OnStart()
{
Exit();
return true;
diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
index baab973ae5..19732107c7 100644
--- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
@@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
@@ -142,6 +143,8 @@ namespace osu.Game.Screens.Select.Leaderboards
{
flagBadgeContainer = new Container
{
+ Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomLeft,
Size = new Vector2(87f, 20f),
Masking = true,
Children = new Drawable[]
@@ -155,14 +158,16 @@ namespace osu.Game.Screens.Select.Leaderboards
},
new FillFlowContainer
{
+ Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
- Margin = new MarginPadding { Left = edge_margin, },
+ Margin = new MarginPadding { Left = edge_margin },
Children = new Drawable[]
{
- maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString()),
- accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy)),
+ maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString(), "Max Combo"),
+ accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy), "Accuracy"),
},
},
},
@@ -305,37 +310,61 @@ namespace osu.Game.Screens.Select.Leaderboards
}
}
- private class ScoreComponentLabel : Container
+ private class ScoreComponentLabel : Container, IHasTooltip
{
- public ScoreComponentLabel(FontAwesome icon, string value)
- {
- Anchor = Anchor.CentreLeft;
- Origin = Anchor.CentreLeft;
- Size = new Vector2(60f, 20f);
- Padding = new MarginPadding { Top = 10f, };
+ private const float icon_size = 20;
- Children = new Drawable[]
+ private readonly string name;
+ private readonly FillFlowContainer content;
+
+ public override bool Contains(Vector2 screenSpacePos) => content.Contains(screenSpacePos);
+
+ public string TooltipText => name;
+
+ public ScoreComponentLabel(FontAwesome icon, string value, string name)
+ {
+ this.name = name;
+ AutoSizeAxes = Axes.Y;
+ Width = 60;
+
+ Child = content = new FillFlowContainer
{
- new SpriteIcon
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
{
- Origin = Anchor.Centre,
- Icon = FontAwesome.fa_square,
- Colour = OsuColour.FromHex(@"3087ac"),
- Rotation = 45,
- Size = new Vector2(20),
- Shadow = true,
- },
- new SpriteIcon
- {
- Origin = Anchor.Centre,
- Icon = icon,
- Colour = OsuColour.FromHex(@"a4edff"),
- Size = new Vector2(14),
- },
- new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
- {
- Origin = Anchor.CentreLeft,
- Margin = new MarginPadding { Left = 15, },
+ new Container
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ AutoSizeAxes = Axes.Both,
+ Children = new[]
+ {
+ new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(icon_size),
+ Rotation = 45,
+ Colour = OsuColour.FromHex(@"3087ac"),
+ Icon = FontAwesome.fa_square,
+ Shadow = true,
+ },
+ new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(icon_size - 6),
+ Colour = OsuColour.FromHex(@"a4edff"),
+ Icon = icon,
+ },
+ },
+ },
+ new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ },
},
};
}
diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs
index 3ffac591f3..a0c96d0cee 100644
--- a/osu.Game/Screens/Select/MatchSongSelect.cs
+++ b/osu.Game/Screens/Select/MatchSongSelect.cs
@@ -5,7 +5,7 @@ namespace osu.Game.Screens.Select
{
public class MatchSongSelect : SongSelect
{
- protected override bool OnSelectionFinalised()
+ protected override bool OnStart()
{
Schedule(() =>
{
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index 7992930c45..8ce40fcfa0 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -137,7 +137,7 @@ namespace osu.Game.Screens.Select
return false;
}
- protected override bool OnSelectionFinalised()
+ protected override bool OnStart()
{
if (player != null) return false;
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 4ffa9e2a90..41ba38cb0f 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using System.Threading;
using OpenTK;
using OpenTK.Input;
using osu.Framework.Allocation;
@@ -63,8 +62,6 @@ namespace osu.Game.Screens.Select
private SampleChannel sampleChangeDifficulty;
private SampleChannel sampleChangeBeatmap;
- private CancellationTokenSource initialAddSetsTask;
-
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
@@ -207,9 +204,7 @@ namespace osu.Game.Screens.Select
sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty");
sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand");
- initialAddSetsTask = new CancellationTokenSource();
-
- Carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSets();
+ Carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSetsEnumerable();
Beatmap.DisabledChanged += disabled => Carousel.AllowSelection = !disabled;
Beatmap.TriggerChange();
@@ -227,7 +222,8 @@ namespace osu.Game.Screens.Select
/// Call to make a selection and perform the default action for this SongSelect.
///
/// An optional beatmap to override the current carousel selection.
- public void FinaliseSelection(BeatmapInfo beatmap = null)
+ /// Whether to trigger .
+ public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true)
{
// if we have a pending filter operation, we want to run it now.
// it could change selection (ie. if the ruleset has been changed).
@@ -243,14 +239,15 @@ namespace osu.Game.Screens.Select
selectionChangedDebounce = null;
}
- OnSelectionFinalised();
+ if (performStartAction)
+ OnStart();
}
///
/// Called when a selection is made.
///
/// If a resultant action occurred that takes the user away from SongSelect.
- protected abstract bool OnSelectionFinalised();
+ protected abstract bool OnStart();
private ScheduledDelegate selectionChangedDebounce;
@@ -395,7 +392,7 @@ namespace osu.Game.Screens.Select
protected override bool OnExiting(Screen next)
{
- FinaliseSelection();
+ FinaliseSelection(performStartAction: false);
beatmapInfoWedge.State = Visibility.Hidden;
@@ -417,8 +414,6 @@ namespace osu.Game.Screens.Select
beatmaps.BeatmapHidden -= onBeatmapHidden;
beatmaps.BeatmapRestored -= onBeatmapRestored;
}
-
- initialAddSetsTask?.Cancel();
}
///
@@ -435,7 +430,7 @@ namespace osu.Game.Screens.Select
backgroundModeBeatmap.FadeTo(1, 250);
}
- beatmapInfoWedge.UpdateBeatmap(beatmap);
+ beatmapInfoWedge.Beatmap = beatmap;
}
private void ensurePlayingSelected(bool preview = false)
diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs
index 43b20f7021..1ce7023be0 100644
--- a/osu.Game/Tests/Visual/EditorClockTestCase.cs
+++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual
protected EditorClockTestCase()
{
- Clock = new EditorClock(new ControlPointInfo(), BeatDivisor) { IsCoupled = false };
+ Clock = new EditorClock(new ControlPointInfo(), 5000, BeatDivisor) { IsCoupled = false };
}
[BackgroundDependencyLoader]
@@ -59,9 +59,9 @@ namespace osu.Game.Tests.Visual
Clock.ProcessFrame();
}
- protected override bool OnWheel(InputState state)
+ protected override bool OnScroll(InputState state)
{
- if (state.Mouse.WheelDelta > 0)
+ if (state.Mouse.ScrollDelta.Y > 0)
Clock.SeekBackward(true);
else
Clock.SeekForward(true);
diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs
index 2b677f1f42..fa441d8012 100644
--- a/osu.Game/Tests/Visual/OsuTestCase.cs
+++ b/osu.Game/Tests/Visual/OsuTestCase.cs
@@ -1,8 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.IO;
-using System.Reflection;
using osu.Framework.Testing;
namespace osu.Game.Tests.Visual
@@ -13,8 +11,6 @@ namespace osu.Game.Tests.Visual
public class OsuTestCaseTestRunner : OsuGameBase, ITestCaseTestRunner
{
- protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location;
-
private TestCaseTestRunner.TestRunner runner;
protected override void LoadAsyncComplete()
diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs
index bda438d906..bf6236e4d5 100644
--- a/osu.Game/Tests/Visual/TestCasePlayer.cs
+++ b/osu.Game/Tests/Visual/TestCasePlayer.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual
{
Player p = null;
AddStep(ruleset.RulesetInfo.Name, () => p = loadPlayerFor(ruleset));
- AddUntilStep(() => p.IsLoaded);
+ AddUntilStep(() => ContinueCondition(p));
}
else
{
@@ -52,11 +52,13 @@ namespace osu.Game.Tests.Visual
{
Player p = null;
AddStep(r.Name, () => p = loadPlayerFor(r));
- AddUntilStep(() => p.IsLoaded);
+ AddUntilStep(() => ContinueCondition(p));
}
}
}
+ protected virtual bool ContinueCondition(Player player) => player.IsLoaded;
+
protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo);
private Player loadPlayerFor(RulesetInfo ri) => loadPlayerFor(ri.CreateInstance());
diff --git a/osu.Game/Utils/DebugUtils.cs b/osu.Game/Utils/DebugUtils.cs
new file mode 100644
index 0000000000..191662c690
--- /dev/null
+++ b/osu.Game/Utils/DebugUtils.cs
@@ -0,0 +1,21 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Utils
+{
+ public static class DebugUtils
+ {
+ public static bool IsDebug
+ {
+ get
+ {
+ // ReSharper disable once RedundantAssignment
+ bool isDebug = false;
+ // Debug.Assert conditions are only evaluated in debug mode
+ System.Diagnostics.Debug.Assert(isDebug = true);
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+ return isDebug;
+ }
+ }
+ }
+}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 1a75f1979a..2300ba6a72 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -11,14 +11,14 @@
-
-
+
+
diff --git a/osu.TestProject.props b/osu.TestProject.props
index afdf895eac..b51ca13ed5 100644
--- a/osu.TestProject.props
+++ b/osu.TestProject.props
@@ -7,7 +7,6 @@
-
@@ -18,7 +17,7 @@
-
+
VisualTestRunner.cs
diff --git a/osu.sln b/osu.sln
index 5c4b644489..bf1b6d60e1 100644
--- a/osu.sln
+++ b/osu.sln
@@ -5,8 +5,6 @@ VisualStudioVersion = 15.0.27004.2006
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework", "osu-framework\osu.Framework\osu.Framework.csproj", "{C76BF5B3-985E-4D39-95FE-97C9C879B83A}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Resources", "osu-resources\osu.Game.Resources\osu.Game.Resources.csproj", "{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}"
@@ -17,8 +15,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Mania", "osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj", "{48F4582B-7687-4621-9CBE-5C24197CB536}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Desktop.Deploy", "osu.Desktop.Deploy\osu.Desktop.Deploy.csproj", "{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Tests", "osu.Game.Tests\osu.Game.Tests.csproj", "{54377672-20B1-40AF-8087-5CF73BF3953A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Desktop", "osu.Desktop\osu.Desktop.csproj", "{419659FD-72EA-4678-9EB8-B22A746CED70}"
@@ -41,10 +37,6 @@ Global
{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.Build.0 = Release|Any CPU
- {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.Build.0 = Release|Any CPU
{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -65,8 +57,6 @@ Global
{48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.Build.0 = Debug|Any CPU
{48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU
{48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU
- {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.ActiveCfg = Release|Any CPU