1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 08:27:49 +08:00

Merge remote-tracking branch 'origin/master' into i-beatmap

This commit is contained in:
smoogipoo 2018-05-07 09:58:53 +09:00
commit 1aefd71b65
96 changed files with 1002 additions and 428 deletions

View File

@ -0,0 +1,22 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (catch)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
</component>

View File

@ -0,0 +1,22 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (mania)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
</component>

View File

@ -0,0 +1,22 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (osu!)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
</component>

View File

@ -0,0 +1,22 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RulesetTests (taiko)" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
<env name="ASPNETCORE_URLS" value="http://localhost:5000" />
</envs>
<option name="USE_MONO" value="0" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<browser url="http://localhost:5000" />
<method />
</configuration>
</component>

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="VisualTests (net461)" type="DotNetProject" factoryName=".NET Project" singleton="true"> <configuration default="false" name="VisualTests (net471)" type="DotNetProject" factoryName=".NET Project" singleton="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.6.1" /> <option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<method /> <method />
</configuration> </configuration>
</component> </component>

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (net461)" type="DotNetProject" factoryName=".NET Project" singleton="true"> <configuration default="false" name="osu! (net471)" type="DotNetProject" factoryName=".NET Project" singleton="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net461/osu!.exe" /> <option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net471/osu!.exe" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> <option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" /> <option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.6.1" /> <option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.1" />
<method /> <method />
</configuration> </configuration>
</component> </component>

16
.vscode/launch.json vendored
View File

@ -2,13 +2,13 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "VisualTests (Debug, net461)", "name": "VisualTests (Debug, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe", "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)", "preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,
@ -16,13 +16,13 @@
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "VisualTests (Release, net461)", "name": "VisualTests (Release, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net461/osu.Game.Tests.exe", "program": "${workspaceRoot}/osu.Game.Tests/bin/Debug/net471/osu.Game.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)", "preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,
@ -30,13 +30,13 @@
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "osu! (Debug, net461)", "name": "osu! (Debug, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Debug/net461/osu!.exe", "program": "${workspaceRoot}/osu.Desktop/bin/Debug/net471/osu!.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)", "preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,
@ -44,13 +44,13 @@
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "osu! (Release, net461)", "name": "osu! (Release, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Release/net461/osu!.exe", "program": "${workspaceRoot}/osu.Desktop/bin/Release/net471/osu!.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)", "preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,

6
.vscode/tasks.json vendored
View File

@ -8,7 +8,7 @@
"type": "shell", "type": "shell",
"command": "msbuild", "command": "msbuild",
"args": [ "args": [
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -22,7 +22,7 @@
"command": "msbuild", "command": "msbuild",
"args": [ "args": [
"/p:Configuration=Release", "/p:Configuration=Release",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -64,7 +64,7 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "Restore (net461)", "label": "Restore (net471)",
"type": "shell", "type": "shell",
"command": "nuget", "command": "nuget",
"args": [ "args": [

View File

@ -8,7 +8,7 @@ This is still heavily under development and is not intended for end-user use. Th
# Requirements # Requirements
- A desktop platform that can compile .NET 4.6.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here. - A desktop platform that can compile .NET 4.7.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
# Getting Started # Getting Started
- Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`) - Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`)

View File

@ -1,4 +1,3 @@
# 2017-09-14
clone_depth: 1 clone_depth: 1
version: '{branch}-{build}' version: '{branch}-{build}'
image: Visual Studio 2017 image: Visual Studio 2017
@ -17,7 +16,7 @@ before_build:
- cmd: CodeFileSanity.exe - cmd: CodeFileSanity.exe
- cmd: nuget restore -verbosity quiet - cmd: nuget restore -verbosity quiet
environment: environment:
TargetFramework: net461 TargetFramework: net471
build: build:
project: osu.sln project: osu.sln
parallel: true parallel: true

34
appveyor_deploy.yml Normal file
View File

@ -0,0 +1,34 @@
branches:
only:
- release
skip_tags: true
skip_branch_with_pr: true
clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017
configuration: Debug
cache:
- packages -> **\packages.config
install:
- cmd: git submodule update --init --recursive --depth=5
before_build:
- cmd: nuget restore -verbosity quiet
build:
project: osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
verbosity: minimal
after_build:
- ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
- appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate
- cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
- appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration
- cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net471\osu.Desktop.Deploy.exe.config
- cd osu.Desktop.Deploy\bin\Debug\net471\
- osu.Desktop.Deploy.exe %code_signing_password%
environment:
TargetFramework: net471
decode_secret:
secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
code_signing_password:
secure: 34tLNqvjmmZEi97MLKfrnQ==
artifacts:
- path: 'Releases\*'

@ -1 +1 @@
Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a Subproject commit 0773d895d9aa0729995cd4a23efc28238e35ceed

View File

@ -7,7 +7,7 @@
"name": "Deploy (Debug)", "name": "Deploy (Debug)",
"request": "launch", "request": "launch",
"type": "mono", "type": "mono",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Desktop.Deploy.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Desktop.Deploy.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)", "preLaunchTask": "Build (Debug)",
"runtimeExecutable": null, "runtimeExecutable": null,
@ -18,7 +18,7 @@
"name": "Deploy (Release)", "name": "Deploy (Release)",
"request": "launch", "request": "launch",
"type": "clr", "type": "clr",
"program": "${workspaceRoot}/bin/Release/net461/osu.Desktop.Deploy.exe", "program": "${workspaceRoot}/bin/Release/net471/osu.Desktop.Deploy.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)", "preLaunchTask": "Build (Release)",
"runtimeExecutable": null, "runtimeExecutable": null,

View File

@ -7,6 +7,7 @@ using System.Configuration;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Management.Automation;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using FileWebRequest = osu.Framework.IO.Network.FileWebRequest; using FileWebRequest = osu.Framework.IO.Network.FileWebRequest;
@ -18,7 +19,7 @@ namespace osu.Desktop.Deploy
{ {
private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); private static string 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 nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe");
private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.7.8\tools\Squirrel.exe"); private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.8.0\tools\Squirrel.exe");
private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"; 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 StagingFolder = ConfigurationManager.AppSettings["StagingFolder"];
@ -57,8 +58,12 @@ namespace osu.Desktop.Deploy
private static string codeSigningPassword; private static string codeSigningPassword;
private static bool interactive;
public static void Main(string[] args) public static void Main(string[] args)
{ {
interactive = args.Length == 0;
displayHeader(); displayHeader();
findSolutionPath(); findSolutionPath();
@ -82,19 +87,20 @@ namespace osu.Desktop.Deploy
string version = $"{verBase}{increment}"; string version = $"{verBase}{increment}";
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
Console.Write($"Ready to deploy {version}: "); Console.Write($"Ready to deploy {version}!");
Console.ReadLine(); pauseIfInteractive();
sw.Start(); sw.Start();
if (!string.IsNullOrEmpty(CodeSigningCertificate)) if (!string.IsNullOrEmpty(CodeSigningCertificate))
{ {
Console.Write("Enter code signing password: "); Console.Write("Enter code signing password: ");
codeSigningPassword = readLineMasked(); codeSigningPassword = args.Length > 0 ? args[0] : readLineMasked();
} }
write("Updating AssemblyInfo..."); write("Updating AssemblyInfo...");
updateCsprojVersion(version); updateCsprojVersion(version);
updateAppveyorVersion(version);
write("Running build process..."); write("Running build process...");
foreach (string targetName in TargetNames.Split(',')) foreach (string targetName in TargetNames.Split(','))
@ -109,7 +115,7 @@ namespace osu.Desktop.Deploy
checkReleaseFiles(); checkReleaseFiles();
write("Running squirrel build..."); write("Running squirrel build...");
runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi"); runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --framework-version=net471 --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi");
//prune again to clean up before upload. //prune again to clean up before upload.
pruneReleases(); pruneReleases();
@ -124,7 +130,7 @@ namespace osu.Desktop.Deploy
updateCsprojVersion("0.0.0"); updateCsprojVersion("0.0.0");
write("Done!", ConsoleColor.White); write("Done!", ConsoleColor.White);
Console.ReadLine(); pauseIfInteractive();
} }
private static void displayHeader() private static void displayHeader()
@ -388,10 +394,37 @@ namespace osu.Desktop.Deploy
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"FATAL ERROR: {message}"); Console.WriteLine($"FATAL ERROR: {message}");
Console.ReadLine(); pauseIfInteractive();
Environment.Exit(-1); 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) private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
{ {
if (sw.ElapsedMilliseconds > 0) if (sw.ElapsedMilliseconds > 0)

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Game.props" /> <Import Project="..\osu.Game.props" />
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFrameworks>net461</TargetFrameworks> <TargetFrameworks>net471</TargetFrameworks>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -12,7 +12,8 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="NuGet.CommandLine" Version="4.5.1" /> <PackageReference Include="NuGet.CommandLine" Version="4.5.1" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net461'" /> <PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" />
<PackageReference Include="System.Management.Automation.dll" Version="10.0.10586" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Game.props" /> <Import Project="..\osu.Game.props" />
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks> <TargetFrameworks>net471;netcoreapp2.0</TargetFrameworks>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -10,11 +10,11 @@
<Title>osu!lazer</Title> <Title>osu!lazer</Title>
<Product>osu!lazer</Product> <Product>osu!lazer</Product>
<ApplicationIcon>lazer.ico</ApplicationIcon> <ApplicationIcon>lazer.ico</ApplicationIcon>
<Version>0.0.0.0</Version> <Version>0.0.0</Version>
<FileVersion>0.0.0.0</FileVersion> <FileVersion>0.0.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Defines"> <PropertyGroup Label="Defines">
<DefineConstants Condition="'$(TargetFramework)' == 'net461'">$(DefineConstants);NET_FRAMEWORK</DefineConstants> <DefineConstants Condition="'$(TargetFramework)' == 'net471'">$(DefineConstants);NET_FRAMEWORK</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<StartupObject>osu.Desktop.Program</StartupObject> <StartupObject>osu.Desktop.Program</StartupObject>
@ -31,7 +31,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" />
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net461'" /> <PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -2,13 +2,13 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "VisualTests (Debug, net461)", "name": "VisualTests (Debug, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Catch.Tests.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)", "preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,
@ -16,13 +16,13 @@
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "VisualTests (Release, net461)", "name": "VisualTests (Release, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Catch.Tests.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)", "preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,

View File

@ -9,7 +9,7 @@
"command": "msbuild", "command": "msbuild",
"args": [ "args": [
"osu.Game.Rulesets.Catch.Tests.csproj", "osu.Game.Rulesets.Catch.Tests.csproj",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -24,7 +24,7 @@
"args": [ "args": [
"osu.Game.Rulesets.Catch.Tests.csproj", "osu.Game.Rulesets.Catch.Tests.csproj",
"/p:Configuration=Release", "/p:Configuration=Release",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -66,7 +66,7 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "Restore (net461)", "label": "Restore (net471)",
"type": "shell", "type": "shell",
"command": "nuget", "command": "nuget",
"args": [ "args": [

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> <TargetFrameworks>netcoreapp2.0;net471</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />

View File

@ -2,13 +2,13 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "VisualTests (Debug, net461)", "name": "VisualTests (Debug, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Mania.Tests.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)", "preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,
@ -16,13 +16,13 @@
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "VisualTests (Release, net461)", "name": "VisualTests (Release, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Mania.Tests.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)", "preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,

View File

@ -9,7 +9,7 @@
"command": "msbuild", "command": "msbuild",
"args": [ "args": [
"osu.Game.Rulesets.Mania.Tests.csproj", "osu.Game.Rulesets.Mania.Tests.csproj",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -24,7 +24,7 @@
"args": [ "args": [
"osu.Game.Rulesets.Mania.Tests.csproj", "osu.Game.Rulesets.Mania.Tests.csproj",
"/p:Configuration=Release", "/p:Configuration=Release",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -66,7 +66,7 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "Restore (net461)", "label": "Restore (net471)",
"type": "shell", "type": "shell",
"command": "nuget", "command": "nuget",
"args": [ "args": [

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> <TargetFrameworks>netcoreapp2.0;net471</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />

View File

@ -2,13 +2,13 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "VisualTests (Debug, net461)", "name": "VisualTests (Debug, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Osu.Tests.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)", "preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,
@ -16,13 +16,13 @@
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "VisualTests (Release, net461)", "name": "VisualTests (Release, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Osu.Tests.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)", "preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,

View File

@ -9,7 +9,7 @@
"command": "msbuild", "command": "msbuild",
"args": [ "args": [
"osu.Game.Rulesets.Osu.Tests.csproj", "osu.Game.Rulesets.Osu.Tests.csproj",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -24,7 +24,7 @@
"args": [ "args": [
"osu.Game.Rulesets.Osu.Tests.csproj", "osu.Game.Rulesets.Osu.Tests.csproj",
"/p:Configuration=Release", "/p:Configuration=Release",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -66,7 +66,7 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "Restore (net461)", "label": "Restore (net471)",
"type": "shell", "type": "shell",
"command": "nuget", "command": "nuget",
"args": [ "args": [

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> <TargetFrameworks>netcoreapp2.0;net471</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />

View File

@ -7,7 +7,6 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
public class OsuEditPlayfield : OsuPlayfield public class OsuEditPlayfield : OsuPlayfield
{ {
protected override bool ProxyApproachCircles => false;
protected override bool DisplayJudgements => false; protected override bool DisplayJudgements => false;
} }
} }

View File

@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Children = new Drawable[] Children = new[]
{ {
Background = new SpinnerBackground Background = new SpinnerBackground
{ {

View File

@ -21,9 +21,6 @@ namespace osu.Game.Rulesets.Osu.UI
private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer; private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer; private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
// Todo: This should not be a thing, but is currently required for the editor
// https://github.com/ppy/osu-framework/issues/1283
protected virtual bool ProxyApproachCircles => true;
protected virtual bool DisplayJudgements => true; protected virtual bool DisplayJudgements => true;
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
@ -59,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.UI
h.OnJudgement += onJudgement; h.OnJudgement += onJudgement;
var c = h as IDrawableHitObjectWithProxiedApproach; var c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null && ProxyApproachCircles) if (c != null)
approachCircles.Add(c.ProxiedLayer.CreateProxy()); approachCircles.Add(c.ProxiedLayer.CreateProxy());
base.Add(h); base.Add(h);

View File

@ -2,13 +2,13 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "VisualTests (Debug, net461)", "name": "VisualTests (Debug, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Taiko.Tests.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)", "preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,
@ -16,13 +16,13 @@
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "VisualTests (Release, net461)", "name": "VisualTests (Release, net471)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/bin/Debug/net461/osu.Game.Rulesets.Taiko.Tests.exe", "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)", "preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null, "runtimeExecutable": null,

View File

@ -9,7 +9,7 @@
"command": "msbuild", "command": "msbuild",
"args": [ "args": [
"osu.Game.Rulesets.Taiko.Tests.csproj", "osu.Game.Rulesets.Taiko.Tests.csproj",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -24,7 +24,7 @@
"args": [ "args": [
"osu.Game.Rulesets.Taiko.Tests.csproj", "osu.Game.Rulesets.Taiko.Tests.csproj",
"/p:Configuration=Release", "/p:Configuration=Release",
"/p:TargetFramework=net461", "/p:TargetFramework=net471",
"/p:GenerateFullPaths=true", "/p:GenerateFullPaths=true",
"/m", "/m",
"/verbosity:m" "/verbosity:m"
@ -66,7 +66,7 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "Restore (net461)", "label": "Restore (net471)",
"type": "shell", "type": "shell",
"command": "nuget", "command": "nuget",
"args": [ "args": [

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> <TargetFrameworks>netcoreapp2.0;net471</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />

View File

@ -116,8 +116,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
// [TestCase(with_sb)] // [TestCase(with_sb)]
public void TestParity(string beatmap) public void TestParity(string beatmap)
{ {
var beatmaps = decode(beatmap); var legacy = decode(beatmap, out Beatmap json);
beatmaps.jsonDecoded.ShouldDeepEqual(beatmaps.legacyDecoded); json.ShouldDeepEqual(legacy);
} }
/// <summary> /// <summary>
@ -126,15 +126,20 @@ namespace osu.Game.Tests.Beatmaps.Formats
/// </summary> /// </summary>
/// <param name="filename">The .osu file to decode.</param> /// <param name="filename">The .osu file to decode.</param>
/// <returns>The <see cref="Beatmap"/> after being decoded by an <see cref="LegacyBeatmapDecoder"/>.</returns> /// <returns>The <see cref="Beatmap"/> after being decoded by an <see cref="LegacyBeatmapDecoder"/>.</returns>
private Beatmap decodeAsJson(string filename) => decode(filename).jsonDecoded; private Beatmap decodeAsJson(string filename)
{
decode(filename, out Beatmap jsonDecoded);
return jsonDecoded;
}
/// <summary> /// <summary>
/// Reads a .osu file first with a <see cref="LegacyBeatmapDecoder"/>, serializes the resulting <see cref="Beatmap"/> to JSON /// Reads a .osu file first with a <see cref="LegacyBeatmapDecoder"/>, serializes the resulting <see cref="Beatmap"/> to JSON
/// and then deserializes the result back into a <see cref="Beatmap"/> through an <see cref="JsonBeatmapDecoder"/>. /// and then deserializes the result back into a <see cref="Beatmap"/> through an <see cref="JsonBeatmapDecoder"/>.
/// </summary> /// </summary>
/// <param name="filename">The .osu file to decode.</param> /// <param name="filename">The .osu file to decode.</param>
/// <param name="jsonDecoded">The <see cref="Beatmap"/> after being decoded by an <see cref="JsonBeatmapDecoder"/>.</param>
/// <returns>The <see cref="Beatmap"/> after being decoded by an <see cref="LegacyBeatmapDecoder"/>.</returns> /// <returns>The <see cref="Beatmap"/> after being decoded by an <see cref="LegacyBeatmapDecoder"/>.</returns>
private (Beatmap legacyDecoded, Beatmap jsonDecoded) decode(string filename) private Beatmap decode(string filename, out Beatmap jsonDecoded)
{ {
using (var stream = Resource.OpenResource(filename)) using (var stream = Resource.OpenResource(filename))
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
@ -149,7 +154,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
sw.Flush(); sw.Flush();
ms.Position = 0; ms.Position = 0;
return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2));
jsonDecoded = new JsonBeatmapDecoder().Decode(sr2);
return legacyDecoded;
} }
} }
} }

View File

@ -52,6 +52,9 @@ namespace osu.Game.Tests.Visual
infoWedge.UpdateBeatmap(beatmap); infoWedge.UpdateBeatmap(beatmap);
}); });
// select part is redundant, but wait for load isn't
selectBeatmap(beatmap.Value.Beatmap);
AddWaitStep(3); AddWaitStep(3);
AddStep("hide", () => { infoWedge.State = Visibility.Hidden; }); AddStep("hide", () => { infoWedge.State = Visibility.Hidden; });
@ -63,10 +66,11 @@ namespace osu.Game.Tests.Visual
foreach (var rulesetInfo in rulesets.AvailableRulesets) foreach (var rulesetInfo in rulesets.AvailableRulesets)
{ {
var ruleset = rulesetInfo.CreateInstance(); var ruleset = rulesetInfo.CreateInstance();
beatmaps.Add(createTestBeatmap(rulesetInfo)); var testBeatmap = createTestBeatmap(rulesetInfo);
var name = rulesetInfo.ShortName; beatmaps.Add(testBeatmap);
selectBeatmap(name);
selectBeatmap(testBeatmap);
// TODO: adjust cases once more info is shown for other gamemodes // TODO: adjust cases once more info is shown for other gamemodes
switch (ruleset) switch (ruleset)
@ -108,14 +112,14 @@ namespace osu.Game.Tests.Visual
AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
} }
private void selectBeatmap(string name) private void selectBeatmap(Beatmap b)
{ {
var infoBefore = infoWedge.Info; BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null;
AddStep($"select {name} beatmap", () => AddStep($"select {b.Metadata.Title} beatmap", () =>
{ {
beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name)); infoBefore = infoWedge.Info;
infoWedge.UpdateBeatmap(beatmap); infoWedge.UpdateBeatmap(beatmap.Value = new TestWorkingBeatmap(b));
}); });
AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load"); AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load");

View File

@ -14,6 +14,8 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Users; using osu.Game.Users;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -47,11 +49,11 @@ namespace osu.Game.Tests.Visual
AddStep("scores pack 1", () => scoresContainer.Scores = scores); AddStep("scores pack 1", () => scoresContainer.Scores = scores);
AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
AddStep("only top score", () => scoresContainer.Scores = new[] { topScore }); AddStep("only top score", () => scoresContainer.Scores = new[] { topScore });
AddStep("remove scores", scoresContainer.CleanAllScores); AddStep("remove scores", () => scoresContainer.Scores = null);
AddStep("turn on loading", () => scoresContainer.IsLoading = true);
AddStep("turn off loading", () => scoresContainer.IsLoading = false);
AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapSetID = 1, OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
scores = new[] scores = new[]
{ {

View File

@ -8,6 +8,9 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Users; using osu.Game.Users;
@ -18,6 +21,26 @@ namespace osu.Game.Tests.Visual
{ {
private readonly BeatmapSetOverlay overlay; private readonly BeatmapSetOverlay overlay;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Header),
typeof(ClickableUsername),
typeof(DrawableScore),
typeof(DrawableTopScore),
typeof(ScoresContainer),
typeof(AuthorInfo),
typeof(BasicStats),
typeof(BeatmapPicker),
typeof(Details),
typeof(DownloadButton),
typeof(FavouriteButton),
typeof(Header),
typeof(HeaderButton),
typeof(Info),
typeof(PreviewButton),
typeof(SuccessRate),
};
public TestCaseBeatmapSetOverlay() public TestCaseBeatmapSetOverlay()
{ {
Add(overlay = new BeatmapSetOverlay()); Add(overlay = new BeatmapSetOverlay());
@ -29,6 +52,10 @@ namespace osu.Game.Tests.Visual
var mania = rulesets.GetRuleset(3); var mania = rulesets.GetRuleset(3);
var taiko = rulesets.GetRuleset(1); var taiko = rulesets.GetRuleset(1);
AddStep(@"show loading", () => overlay.ShowBeatmapSet(null));
AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55));
AddStep(@"show first", () => AddStep(@"show first", () =>
{ {
overlay.ShowBeatmapSet(new BeatmapSetInfo overlay.ShowBeatmapSet(new BeatmapSetInfo

View File

@ -0,0 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
public class TestCasePlayerLoader : OsuTestCase
{
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
AddStep("load dummy beatmap", () => Add(new PlayerLoader(new Player
{
InitialBeatmap = new DummyWorkingBeatmap(game),
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
})));
}
}
}

View File

@ -5,7 +5,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile;
using osu.Game.Overlays.Profile.Header; using osu.Game.Overlays.Profile.Header;
@ -17,6 +19,7 @@ namespace osu.Game.Tests.Visual
public class TestCaseUserProfile : OsuTestCase public class TestCaseUserProfile : OsuTestCase
{ {
private readonly TestUserProfileOverlay profile; private readonly TestUserProfileOverlay profile;
private APIAccess api;
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
@ -32,6 +35,12 @@ namespace osu.Game.Tests.Visual
Add(profile = new TestUserProfileOverlay()); Add(profile = new TestUserProfileOverlay());
} }
[BackgroundDependencyLoader]
private void load(APIAccess api)
{
this.api = api;
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -79,9 +88,10 @@ namespace osu.Game.Tests.Visual
{ {
Username = @"peppy", Username = @"peppy",
Id = 2, Id = 2,
IsSupporter = true,
Country = new Country { FullName = @"Australia", FlagName = @"AU" }, Country = new Country { FullName = @"Australia", FlagName = @"AU" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
})); }, api.IsLoggedIn));
checkSupporterTag(true); checkSupporterTag(true);
@ -91,7 +101,7 @@ namespace osu.Game.Tests.Visual
Id = 3103765, Id = 3103765,
Country = new Country { FullName = @"Japan", FlagName = @"JP" }, Country = new Country { FullName = @"Japan", FlagName = @"JP" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
})); }, api.IsLoggedIn));
AddStep("Hide", profile.Hide); AddStep("Hide", profile.Hide);
AddStep("Show without reload", profile.Show); AddStep("Show without reload", profile.Show);

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> <TargetFrameworks>netcoreapp2.0;net471</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />

View File

@ -21,7 +21,7 @@ namespace osu.Game.Beatmaps.Formats
return output; return output;
} }
protected abstract void ParseStreamInto(StreamReader stream, TOutput beatmap); protected abstract void ParseStreamInto(StreamReader stream, TOutput output);
} }
public abstract class Decoder public abstract class Decoder

View File

@ -13,15 +13,15 @@ namespace osu.Game.Beatmaps.Formats
AddDecoder<Beatmap>("{", m => new JsonBeatmapDecoder()); AddDecoder<Beatmap>("{", m => new JsonBeatmapDecoder());
} }
protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap) protected override void ParseStreamInto(StreamReader stream, Beatmap output)
{ {
stream.BaseStream.Position = 0; stream.BaseStream.Position = 0;
stream.DiscardBufferedData(); stream.DiscardBufferedData();
stream.ReadToEnd().DeserializeInto(beatmap); stream.ReadToEnd().DeserializeInto(output);
foreach (var hitObject in beatmap.HitObjects) foreach (var hitObject in output.HitObjects)
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); hitObject.ApplyDefaults(output.ControlPointInfo, output.BeatmapInfo.BaseDifficulty);
} }
} }
} }

View File

@ -373,17 +373,18 @@ namespace osu.Game.Beatmaps.Formats
if (parser == null) if (parser == null)
parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
var obj = parser.Parse(line); var obj = parser.Parse(line, getOffsetTime());
if (obj != null) if (obj != null)
{ {
obj.StartTime = getOffsetTime(obj.StartTime);
beatmap.HitObjects.Add(obj); beatmap.HitObjects.Add(obj);
} }
} }
private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0); private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0);
private double getOffsetTime() => ApplyOffsets ? offset : 0;
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0); private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
} }
} }

View File

@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps.Formats
FormatVersion = version; FormatVersion = version;
} }
protected override void ParseStreamInto(StreamReader stream, T beatmap) protected override void ParseStreamInto(StreamReader stream, T output)
{ {
Section section = Section.None; Section section = Section.None;
@ -33,14 +33,21 @@ namespace osu.Game.Beatmaps.Formats
{ {
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
{ {
Logger.Log($"Unknown section \"{line}\" in {beatmap}"); Logger.Log($"Unknown section \"{line}\" in {output}");
section = Section.None; section = Section.None;
} }
continue; continue;
} }
ParseLine(beatmap, section, line); try
{
ParseLine(output, section, line);
}
catch (Exception e)
{
Logger.Error(e, $"Failed to process line \"{line}\" into {output}");
}
} }
} }

View File

@ -60,6 +60,9 @@ namespace osu.Game.Configuration
Set(OsuSetting.ShowFpsDisplay, false); Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuSetting.ShowStoryboard, true); Set(OsuSetting.ShowStoryboard, true);
Set(OsuSetting.BeatmapSkins, true);
Set(OsuSetting.BeatmapHitsounds, true);
Set(OsuSetting.CursorRotation, true); Set(OsuSetting.CursorRotation, true);
Set(OsuSetting.MenuParallax, true); Set(OsuSetting.MenuParallax, true);
@ -133,6 +136,8 @@ namespace osu.Game.Configuration
Skin, Skin,
ScreenshotFormat, ScreenshotFormat,
ScreenshotCaptureMenuCursor, ScreenshotCaptureMenuCursor,
SongSelectRightMouseScroll SongSelectRightMouseScroll,
BeatmapSkins,
BeatmapHitsounds
} }
} }

View File

@ -61,9 +61,9 @@ namespace osu.Game.Graphics.Containers
AddText(text.Substring(previousLinkEnd)); AddText(text.Substring(previousLinkEnd));
} }
public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null) public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action<SpriteText> creationParameters = null)
{ {
AddInternal(new DrawableLinkCompiler(AddText(text).ToList()) AddInternal(new DrawableLinkCompiler(AddText(text, creationParameters).ToList())
{ {
TooltipText = tooltipText ?? (url != text ? url : string.Empty), TooltipText = tooltipText ?? (url != text ? url : string.Empty),
Action = () => Action = () =>
@ -71,9 +71,9 @@ namespace osu.Game.Graphics.Containers
switch (linkType) switch (linkType)
{ {
case LinkAction.OpenBeatmap: case LinkAction.OpenBeatmap:
// todo: replace this with overlay.ShowBeatmap(id) once an appropriate API call is implemented. // TODO: proper query params handling
if (int.TryParse(linkArgument, out int beatmapId)) if (linkArgument != null && int.TryParse(linkArgument.Contains('?') ? linkArgument.Split('?')[0] : linkArgument, out int beatmapId))
Process.Start($"https://osu.ppy.sh/b/{beatmapId}"); game?.ShowBeatmap(beatmapId);
break; break;
case LinkAction.OpenBeatmapSet: case LinkAction.OpenBeatmapSet:
if (int.TryParse(linkArgument, out int setId)) if (int.TryParse(linkArgument, out int setId))

View File

@ -1,14 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
namespace osu.Game.IO.Archives namespace osu.Game.IO.Archives
{ {
public abstract class ArchiveReader : IDisposable, IResourceStore<byte[]> public abstract class ArchiveReader : IResourceStore<byte[]>
{ {
/// <summary> /// <summary>
/// Opens a stream for reading a specific file from this archive. /// Opens a stream for reading a specific file from this archive.

View File

@ -5,13 +5,21 @@ namespace osu.Game.Online.API.Requests
{ {
public class GetBeatmapSetRequest : APIRequest<APIResponseBeatmapSet> public class GetBeatmapSetRequest : APIRequest<APIResponseBeatmapSet>
{ {
private readonly int beatmapSetId; private readonly int id;
private readonly BeatmapSetLookupType type;
public GetBeatmapSetRequest(int beatmapSetId) public GetBeatmapSetRequest(int id, BeatmapSetLookupType type = BeatmapSetLookupType.SetId)
{ {
this.beatmapSetId = beatmapSetId; this.id = id;
this.type = type;
} }
protected override string Target => $@"beatmapsets/{beatmapSetId}"; protected override string Target => type == BeatmapSetLookupType.SetId ? $@"beatmapsets/{id}" : $@"beatmapsets/lookup?beatmap_id={id}";
}
public enum BeatmapSetLookupType
{
SetId,
BeatmapId,
} }
} }

View File

@ -11,7 +11,7 @@ namespace osu.Game.Online.API.Requests
public class GetMessagesRequest : APIRequest<List<Message>> public class GetMessagesRequest : APIRequest<List<Message>>
{ {
private readonly List<Channel> channels; private readonly List<Channel> channels;
private long? since; private readonly long? since;
public GetMessagesRequest(List<Channel> channels, long? sinceId) public GetMessagesRequest(List<Channel> channels, long? sinceId)
{ {

View File

@ -7,7 +7,7 @@ namespace osu.Game.Online.API.Requests
{ {
public class GetUserRequest : APIRequest<User> public class GetUserRequest : APIRequest<User>
{ {
private long? userId; private readonly long? userId;
public GetUserRequest(long? userId = null) public GetUserRequest(long? userId = null)
{ {

View File

@ -158,7 +158,7 @@ namespace osu.Game
/// Show a beatmap set as an overlay. /// Show a beatmap set as an overlay.
/// </summary> /// </summary>
/// <param name="setId">The set to display.</param> /// <param name="setId">The set to display.</param>
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId); public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
/// <summary> /// <summary>
/// Show a user's profile as an overlay. /// Show a user's profile as an overlay.
@ -166,6 +166,12 @@ namespace osu.Game
/// <param name="userId">The user to display.</param> /// <param name="userId">The user to display.</param>
public void ShowUser(long userId) => userProfile.ShowUser(userId); public void ShowUser(long userId) => userProfile.ShowUser(userId);
/// <summary>
/// Show a beatmap's set as an overlay, displaying the given beatmap.
/// </summary>
/// <param name="beatmapId">The beatmap to show.</param>
public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId);
protected void LoadScore(Score s) protected void LoadScore(Score s)
{ {
scoreLoad?.Cancel(); scoreLoad?.Cancel();
@ -195,6 +201,8 @@ namespace osu.Game
return; return;
} }
Ruleset.Value = s.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap);
Beatmap.Value.Mods.Value = s.Mods; Beatmap.Value.Mods.Value = s.Mods;

View File

@ -188,6 +188,20 @@ namespace osu.Game
FileStore.Cleanup(); FileStore.Cleanup();
AddInternal(api); AddInternal(api);
GlobalActionContainer globalBinding;
CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both };
CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this)
{
RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both }
};
base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer });
KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding);
} }
private void runMigrations() private void runMigrations()
@ -217,20 +231,6 @@ namespace osu.Game
{ {
base.LoadComplete(); base.LoadComplete();
GlobalActionContainer globalBinding;
CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both };
CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this)
{
RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both }
};
base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer });
KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding);
// TODO: This is temporary until we reimplement the local FPS display. // TODO: This is temporary until we reimplement the local FPS display.
// It's just to allow end-users to access the framework FPS display without knowing the shortcut key. // It's just to allow end-users to access the framework FPS display without knowing the shortcut key.
fpsDisplayVisible = LocalConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay); fpsDisplayVisible = LocalConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay);

View File

@ -23,9 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly ClickableArea clickableArea; private readonly ClickableArea clickableArea;
private readonly FillFlowContainer fields; private readonly FillFlowContainer fields;
private UserProfileOverlay profile;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -34,28 +33,36 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
var i = BeatmapSet.OnlineInfo; updateDisplay();
}
}
avatar.User = BeatmapSet.Metadata.Author; private void updateDisplay()
clickableArea.Action = () => profile?.ShowUser(avatar.User); {
avatar.User = BeatmapSet?.Metadata.Author;
fields.Children = new Drawable[] fields.Clear();
{ if (BeatmapSet == null)
new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), return;
new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
{
Margin = new MarginPadding { Top = 5 },
},
};
if (i.Ranked.HasValue) var online = BeatmapSet.OnlineInfo;
fields.Children = new Drawable[]
{
new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"),
new Field("submitted on", online.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
{ {
fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); Margin = new MarginPadding { Top = 5 },
} },
else if (i.LastUpdated.HasValue) };
{
fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); if (online.Ranked.HasValue)
} {
fields.Add(new Field("ranked on ", online.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
}
else if (online.LastUpdated.HasValue)
{
fields.Add(new Field("last updated on ", online.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
} }
} }
@ -73,6 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet
Masking = true, Masking = true,
Child = avatar = new UpdateableAvatar Child = avatar = new UpdateableAvatar
{ {
ShowGuestOnNull = false,
Size = new Vector2(height), Size = new Vector2(height),
}, },
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
@ -95,8 +103,12 @@ namespace osu.Game.Overlays.BeatmapSet
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile) private void load(UserProfileOverlay profile)
{ {
this.profile = profile; clickableArea.Action = () =>
clickableArea.Action = () => profile?.ShowUser(avatar.User); {
if (avatar.User != null) profile?.ShowUser(avatar.User);
};
updateDisplay();
} }
private class Field : FillFlowContainer private class Field : FillFlowContainer

View File

@ -18,6 +18,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Statistic length, bpm, circleCount, sliderCount; private readonly Statistic length, bpm, circleCount, sliderCount;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -26,11 +27,12 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##"); updateDisplay();
} }
} }
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return beatmap; } get { return beatmap; }
@ -39,6 +41,22 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value; beatmap = value;
updateDisplay();
}
}
private void updateDisplay()
{
bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-";
if (beatmap == null)
{
length.Value = string.Empty;
circleCount.Value = string.Empty;
sliderCount.Value = string.Empty;
}
else
{
length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss"); length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString();
sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString();
@ -62,12 +80,19 @@ namespace osu.Game.Overlays.BeatmapSet
}; };
} }
[BackgroundDependencyLoader]
private void load()
{
updateDisplay();
}
private class Statistic : Container, IHasTooltip private class Statistic : Container, IHasTooltip
{ {
private readonly string name; private readonly string name;
private readonly OsuSpriteText value; private readonly OsuSpriteText value;
public string TooltipText => name; public string TooltipText => name;
public string Value public string Value
{ {
get { return value.Text; } get { return value.Text; }

View File

@ -41,9 +41,16 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
Beatmap.Value = BeatmapSet.Beatmaps.First(); updateDisplay();
plays.Value = BeatmapSet.OnlineInfo.PlayCount; }
favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; }
private void updateDisplay()
{
difficulties.Clear();
if (BeatmapSet != null)
{
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b) difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b)
{ {
State = DifficultySelectorState.NotSelected, State = DifficultySelectorState.NotSelected,
@ -53,14 +60,16 @@ namespace osu.Game.Overlays.BeatmapSet
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##"); starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
starRating.FadeIn(100); starRating.FadeIn(100);
}, },
OnClicked = beatmap => OnClicked = beatmap => { Beatmap.Value = beatmap; },
{
Beatmap.Value = beatmap;
},
}); });
updateDifficultyButtons();
} }
starRating.FadeOut(100);
Beatmap.Value = BeatmapSet?.Beatmaps.FirstOrDefault();
plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0;
favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0;
updateDifficultyButtons();
} }
public BeatmapPicker() public BeatmapPicker()
@ -140,6 +149,7 @@ namespace osu.Game.Overlays.BeatmapSet
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
starRating.Colour = colours.Yellow; starRating.Colour = colours.Yellow;
updateDisplay();
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -150,7 +160,10 @@ namespace osu.Game.Overlays.BeatmapSet
Beatmap.TriggerChange(); Beatmap.TriggerChange();
} }
private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version; private void showBeatmap(BeatmapInfo beatmap)
{
version.Text = beatmap?.Version;
}
private void updateDifficultyButtons() private void updateDifficultyButtons()
{ {

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using OpenTK; using OpenTK;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class DownloadButton : HeaderButton public class DownloadButton : HeaderButton
{ {

View File

@ -10,7 +10,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using OpenTK; using OpenTK;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class FavouriteButton : HeaderButton public class FavouriteButton : HeaderButton
{ {

View File

@ -1,12 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // 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;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class HeaderButton : TriangleButton public class HeaderButton : TriangleButton
{ {

View File

@ -3,6 +3,7 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -11,12 +12,11 @@ using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Direct;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Overlays.Direct;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class PreviewButton : OsuClickableContainer public class PreviewButton : OsuClickableContainer
{ {
@ -85,6 +85,8 @@ namespace osu.Game.Overlays.BeatmapSet
// prevent negative (potential infinite) width if a track without length was loaded // prevent negative (potential infinite) width if a track without length was loaded
progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f;
} }
else
progress.Width = 0;
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)

View File

@ -1,11 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Details;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -20,6 +22,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly UserRatings ratings; private readonly UserRatings ratings;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -33,19 +36,24 @@ namespace osu.Game.Overlays.BeatmapSet
} }
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return beatmap; } get { return beatmap; }
set set
{ {
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value;
basic.Beatmap = advanced.Beatmap = Beatmap; basic.Beatmap = advanced.Beatmap = beatmap = value;
ratings.Metrics = Beatmap.Metrics; updateDisplay();
} }
} }
private void updateDisplay()
{
ratings.Metrics = Beatmap?.Metrics;
}
public Details() public Details()
{ {
Width = BeatmapSetOverlay.RIGHT_WIDTH; Width = BeatmapSetOverlay.RIGHT_WIDTH;
@ -88,6 +96,12 @@ namespace osu.Game.Overlays.BeatmapSet
}; };
} }
[BackgroundDependencyLoader]
private void load()
{
updateDisplay();
}
public void StopPreview() => preview.Playing.Value = false; public void StopPreview() => preview.Playing.Value = false;
private class DetailBox : Container private class DetailBox : Container

View File

@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.BeatmapSet.Buttons;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -18,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
public class Header : Container public class Header : Container
{ {
private const float transition_duration = 250; private const float transition_duration = 200;
private const float tabs_height = 50; private const float tabs_height = 50;
private const float buttons_height = 45; private const float buttons_height = 45;
private const float buttons_spacing = 5; private const float buttons_spacing = 5;
@ -34,12 +35,13 @@ namespace osu.Game.Overlays.BeatmapSet
public Details Details; public Details Details;
private BeatmapManager beatmaps; private BeatmapManager beatmaps;
private DelayedLoadWrapper cover; private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker; public readonly BeatmapPicker Picker;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
private readonly FavouriteButton favouriteButton;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -49,15 +51,26 @@ namespace osu.Game.Overlays.BeatmapSet
beatmapSet = value; beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet;
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
onlineStatusPill.Status = BeatmapSet.OnlineInfo.Status;
downloadButtonsContainer.FadeIn(); updateDisplay();
}
}
private void updateDisplay()
{
title.Text = BeatmapSet?.Metadata.Title ?? string.Empty;
artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty;
onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
cover?.FadeOut(400, Easing.Out);
if (BeatmapSet != null)
{
downloadButtonsContainer.FadeIn(transition_duration);
favouriteButton.FadeIn(transition_duration);
noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration); noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration); videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
cover?.FadeOut(400, Easing.Out);
coverContainer.Add(cover = new DelayedLoadWrapper( coverContainer.Add(cover = new DelayedLoadWrapper(
new BeatmapSetCover(BeatmapSet) new BeatmapSetCover(BeatmapSet)
{ {
@ -71,6 +84,11 @@ namespace osu.Game.Overlays.BeatmapSet
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}); });
} }
else
{
downloadButtonsContainer.FadeOut(transition_duration);
favouriteButton.FadeOut(transition_duration);
}
} }
public Header() public Header()
@ -166,7 +184,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Top = 10 }, Margin = new MarginPadding { Top = 10 },
Children = new Drawable[] Children = new Drawable[]
{ {
new FavouriteButton(), favouriteButton = new FavouriteButton(),
downloadButtonsContainer = new Container downloadButtonsContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -238,6 +256,8 @@ namespace osu.Game.Overlays.BeatmapSet
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
beatmaps.ItemAdded += handleBeatmapAdd; beatmaps.ItemAdded += handleBeatmapAdd;
updateDisplay();
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)

View File

@ -34,11 +34,16 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
source.Text = BeatmapSet.Metadata.Source; updateDisplay();
tags.Text = BeatmapSet.Metadata.Tags;
} }
} }
private void updateDisplay()
{
source.Text = BeatmapSet?.Metadata.Source ?? string.Empty;
tags.Text = BeatmapSet?.Metadata.Tags ?? string.Empty;
}
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return successRate.Beatmap; } get { return successRate.Beatmap; }
@ -132,6 +137,8 @@ namespace osu.Game.Overlays.BeatmapSet
successRateBackground.Colour = colours.GrayE; successRateBackground.Colour = colours.GrayE;
source.TextColour = description.TextColour = colours.Gray5; source.TextColour = description.TextColour = colours.Gray5;
tags.TextColour = colours.BlueDark; tags.TextColour = colours.BlueDark;
updateDisplay();
} }
private class MetadataSection : FillFlowContainer private class MetadataSection : FillFlowContainer

View File

@ -2,15 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK; using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores
{ {
@ -22,49 +22,75 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly FillFlowContainer flow; private readonly FillFlowContainer flow;
private readonly DrawableTopScore topScore; private readonly DrawableTopScore topScore;
private readonly LoadingAnimation loadingAnimation; private readonly LoadingAnimation loadingAnimation;
private readonly Box foreground;
private bool isLoading; private bool loading
public bool IsLoading
{ {
get { return isLoading; } set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
set
{
if (isLoading == value) return;
isLoading = value;
foreground.FadeTo(isLoading ? 1 : 0, fade_duration);
loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration);
}
} }
private IEnumerable<OnlineScore> scores; private IEnumerable<OnlineScore> scores;
private BeatmapInfo beatmap;
public IEnumerable<OnlineScore> Scores public IEnumerable<OnlineScore> Scores
{ {
get { return scores; } get { return scores; }
set set
{ {
getScoresRequest?.Cancel();
scores = value; scores = value;
var scoresAmount = scores.Count();
if (scoresAmount == 0)
{
CleanAllScores();
return;
}
topScore.Score = scores.FirstOrDefault(); updateDisplay();
topScore.Show();
flow.Clear();
if (scoresAmount < 2)
return;
for (int i = 1; i < scoresAmount; i++)
flow.Add(new DrawableScore(i, scores.ElementAt(i)));
} }
} }
private GetScoresRequest getScoresRequest;
private APIAccess api;
public BeatmapInfo Beatmap
{
get => beatmap;
set
{
beatmap = value;
Scores = null;
if (beatmap?.OnlineBeatmapID.HasValue != true)
return;
loading = true;
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r => Scores = r.Scores;
api.Queue(getScoresRequest);
}
}
private void updateDisplay()
{
loading = false;
var scoreCount = scores?.Count() ?? 0;
if (scoreCount == 0)
{
topScore.Hide();
flow.Clear();
return;
}
topScore.Score = scores.FirstOrDefault();
topScore.Show();
flow.Clear();
if (scoreCount < 2)
return;
for (int i = 1; i < scoreCount; i++)
flow.Add(new DrawableScore(i, scores.ElementAt(i)));
}
public ScoresContainer() public ScoresContainer()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -93,23 +119,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
}, },
} }
}, },
foreground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.7f),
Alpha = 0,
},
loadingAnimation = new LoadingAnimation loadingAnimation = new LoadingAnimation
{ {
Alpha = 0, Alpha = 0,
Margin = new MarginPadding(20)
}, },
}; };
} }
public void CleanAllScores() [BackgroundDependencyLoader]
private void load(APIAccess api)
{ {
topScore.Hide(); this.api = api;
flow.Clear(); updateDisplay();
} }
} }
} }

View File

@ -29,18 +29,23 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value; beatmap = value;
int passCount = beatmap.OnlineInfo.PassCount; updateDisplay();
int playCount = beatmap.OnlineInfo.PlayCount;
var rate = playCount != 0 ? (float)passCount / playCount : 0;
successPercent.Text = rate.ToString("P0");
successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
graph.Metrics = Beatmap.Metrics;
} }
} }
private void updateDisplay()
{
int passCount = beatmap?.OnlineInfo.PassCount ?? 0;
int playCount = beatmap?.OnlineInfo.PlayCount ?? 0;
var rate = playCount != 0 ? (float)passCount / playCount : 0;
successPercent.Text = rate.ToString("P0");
successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
graph.Metrics = beatmap?.Metrics;
}
public SuccessRate() public SuccessRate()
{ {
Children = new Drawable[] Children = new Drawable[]
@ -74,7 +79,6 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Text = @"0%",
TextSize = 13, TextSize = 13,
}, },
}, },
@ -103,6 +107,8 @@ namespace osu.Game.Overlays.BeatmapSet
successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5; successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5;
successRate.AccentColour = colours.Green; successRate.AccentColour = colours.Green;
successRate.BackgroundColour = colours.GrayD; successRate.BackgroundColour = colours.GrayD;
updateDisplay();
} }
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()

View File

@ -17,29 +17,45 @@ using osu.Game.Online.API.Requests;
using osu.Game.Overlays.BeatmapSet; using osu.Game.Overlays.BeatmapSet;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Overlays.BeatmapSet.Scores;
using System.Linq;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public class BeatmapSetOverlay : WaveOverlayContainer public class BeatmapSetOverlay : WaveOverlayContainer
{ {
private const int fade_duration = 300;
public const float X_PADDING = 40; public const float X_PADDING = 40;
public const float RIGHT_WIDTH = 275; public const float RIGHT_WIDTH = 275;
private readonly Header header; private readonly Header header;
private readonly Info info; private readonly Info info;
private readonly ScoresContainer scores;
private APIAccess api; private APIAccess api;
private RulesetStore rulesets; private RulesetStore rulesets;
private GetScoresRequest getScoresRequest;
private readonly ScrollContainer scroll; private readonly ScrollContainer scroll;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get => beatmapSet;
set
{
if (value == beatmapSet)
return;
header.BeatmapSet = info.BeatmapSet = beatmapSet = value;
}
}
// receive input outside our bounds so we can trigger a close event on ourselves. // receive input outside our bounds so we can trigger a close event on ourselves.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
public BeatmapSetOverlay() public BeatmapSetOverlay()
{ {
ScoresContainer scores;
Waves.FirstWaveColour = OsuColour.Gray(0.4f); Waves.FirstWaveColour = OsuColour.Gray(0.4f);
Waves.SecondWaveColour = OsuColour.Gray(0.3f); Waves.SecondWaveColour = OsuColour.Gray(0.3f);
Waves.ThirdWaveColour = OsuColour.Gray(0.2f); Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
@ -88,31 +104,10 @@ namespace osu.Game.Overlays
header.Picker.Beatmap.ValueChanged += b => header.Picker.Beatmap.ValueChanged += b =>
{ {
info.Beatmap = b; info.Beatmap = b;
updateScores(b); scores.Beatmap = b;
}; };
} }
private void updateScores(BeatmapInfo beatmap)
{
getScoresRequest?.Cancel();
if (!beatmap.OnlineBeatmapID.HasValue)
{
scores.CleanAllScores();
return;
}
scores.IsLoading = true;
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r =>
{
scores.Scores = r.Scores;
scores.IsLoading = false;
};
api.Queue(getScoresRequest);
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets) private void load(APIAccess api, RulesetStore rulesets)
{ {
@ -130,7 +125,8 @@ namespace osu.Game.Overlays
{ {
base.PopOut(); base.PopOut();
header.Details.StopPreview(); header.Details.StopPreview();
FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out);
FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null);
} }
protected override bool OnClick(InputState state) protected override bool OnClick(InputState state)
@ -139,17 +135,31 @@ namespace osu.Game.Overlays
return true; return true;
} }
public void ShowBeatmapSet(int beatmapSetId) public void FetchAndShowBeatmap(int beatmapId)
{ {
// todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. BeatmapSet = null;
var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId);
req.Success += res =>
{
ShowBeatmapSet(res.ToBeatmapSet(rulesets));
header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
};
api.Queue(req);
Show();
}
public void FetchAndShowBeatmapSet(int beatmapSetId)
{
BeatmapSet = null;
var req = new GetBeatmapSetRequest(beatmapSetId); var req = new GetBeatmapSetRequest(beatmapSetId);
req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets)); req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets));
api.Queue(req); api.Queue(req);
Show();
} }
public void ShowBeatmapSet(BeatmapSetInfo set) public void ShowBeatmapSet(BeatmapSetInfo set)
{ {
header.BeatmapSet = info.BeatmapSet = set; BeatmapSet = set;
Show(); Show();
scroll.ScrollTo(0); scroll.ScrollTo(0);
} }

View File

@ -307,6 +307,8 @@ namespace osu.Game.Overlays.Chat
{ {
public override bool IsRemovable => false; public override bool IsRemovable => false;
public override bool IsSwitchable => false;
public ChannelSelectorTabItem(Channel value) : base(value) public ChannelSelectorTabItem(Channel value) : base(value)
{ {
Depth = float.MaxValue; Depth = float.MaxValue;

View File

@ -78,12 +78,7 @@ namespace osu.Game.Overlays.Direct
loadingAnimation = new LoadingAnimation(), loadingAnimation = new LoadingAnimation(),
}); });
Playing.ValueChanged += playing => Playing.ValueChanged += updatePreviewTrack;
{
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
updatePreviewTrack(playing);
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -125,6 +120,15 @@ namespace osu.Game.Overlays.Direct
private void updatePreviewTrack(bool playing) private void updatePreviewTrack(bool playing)
{ {
if (playing && BeatmapSet == null)
{
Playing.Value = false;
return;
}
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
if (playing) if (playing)
{ {
if (Preview == null) if (Preview == null)

View File

@ -92,22 +92,18 @@ namespace osu.Game.Overlays.Profile.Header
public void ShowBadges(Badge[] badges) public void ShowBadges(Badge[] badges)
{ {
switch (badges.Length) if (badges == null || badges.Length == 0)
{ {
case 0: Hide();
Hide(); return;
return;
case 1:
badgeCountText.Hide();
break;
default:
badgeCountText.Show();
badgeCountText.Text = $"{badges.Length} badges";
break;
} }
Show();
badgeCount = badges.Length; badgeCount = badges.Length;
badgeCountText.FadeTo(badgeCount > 1 ? 1 : 0);
badgeCountText.Text = $"{badges.Length} badges";
Show();
visibleBadge = 0; visibleBadge = 0;
badgeFlowContainer.Clear(); badgeFlowContainer.Clear();

View File

@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile
{ {
public class ProfileHeader : Container public class ProfileHeader : Container
{ {
private readonly OsuTextFlowContainer infoTextLeft; private readonly LinkFlowContainer infoTextLeft;
private readonly LinkFlowContainer infoTextRight; private readonly LinkFlowContainer infoTextRight;
private readonly FillFlowContainer<SpriteText> scoreText, scoreNumberText; private readonly FillFlowContainer<SpriteText> scoreText, scoreNumberText;
private readonly RankGraph rankGraph; private readonly RankGraph rankGraph;
@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Profile
} }
} }
}, },
infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14) infoTextLeft = new LinkFlowContainer(t => t.TextSize = 14)
{ {
X = UserProfileOverlay.CONTENT_X_MARGIN, X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = cover_height + 20, Y = cover_height + 20,
@ -350,7 +350,7 @@ namespace osu.Game.Overlays.Profile
if (user.Country != null) if (user.Country != null)
{ {
infoTextLeft.AddText("from ", lightText); infoTextLeft.AddText("From ", lightText);
infoTextLeft.AddText(user.Country.FullName, boldItalic); infoTextLeft.AddText(user.Country.FullName, boldItalic);
countryFlag.Country = user.Country; countryFlag.Country = user.Country;
} }
@ -378,6 +378,10 @@ namespace osu.Game.Overlays.Profile
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
} }
infoTextLeft.NewLine();
infoTextLeft.AddText("Contributed ", lightText);
infoTextLeft.AddLink($@"{user.PostCount} forum posts", url: $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: boldItalic);
string websiteWithoutProtcol = user.Website; string websiteWithoutProtcol = user.Website;
if (!string.IsNullOrEmpty(websiteWithoutProtcol)) if (!string.IsNullOrEmpty(websiteWithoutProtcol))
{ {
@ -392,8 +396,10 @@ namespace osu.Game.Overlays.Profile
infoTextRight.NewParagraph(); infoTextRight.NewParagraph();
if (!string.IsNullOrEmpty(user.Twitter)) if (!string.IsNullOrEmpty(user.Twitter))
tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website); tryAddInfoRightLine(FontAwesome.fa_gamepad, user.Discord);
tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat"); tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat");
tryAddInfoRightLine(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}");
tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website);
if (user.Statistics != null) if (user.Statistics != null)
{ {

View File

@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Sections
{ {
Action = () => Action = () =>
{ {
if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value);
}; };
Child = new FillFlowContainer Child = new FillFlowContainer

View File

@ -19,6 +19,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
public abstract class ConvertHitObjectParser : HitObjectParser public abstract class ConvertHitObjectParser : HitObjectParser
{ {
public override HitObject Parse(string text) public override HitObject Parse(string text)
{
return Parse(text, 0);
}
public HitObject Parse(string text, double offset)
{ {
try try
{ {
@ -146,7 +151,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
} }
else if ((type & ConvertHitObjectType.Spinner) > 0) else if ((type & ConvertHitObjectType.Spinner) > 0)
{ {
result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture)); result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + offset);
if (split.Length > 6) if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo); readCustomSampleBanks(split[6], bankInfo);
@ -164,13 +169,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
} }
result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime); result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime + offset);
} }
if (result == null) if (result == null)
throw new InvalidOperationException($@"Unknown hit object type {type}."); throw new InvalidOperationException($@"Unknown hit object type {type}.");
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + offset;
result.Samples = convertSoundType(soundType, bankInfo); result.Samples = convertSoundType(soundType, bankInfo);
return result; return result;

View File

@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
base.UpdateAfterChildrenLife(); base.UpdateAfterChildrenLife();
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions // We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize); speedChangeVisualiser.UpdatePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize);
} }
} }
} }

View File

@ -10,24 +10,23 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
public interface ISpeedChangeVisualiser public interface ISpeedChangeVisualiser
{ {
/// <summary> /// <summary>
/// Computes the states of <see cref="DrawableHitObject"/>s that are constant, such as lifetime and spatial length. /// Computes the states of <see cref="DrawableHitObject"/>s that remain constant while scrolling, such as lifetime and spatial length.
/// This is invoked once whenever <paramref name="timeRange"/> or <paramref name="length"/> changes. /// This is invoked once whenever <paramref name="timeRange"/> or <paramref name="length"/> changes.
/// </summary> /// </summary>
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param> /// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
/// <param name="direction">The scrolling direction.</param> /// <param name="direction">The scrolling direction.</param>
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param> /// <param name="timeRange">The duration required to scroll through one length of the screen before any speed adjustments.</param>
/// <param name="length">The length of the screen that is scrolled through.</param> /// <param name="length">The length of the screen that is scrolled through.</param>
void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length); void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length);
/// <summary> /// <summary>
/// Computes the states of <see cref="DrawableHitObject"/>s that change depending on <paramref name="currentTime"/>, such as position. /// Updates the positions of <see cref="DrawableHitObject"/>s, depending on the current time. This is invoked once per frame.
/// This is invoked once per frame.
/// </summary> /// </summary>
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param> /// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose positions should be computed.</param>
/// <param name="direction">The scrolling direction.</param> /// <param name="direction">The scrolling direction.</param>
/// <param name="currentTime">The current time.</param> /// <param name="currentTime">The current time.</param>
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param> /// <param name="timeRange">The duration required to scroll through one length of the screen before any speed adjustments.</param>
/// <param name="length">The length of the screen that is scrolled through.</param> /// <param name="length">The length of the screen that is scrolled through.</param>
void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length); void UpdatePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length);
} }
} }

View File

@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Lists; using osu.Framework.Lists;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.Timing;
using OpenTK; using OpenTK;
@ -22,24 +23,45 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
{ {
foreach (var obj in hitObjects) foreach (var obj in hitObjects)
{ {
var controlPoint = controlPointAt(obj.HitObject.StartTime); // The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases
obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier; double visibleDuration = timeRange / controlPointAt(obj.HitObject.StartTime).Multiplier;
obj.LifetimeStart = obj.HitObject.StartTime - visibleDuration;
if (obj.HitObject is IHasEndTime endTime)
{
// At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin.
// This results in a negative-position value, and the absolute of it indicates the length of the hitobject.
var hitObjectLength = -hitObjectPositionAt(obj, endTime.EndTime, timeRange);
switch (direction)
{
case ScrollingDirection.Up:
case ScrollingDirection.Down:
obj.Height = (float)(hitObjectLength * length.Y);
break;
case ScrollingDirection.Left:
case ScrollingDirection.Right:
obj.Width = (float)(hitObjectLength * length.X);
break;
}
}
if (obj.HasNestedHitObjects) if (obj.HasNestedHitObjects)
{ {
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
// Nested hitobjects don't need to scroll, but they do need accurate positions
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
} }
} }
} }
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) public void UpdatePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
{ {
foreach (var obj in hitObjects) foreach (var obj in hitObjects)
{ {
var controlPoint = controlPointAt(obj.HitObject.StartTime); var position = hitObjectPositionAt(obj, currentTime, timeRange);
var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange;
switch (direction) switch (direction)
{ {
@ -59,7 +81,28 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
} }
} }
/// <summary>
/// Computes the position of a <see cref="DrawableHitObject"/> at a point in time.
/// <para>
/// At t &lt; startTime, position &gt; 0. <br />
/// At t = startTime, position = 0. <br />
/// At t &gt; startTime, position &lt; 0.
/// </para>
/// </summary>
/// <param name="obj">The <see cref="DrawableHitObject"/>.</param>
/// <param name="time">The time to find the position of <paramref name="obj"/> at.</param>
/// <param name="timeRange">The amount of time visualised by the scrolling area.</param>
/// <returns>The position of <paramref name="obj"/> in the scrolling area at time = <paramref name="time"/>.</returns>
private double hitObjectPositionAt(DrawableHitObject obj, double time, double timeRange)
=> (obj.HitObject.StartTime - time) / timeRange * controlPointAt(obj.HitObject.StartTime).Multiplier;
private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint();
/// <summary>
/// Finds the <see cref="MultiplierControlPoint"/> which affects the speed of hitobjects at a specific time.
/// </summary>
/// <param name="time">The time which the <see cref="MultiplierControlPoint"/> should affect.</param>
/// <returns>The <see cref="MultiplierControlPoint"/>.</returns>
private MultiplierControlPoint controlPointAt(double time) private MultiplierControlPoint controlPointAt(double time)
{ {
if (controlPoints.Count == 0) if (controlPoints.Count == 0)

View File

@ -25,23 +25,25 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
{ {
foreach (var obj in hitObjects) foreach (var obj in hitObjects)
{ {
// To reduce iterations when updating hitobject positions later on, their initial positions are cached
var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange); var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange);
// Todo: This is approximate and will be incorrect in the case of extreme speed changes
obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000; obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000;
if (obj.HitObject is IHasEndTime endTime) if (obj.HitObject is IHasEndTime endTime)
{ {
var diff = positionAt(endTime.EndTime, timeRange) - startPosition; var hitObjectLength = positionAt(endTime.EndTime, timeRange) - startPosition;
switch (direction) switch (direction)
{ {
case ScrollingDirection.Up: case ScrollingDirection.Up:
case ScrollingDirection.Down: case ScrollingDirection.Down:
obj.Height = (float)(diff * length.Y); obj.Height = (float)(hitObjectLength * length.Y);
break; break;
case ScrollingDirection.Left: case ScrollingDirection.Left:
case ScrollingDirection.Right: case ScrollingDirection.Right:
obj.Width = (float)(diff * length.X); obj.Width = (float)(hitObjectLength * length.X);
break; break;
} }
} }
@ -49,12 +51,14 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
if (obj.HasNestedHitObjects) if (obj.HasNestedHitObjects)
{ {
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
// Nested hitobjects don't need to scroll, but they do need accurate positions
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
} }
} }
} }
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) public void UpdatePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
{ {
var timelinePosition = positionAt(currentTime, timeRange); var timelinePosition = positionAt(currentTime, timeRange);
@ -80,21 +84,38 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
} }
} }
/// <summary>
/// Finds the position which corresponds to a point in time.
/// This is a non-linear operation that depends on all the control points up to and including the one active at the time value.
/// </summary>
/// <param name="time">The time to find the position at.</param>
/// <param name="timeRange">The amount of time visualised by the scrolling area.</param>
/// <returns>A positive value indicating the position at <paramref name="time"/>.</returns>
private double positionAt(double time, double timeRange) private double positionAt(double time, double timeRange)
{ {
double length = 0; double length = 0;
// We need to consider all timing points until the specified time and not just the currently-active one,
// since each timing point individually affects the positions of _all_ hitobjects after its start time
for (int i = 0; i < controlPoints.Count; i++) for (int i = 0; i < controlPoints.Count; i++)
{ {
var current = controlPoints[i]; var current = controlPoints[i];
var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null; var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null;
// We don't need to consider any control points beyond the current time, since it will not yet
// affect any hitobjects
if (i > 0 && current.StartTime > time) if (i > 0 && current.StartTime > time)
continue; continue;
// Duration of the current control point // Duration of the current control point
var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime; var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime;
length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange; // We want to consider the minimal amount of time that this control point has affected,
// which may be either its duration, or the amount of time that has passed within it
var durationInCurrent = Math.Min(currentDuration, time - current.StartTime);
// Figure out how much of the time range the duration represents, and adjust it by the speed multiplier
length += durationInCurrent / timeRange * current.Multiplier;
} }
return length; return length;

View File

@ -26,14 +26,14 @@ namespace osu.Game.Screens
return false; return false;
} }
public override bool Push(Screen screen) public override void Push(Screen screen)
{ {
// When trying to push a non-loaded screen, load it asynchronously and re-invoke Push // When trying to push a non-loaded screen, load it asynchronously and re-invoke Push
// once it's done. // once it's done.
if (screen.LoadState == LoadState.NotLoaded) if (screen.LoadState == LoadState.NotLoaded)
{ {
LoadComponentAsync(screen, d => Push((BackgroundScreen)d)); LoadComponentAsync(screen, d => Push((BackgroundScreen)d));
return true; return;
} }
// Make sure the in-progress loading is complete before pushing the screen. // Make sure the in-progress loading is complete before pushing the screen.
@ -41,8 +41,6 @@ namespace osu.Game.Screens
Thread.Sleep(1); Thread.Sleep(1);
base.Push(screen); base.Push(screen);
return true;
} }
protected override void Update() protected override void Update()

View File

@ -178,7 +178,7 @@ namespace osu.Game.Screens.Edit
} }
currentScreen.Beatmap.BindTo(Beatmap); currentScreen.Beatmap.BindTo(Beatmap);
screenContainer.Add(currentScreen); LoadComponentAsync(currentScreen, screenContainer.Add);
} }
protected override bool OnWheel(InputState state) protected override bool OnWheel(InputState state)

View File

@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
var dragLayer = new DragLayer(maskContainer.Select); var dragLayer = new DragLayer(maskContainer.Select);
dragLayer.DragEnd += () => maskSelection.UpdateVisibility(); dragLayer.DragEnd += () => maskSelection.UpdateVisibility();
InternalChildren = new Drawable[] InternalChildren = new[]
{ {
dragLayer, dragLayer,
maskSelection, maskSelection,

View File

@ -43,6 +43,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
public override void Add(HitObjectMask drawable) public override void Add(HitObjectMask drawable)
{ {
if (drawable == null) throw new ArgumentNullException(nameof(drawable));
base.Add(drawable); base.Add(drawable);
drawable.Selected += onMaskSelected; drawable.Selected += onMaskSelected;
@ -53,6 +55,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
public override bool Remove(HitObjectMask drawable) public override bool Remove(HitObjectMask drawable)
{ {
if (drawable == null) throw new ArgumentNullException(nameof(drawable));
var result = base.Remove(drawable); var result = base.Remove(drawable);
if (result) if (result)

View File

@ -39,6 +39,7 @@ namespace osu.Game.Screens.Menu
private Bindable<bool> menuVoice; private Bindable<bool> menuVoice;
private Bindable<bool> menuMusic; private Bindable<bool> menuMusic;
private Track track; private Track track;
private WorkingBeatmap beatmap;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
@ -69,9 +70,8 @@ namespace osu.Game.Screens.Menu
} }
} }
Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); beatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
track = beatmap.Track;
track = Beatmap.Value.Track;
welcome = audio.Sample.Get(@"welcome"); welcome = audio.Sample.Get(@"welcome");
seeya = audio.Sample.Get(@"seeya"); seeya = audio.Sample.Get(@"seeya");
@ -81,6 +81,8 @@ namespace osu.Game.Screens.Menu
{ {
base.OnEntering(last); base.OnEntering(last);
Game.Beatmap.Value = beatmap;
if (menuVoice) if (menuVoice)
welcome.Play(); welcome.Play();

View File

@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play
private DrawableStoryboard storyboard; private DrawableStoryboard storyboard;
private Container storyboardContainer; private Container storyboardContainer;
private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true; public bool LoadedBeatmapSuccessfully => RulesetContainer?.Objects.Any() == true;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, APIAccess api, OsuConfigManager config) private void load(AudioManager audio, APIAccess api, OsuConfigManager config)
@ -86,10 +86,7 @@ namespace osu.Game.Screens.Play
WorkingBeatmap working = Beatmap.Value; WorkingBeatmap working = Beatmap.Value;
if (working is DummyWorkingBeatmap) if (working is DummyWorkingBeatmap)
{
Exit();
return; return;
}
sampleRestart = audio.Sample.Get(@"Gameplay/restart"); sampleRestart = audio.Sample.Get(@"Gameplay/restart");
@ -122,14 +119,15 @@ namespace osu.Game.Screens.Play
} }
if (!RulesetContainer.Objects.Any()) if (!RulesetContainer.Objects.Any())
throw new InvalidOperationException("Beatmap contains no hit objects!"); {
Logger.Error(new InvalidOperationException("Beatmap contains no hit objects!"), "Beatmap contains no hit objects!");
return;
}
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error(e, "Could not load beatmap sucessfully!"); Logger.Error(e, "Could not load beatmap sucessfully!");
//couldn't load, hard abort! //couldn't load, hard abort!
Exit();
return; return;
} }
@ -293,7 +291,7 @@ namespace osu.Game.Screens.Play
{ {
base.OnEntering(last); base.OnEntering(last);
if (!loadedSuccessfully) if (!LoadedBeatmapSuccessfully)
return; return;
Content.Alpha = 0; Content.Alpha = 0;
@ -343,7 +341,7 @@ namespace osu.Game.Screens.Play
return base.OnExiting(next); return base.OnExiting(next);
} }
if (loadedSuccessfully) if (LoadedBeatmapSuccessfully)
pauseContainer?.Pause(); pauseContainer?.Pause();
return true; return true;

View File

@ -159,14 +159,14 @@ namespace osu.Game.Screens.Play
loadTask = null; loadTask = null;
if (!Push(player)) //By default, we want to load the player and never be returned to.
Exit(); //Note that this may change if the player we load requested a re-run.
ValidForResume = false;
if (player.LoadedBeatmapSuccessfully)
Push(player);
else else
{ Exit();
//By default, we want to load the player and never be returned to.
//Note that this may change if the player we load requested a re-run.
ValidForResume = false;
}
}); });
}, 500); }, 500);
} }

View File

@ -15,6 +15,8 @@ namespace osu.Game.Screens.Play.PlayerSettings
private readonly PlayerSliderBar<double> dimSliderBar; private readonly PlayerSliderBar<double> dimSliderBar;
private readonly PlayerSliderBar<double> blurSliderBar; private readonly PlayerSliderBar<double> blurSliderBar;
private readonly PlayerCheckbox showStoryboardToggle; private readonly PlayerCheckbox showStoryboardToggle;
private readonly PlayerCheckbox beatmapSkinsToggle;
private readonly PlayerCheckbox beatmapHitsoundsToggle;
public VisualSettings() public VisualSettings()
{ {
@ -34,7 +36,9 @@ namespace osu.Game.Screens.Play.PlayerSettings
{ {
Text = "Toggles:" Text = "Toggles:"
}, },
showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" } showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" },
beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" },
beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }
}; };
} }
@ -44,6 +48,8 @@ namespace osu.Game.Screens.Play.PlayerSettings
dimSliderBar.Bindable = config.GetBindable<double>(OsuSetting.DimLevel); dimSliderBar.Bindable = config.GetBindable<double>(OsuSetting.DimLevel);
blurSliderBar.Bindable = config.GetBindable<double>(OsuSetting.BlurLevel); blurSliderBar.Bindable = config.GetBindable<double>(OsuSetting.BlurLevel);
showStoryboardToggle.Bindable = config.GetBindable<bool>(OsuSetting.ShowStoryboard); showStoryboardToggle.Bindable = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
beatmapSkinsToggle.Bindable = config.GetBindable<bool>(OsuSetting.BeatmapSkins);
beatmapHitsoundsToggle.Bindable = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds);
} }
} }
} }

View File

@ -223,11 +223,11 @@ namespace osu.Game.Screens.Ranking
private class DateTimeDisplay : Container private class DateTimeDisplay : Container
{ {
private DateTime datetime; private readonly DateTime date;
public DateTimeDisplay(DateTime datetime) public DateTimeDisplay(DateTime date)
{ {
this.datetime = datetime; this.date = date;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
@ -251,7 +251,7 @@ namespace osu.Game.Screens.Ranking
{ {
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Text = datetime.ToShortDateString(), Text = date.ToShortDateString(),
Padding = new MarginPadding { Horizontal = 10, Vertical = 5 }, Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Colour = Color4.White, Colour = Color4.White,
}, },
@ -259,7 +259,7 @@ namespace osu.Game.Screens.Ranking
{ {
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Text = datetime.ToShortTimeString(), Text = date.ToShortTimeString(),
Padding = new MarginPadding { Horizontal = 10, Vertical = 5 }, Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Colour = Color4.White, Colour = Color4.White,
} }

View File

@ -16,11 +16,22 @@ namespace osu.Game.Screens.Select.Carousel
protected List<CarouselItem> InternalChildren = new List<CarouselItem>(); protected List<CarouselItem> InternalChildren = new List<CarouselItem>();
/// <summary>
/// Used to assign a monotonically increasing ID to children as they are added. This member is
/// incremented whenever a child is added.
/// </summary>
private ulong currentChildID;
public override List<DrawableCarouselItem> Drawables public override List<DrawableCarouselItem> Drawables
{ {
get get
{ {
var drawables = base.Drawables; var drawables = base.Drawables;
// if we are explicitly not present, don't ever present children.
// without this check, children drawables can potentially be presented without their group header.
if (DrawableRepresentation.Value?.IsPresent == false) return drawables;
foreach (var c in InternalChildren) foreach (var c in InternalChildren)
drawables.AddRange(c.Drawables); drawables.AddRange(c.Drawables);
return drawables; return drawables;
@ -39,6 +50,7 @@ namespace osu.Game.Screens.Select.Carousel
public virtual void AddChild(CarouselItem i) public virtual void AddChild(CarouselItem i)
{ {
i.State.ValueChanged += v => ChildItemStateChanged(i, v); i.State.ValueChanged += v => ChildItemStateChanged(i, v);
i.ChildID = ++currentChildID;
InternalChildren.Add(i); InternalChildren.Add(i);
} }

View File

@ -24,16 +24,18 @@ namespace osu.Game.Screens.Select.Carousel
{ {
var items = new List<DrawableCarouselItem>(); var items = new List<DrawableCarouselItem>();
var self = drawableRepresentation.Value; var self = DrawableRepresentation.Value;
if (self?.IsPresent == true) items.Add(self); if (self?.IsPresent == true) items.Add(self);
return items; return items;
} }
} }
private int creationOrder;
protected CarouselItem() protected CarouselItem()
{ {
drawableRepresentation = new Lazy<DrawableCarouselItem>(CreateDrawableRepresentation); DrawableRepresentation = new Lazy<DrawableCarouselItem>(CreateDrawableRepresentation);
Filtered.ValueChanged += v => Filtered.ValueChanged += v =>
{ {
@ -42,15 +44,23 @@ namespace osu.Game.Screens.Select.Carousel
}; };
} }
private readonly Lazy<DrawableCarouselItem> drawableRepresentation; protected readonly Lazy<DrawableCarouselItem> DrawableRepresentation;
/// <summary>
/// Used as a default sort method for <see cref="CarouselItem"/>s of differing types.
/// </summary>
internal ulong ChildID;
/// <summary>
/// Create a fresh drawable version of this item. If you wish to consume the current representation, use <see cref="DrawableRepresentation"/> instead.
/// </summary>
protected abstract DrawableCarouselItem CreateDrawableRepresentation(); protected abstract DrawableCarouselItem CreateDrawableRepresentation();
public virtual void Filter(FilterCriteria criteria) public virtual void Filter(FilterCriteria criteria)
{ {
} }
public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => GetHashCode().CompareTo(other.GetHashCode()); public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => ChildID.CompareTo(other.ChildID);
} }
public enum CarouselItemState public enum CarouselItemState

View File

@ -17,6 +17,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -35,6 +36,8 @@ namespace osu.Game.Screens.Select.Carousel
private Triangles triangles; private Triangles triangles;
private StarCounter starCounter; private StarCounter starCounter;
private BeatmapSetOverlay beatmapOverlay;
public DrawableCarouselBeatmap(CarouselBeatmap panel) : base(panel) public DrawableCarouselBeatmap(CarouselBeatmap panel) : base(panel)
{ {
beatmap = panel.Beatmap; beatmap = panel.Beatmap;
@ -42,8 +45,10 @@ namespace osu.Game.Screens.Select.Carousel
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(SongSelect songSelect, BeatmapManager manager) private void load(SongSelect songSelect, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay)
{ {
this.beatmapOverlay = beatmapOverlay;
if (songSelect != null) if (songSelect != null)
{ {
startRequested = songSelect.FinaliseSelection; startRequested = songSelect.FinaliseSelection;
@ -171,6 +176,10 @@ namespace osu.Game.Screens.Select.Carousel
new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested?.Invoke(beatmap)), new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested?.Invoke(beatmap)),
new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap)), new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap)),
new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap)), new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap)),
new OsuMenuItem("Details", MenuItemType.Standard, () =>
{
if (beatmap.OnlineBeatmapID.HasValue) beatmapOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value);
}),
}; };
} }
} }

View File

@ -46,7 +46,7 @@ namespace osu.Game.Screens.Select.Carousel
restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore);
dialogOverlay = overlay; dialogOverlay = overlay;
if (beatmapOverlay != null) if (beatmapOverlay != null)
viewDetails = beatmapOverlay.ShowBeatmapSet; viewDetails = beatmapOverlay.FetchAndShowBeatmapSet;
Children = new Drawable[] Children = new Drawable[]
{ {

View File

@ -40,10 +40,10 @@ namespace osu.Game.Screens.Select.Details
firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0; firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0;
} }
hpDrain.Value = beatmap.BaseDifficulty?.DrainRate ?? 0; hpDrain.Value = Beatmap?.BaseDifficulty?.DrainRate ?? 0;
accuracy.Value = beatmap.BaseDifficulty?.OverallDifficulty ?? 0; accuracy.Value = Beatmap?.BaseDifficulty?.OverallDifficulty ?? 0;
approachRate.Value = beatmap.BaseDifficulty?.ApproachRate ?? 0; approachRate.Value = Beatmap?.BaseDifficulty?.ApproachRate ?? 0;
starDifficulty.Value = (float)beatmap.StarDifficulty; starDifficulty.Value = (float)(Beatmap?.StarDifficulty ?? 0);
} }
} }

View File

@ -25,10 +25,10 @@ namespace osu.Game.Screens.Select.Details
if (value == metrics) return; if (value == metrics) return;
metrics = value; metrics = value;
var retries = Metrics.Retries; var retries = Metrics?.Retries ?? new int[0];
var fails = Metrics.Fails; var fails = Metrics?.Fails ?? new int[0];
float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max(); float maxValue = fails.Any() ? fails.Zip(retries, (fail, retry) => fail + retry).Max() : 0;
failGraph.MaxValue = maxValue; failGraph.MaxValue = maxValue;
retryGraph.MaxValue = maxValue; retryGraph.MaxValue = maxValue;

View File

@ -21,6 +21,7 @@ namespace osu.Game.Screens.Select.Details
private readonly BarGraph graph; private readonly BarGraph graph;
private BeatmapMetrics metrics; private BeatmapMetrics metrics;
public BeatmapMetrics Metrics public BeatmapMetrics Metrics
{ {
get { return metrics; } get { return metrics; }
@ -31,15 +32,25 @@ namespace osu.Game.Screens.Select.Details
const int rating_range = 10; const int rating_range = 10;
var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. if (metrics == null)
{
negativeRatings.Text = "0";
positiveRatings.Text = "0";
ratingsBar.Length = 0;
graph.Values = new float[rating_range];
}
else
{
var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0.
var negativeCount = ratings.Take(rating_range / 2).Sum(); var negativeCount = ratings.Take(rating_range / 2).Sum();
var totalCount = ratings.Sum(); var totalCount = ratings.Sum();
negativeRatings.Text = negativeCount.ToString(); negativeRatings.Text = negativeCount.ToString();
positiveRatings.Text = (totalCount - negativeCount).ToString(); positiveRatings.Text = (totalCount - negativeCount).ToString();
ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount;
graph.Values = ratings.Take(rating_range).Select(r => (float)r); graph.Values = ratings.Take(rating_range).Select(r => (float)r);
}
} }
} }

View File

@ -113,6 +113,31 @@ namespace osu.Game.Skinning
string path = getPathForFile(name); string path = getPathForFile(name);
return path == null ? null : underlyingStore.Get(path); return path == null ? null : underlyingStore.Get(path);
} }
#region IDisposable Support
private bool isDisposed;
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
isDisposed = true;
}
}
~LegacySkinResourceStore()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
} }
} }
} }

View File

@ -12,7 +12,7 @@ namespace osu.Game.Skinning
{ {
} }
protected override void ParseLine(SkinConfiguration output, Section section, string line) protected override void ParseLine(SkinConfiguration skin, Section section, string line)
{ {
switch (section) switch (section)
{ {
@ -22,17 +22,17 @@ namespace osu.Game.Skinning
switch (pair.Key) switch (pair.Key)
{ {
case @"Name": case @"Name":
output.SkinInfo.Name = pair.Value; skin.SkinInfo.Name = pair.Value;
break; break;
case @"Author": case @"Author":
output.SkinInfo.Creator = pair.Value; skin.SkinInfo.Creator = pair.Value;
break; break;
} }
break; break;
} }
base.ParseLine(output, section, line); base.ParseLine(skin, section, line);
} }
} }
} }

View File

@ -4,9 +4,11 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Configuration;
namespace osu.Game.Skinning namespace osu.Game.Skinning
{ {
@ -14,17 +16,37 @@ namespace osu.Game.Skinning
{ {
public event Action SourceChanged; public event Action SourceChanged;
public Drawable GetDrawableComponent(string componentName) => source.GetDrawableComponent(componentName) ?? fallbackSource?.GetDrawableComponent(componentName); private Bindable<bool> beatmapSkins = new Bindable<bool>();
private Bindable<bool> beatmapHitsounds = new Bindable<bool>();
public Texture GetTexture(string componentName) => source.GetTexture(componentName) ?? fallbackSource.GetTexture(componentName); public Drawable GetDrawableComponent(string componentName)
{
Drawable sourceDrawable;
if (beatmapSkins && (sourceDrawable = source.GetDrawableComponent(componentName)) != null)
return sourceDrawable;
return fallbackSource?.GetDrawableComponent(componentName);
}
public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName); public Texture GetTexture(string componentName)
{
Texture sourceTexture;
if (beatmapSkins && (sourceTexture = source.GetTexture(componentName)) != null)
return sourceTexture;
return fallbackSource.GetTexture(componentName);
}
public SampleChannel GetSample(string sampleName)
{
SampleChannel sourceChannel;
if (beatmapHitsounds && (sourceChannel = source.GetSample(sampleName)) != null)
return sourceChannel;
return fallbackSource?.GetSample(sampleName);
}
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct
{ {
TValue? val = null; TValue? val = null;
var conf = (source as Skin)?.Configuration as TConfiguration; if ((source as Skin)?.Configuration is TConfiguration conf)
if (conf != null)
val = query?.Invoke(conf); val = query?.Invoke(conf);
return val ?? fallbackSource?.GetValue(query); return val ?? fallbackSource?.GetValue(query);
@ -33,8 +55,7 @@ namespace osu.Game.Skinning
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class
{ {
TValue val = null; TValue val = null;
var conf = (source as Skin)?.Configuration as TConfiguration; if ((source as Skin)?.Configuration is TConfiguration conf)
if (conf != null)
val = query?.Invoke(conf); val = query?.Invoke(conf);
return val ?? fallbackSource?.GetValue(query); return val ?? fallbackSource?.GetValue(query);
@ -60,6 +81,18 @@ namespace osu.Game.Skinning
return dependencies; return dependencies;
} }
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
beatmapSkins = config.GetBindable<bool>(OsuSetting.BeatmapSkins);
beatmapSkins.ValueChanged += val => onSourceChanged();
beatmapSkins.TriggerChange();
beatmapHitsounds = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds);
beatmapHitsounds.ValueChanged += val => onSourceChanged();
beatmapHitsounds.TriggerChange();
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();

View File

@ -15,18 +15,14 @@ namespace osu.Game.Tests.Visual
{ {
protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location; protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location;
private readonly TestCaseTestRunner.TestRunner runner; private TestCaseTestRunner.TestRunner runner;
public OsuTestCaseTestRunner() protected override void LoadAsyncComplete()
{ {
runner = new TestCaseTestRunner.TestRunner(); // this has to be run here rather than LoadComplete because
} // TestCase.cs is checking the IsLoaded state (on another thread) and expects
// the runner to be loaded at that point.
protected override void LoadComplete() Add(runner = new TestCaseTestRunner.TestRunner());
{
base.LoadComplete();
Add(runner);
} }
public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test); public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test);

View File

@ -15,6 +15,11 @@ namespace osu.Game.Users
private User user; private User user;
/// <summary>
/// Whether to show a default guest representation on null user (as opposed to nothing).
/// </summary>
public bool ShowGuestOnNull = true;
public User User public User User
{ {
get { return user; } get { return user; }
@ -40,13 +45,16 @@ namespace osu.Game.Users
{ {
displayedAvatar?.FadeOut(300); displayedAvatar?.FadeOut(300);
displayedAvatar?.Expire(); displayedAvatar?.Expire();
Add(displayedAvatar = new DelayedLoadWrapper( if (user != null || ShowGuestOnNull)
new Avatar(user) {
{ Add(displayedAvatar = new DelayedLoadWrapper(
RelativeSizeAxes = Axes.Both, new Avatar(user)
OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), {
}) RelativeSizeAxes = Axes.Both,
); OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint),
})
);
}
} }
} }
} }

View File

@ -98,9 +98,15 @@ namespace osu.Game.Users
[JsonProperty(@"skype")] [JsonProperty(@"skype")]
public string Skype; public string Skype;
[JsonProperty(@"discord")]
public string Discord;
[JsonProperty(@"website")] [JsonProperty(@"website")]
public string Website; public string Website;
[JsonProperty(@"post_count")]
public int PostCount;
[JsonProperty(@"playstyle")] [JsonProperty(@"playstyle")]
public string[] PlayStyle; public string[] PlayStyle;

View File

@ -12,12 +12,13 @@ namespace osu.Game.Users
public abstract Color4 GetAppropriateColour(OsuColour colours); public abstract Color4 GetAppropriateColour(OsuColour colours);
} }
public abstract class UserStatusAvailable : UserStatus public class UserStatusOnline : UserStatus
{ {
public override string Message => @"Online";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.BlueDarker; public override Color4 GetAppropriateColour(OsuColour colours) => colours.BlueDarker;
} }
public abstract class UserStatusBusy : UserStatus public abstract class UserStatusBusy : UserStatusOnline
{ {
public override Color4 GetAppropriateColour(OsuColour colours) => colours.YellowDark; public override Color4 GetAppropriateColour(OsuColour colours) => colours.YellowDark;
} }
@ -28,17 +29,12 @@ namespace osu.Game.Users
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray7; public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray7;
} }
public class UserStatusOnline : UserStatusAvailable public class UserStatusSpectating : UserStatusOnline
{
public override string Message => @"Online";
}
public class UserStatusSpectating : UserStatusAvailable
{ {
public override string Message => @"Spectating a game"; public override string Message => @"Spectating a game";
} }
public class UserStatusInLobby : UserStatusAvailable public class UserStatusInLobby : UserStatusOnline
{ {
public override string Message => @"in Multiplayer Lobby"; public override string Message => @"in Multiplayer Lobby";
} }
@ -53,13 +49,13 @@ namespace osu.Game.Users
public override string Message => @"Multiplaying"; public override string Message => @"Multiplaying";
} }
public class UserStatusModding : UserStatus public class UserStatusModding : UserStatusOnline
{ {
public override string Message => @"Modding a map"; public override string Message => @"Modding a map";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark; public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark;
} }
public class UserStatusDoNotDisturb : UserStatus public class UserStatusDoNotDisturb : UserStatusBusy
{ {
public override string Message => @"Do not disturb"; public override string Message => @"Do not disturb";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark; public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark;