1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 05:27:23 +08:00

Merge remote-tracking branch 'upstream/master' into reverse-winding

This commit is contained in:
unknown 2020-01-26 16:19:57 +08:00
commit 12b9e8146e
593 changed files with 10248 additions and 3887 deletions

View File

@ -176,8 +176,8 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
#Style - C# 8 features
csharp_prefer_static_local_function = true:warning
csharp_prefer_simple_using_statement = true:silent
csharp_style_prefer_index_operator = false:none
csharp_style_prefer_range_operator = false:none
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_range_operator = true:warning
csharp_style_prefer_switch_expression = false:none
#Supressing roslyn built-in analyzers

3
.gitignore vendored
View File

@ -331,3 +331,6 @@ fastlane/report.xml
# inspectcode
inspectcodereport.xml
inspectcode
# BenchmarkDotNet
/BenchmarkDotNet.Artifacts

View File

@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll" />
<option name="PROGRAM_PARAMETERS" value="--filter *" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/osu.Game.Benchmarks.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=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="CatchRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<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=".NETCoreApp,Version=v3.0" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ManiaRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<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=".NETCoreApp,Version=v3.0" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="OsuRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<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=".NETCoreApp,Version=v3.0" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="TaikoRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<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=".NETCoreApp,Version=v3.0" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tournament" type="DotNetProject" factoryName=".NET Project" folderName="Tournament">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.0/osu!.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--tournament" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<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=".NETCoreApp,Version=v3.0" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
</method>

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tournament (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Tournament">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tournament.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tournament.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tournament.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<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=".NETCoreApp,Version=v2.1" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.0/osu!.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<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=".NETCoreApp,Version=v3.0" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
</method>

View File

@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! SDL" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--sdl" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.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=".NETCoreApp,Version=v3.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp3.0/osu.Game.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<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=".NETCoreApp,Version=v3.0" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
</method>

51
.vscode/launch.json vendored
View File

@ -1,18 +1,19 @@
{
"version": "0.2.0",
"configurations": [{
"configurations": [
{
"name": "osu! (Debug)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.0/osu!.dll"
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -23,13 +24,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.0/osu!.dll"
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1/osu!.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -40,29 +41,30 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.0/osu.Game.Tests.dll"
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
}, {
},
{
"name": "osu! (Tests, Release)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.0/osu.Game.Tests.dll"
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.1/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -73,14 +75,14 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.0/osu!.dll",
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll",
"--tournament"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -91,14 +93,14 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.0/osu!.dll",
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1/osu!.dll",
"--tournament"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -109,14 +111,14 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.0/osu.Game.Tournament.Tests.dll",
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tournament.Tests.dll",
"--tournament"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -127,18 +129,31 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.0/osu.Game.Tournament.Tests.dll",
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tournament.Tests.dll",
"--tournament"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
"name": "Benchmark",
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll",
"args": [
"--filter",
"*"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build benchmarks",
"console": "internalConsole"
},
{
"name": "Cake: Debug Script",
"type": "coreclr",

24
.vscode/tasks.json vendored
View File

@ -2,7 +2,8 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [{
"tasks": [
{
"label": "Build osu! (Debug)",
"type": "shell",
"command": "dotnet",
@ -78,7 +79,8 @@
],
"group": "build",
"problemMatcher": "$msCompile"
}, {
},
{
"label": "Build tournament tests (Release)",
"type": "shell",
"command": "dotnet",
@ -95,7 +97,23 @@
"problemMatcher": "$msCompile"
},
{
"label": "Restore (netcoreapp3.0)",
"label": "Build benchmarks",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Benchmarks",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Restore (netcoreapp3.1)",
"type": "shell",
"command": "dotnet",
"args": [

58
CodeAnalysis/osu.ruleset Normal file
View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="osu! Rule Set" Description=" " ToolsVersion="16.0">
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1016" Action="None" />
<Rule Id="CA1028" Action="None" />
<Rule Id="CA1031" Action="None" />
<Rule Id="CA1034" Action="None" />
<Rule Id="CA1036" Action="None" />
<Rule Id="CA1040" Action="None" />
<Rule Id="CA1044" Action="None" />
<Rule Id="CA1051" Action="None" />
<Rule Id="CA1054" Action="None" />
<Rule Id="CA1056" Action="None" />
<Rule Id="CA1062" Action="None" />
<Rule Id="CA1063" Action="None" />
<Rule Id="CA1067" Action="None" />
<Rule Id="CA1707" Action="None" />
<Rule Id="CA1710" Action="None" />
<Rule Id="CA1714" Action="None" />
<Rule Id="CA1716" Action="None" />
<Rule Id="CA1717" Action="None" />
<Rule Id="CA1720" Action="None" />
<Rule Id="CA1721" Action="None" />
<Rule Id="CA1724" Action="None" />
<Rule Id="CA1801" Action="None" />
<Rule Id="CA1806" Action="None" />
<Rule Id="CA1812" Action="None" />
<Rule Id="CA1814" Action="None" />
<Rule Id="CA1815" Action="None" />
<Rule Id="CA1819" Action="None" />
<Rule Id="CA1822" Action="None" />
<Rule Id="CA1823" Action="None" />
<Rule Id="CA2007" Action="None" />
<Rule Id="CA2214" Action="None" />
<Rule Id="CA2227" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.CSharp.Analyzers" RuleNamespace="Microsoft.CodeQuality.CSharp.Analyzers">
<Rule Id="CA1001" Action="None" />
<Rule Id="CA1032" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="None" />
<Rule Id="CA1304" Action="None" />
<Rule Id="CA1305" Action="None" />
<Rule Id="CA1307" Action="None" />
<Rule Id="CA1308" Action="None" />
<Rule Id="CA1816" Action="None" />
<Rule Id="CA1826" Action="None" />
<Rule Id="CA2000" Action="None" />
<Rule Id="CA2008" Action="None" />
<Rule Id="CA2213" Action="None" />
<Rule Id="CA2235" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.CSharp.Analyzers" RuleNamespace="Microsoft.NetCore.CSharp.Analyzers">
<Rule Id="CA1309" Action="Warning" />
<Rule Id="CA2201" Action="Warning" />
</Rules>
</RuleSet>

View File

@ -18,7 +18,11 @@
<ItemGroup Label="Code Analysis">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="2.9.8" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Label="Code Analysis">
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
@ -36,7 +40,7 @@
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
<PackageReleaseNotes>Automated release.</PackageReleaseNotes>
<Company>ppy Pty Ltd</Company>
<Copyright>Copyright (c) 2019 ppy Pty Ltd</Copyright>
<Copyright>Copyright (c) 2020 ppy Pty Ltd</Copyright>
<PackageTags>osu game</PackageTags>
</PropertyGroup>
</Project>

View File

@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.1)
CFPropertyList (3.0.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
atomos (0.1.3)
@ -18,8 +18,8 @@ GEM
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.5)
emoji_regex (1.0.1)
excon (0.67.0)
faraday (0.15.4)
excon (0.71.1)
faraday (0.17.3)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
@ -27,7 +27,7 @@ GEM
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.7)
fastlane (2.133.0)
fastlane (2.140.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
@ -36,13 +36,13 @@ GEM
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 2.0)
excon (>= 0.45.0, < 1.0.0)
faraday (< 0.16.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 0.17)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (< 0.16.0)
faraday_middleware (~> 0.13.1)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.21.2, < 0.24.0)
google-api-client (>= 0.29.2, < 0.37.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
@ -61,56 +61,58 @@ GEM
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.8.1, < 2.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-clean_testflight_testers (0.3.0)
fastlane-plugin-souyuz (0.8.1)
souyuz (>= 0.8.1)
fastlane-plugin-souyuz (0.9.1)
souyuz (= 0.9.1)
fastlane-plugin-xamarin (0.6.3)
gh_inspector (1.1.3)
google-api-client (0.23.9)
google-api-client (0.36.4)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.5, < 0.7.0)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.9)
google-cloud-core (1.3.1)
signet (~> 0.12)
google-cloud-core (1.5.0)
google-cloud-env (~> 1.0)
google-cloud-env (1.2.1)
google-cloud-errors (~> 1.0)
google-cloud-env (1.3.0)
faraday (~> 0.11)
google-cloud-storage (1.16.0)
google-cloud-errors (1.0.0)
google-cloud-storage (1.25.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.23)
google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
googleauth (>= 0.6.2, < 0.10.0)
googleauth (0.6.7)
googleauth (~> 0.9)
mini_mime (~> 1.0)
googleauth (0.10.0)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.7)
signet (~> 0.12)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
json (2.2.0)
json (2.3.0)
jwt (2.1.0)
memoist (0.16.0)
mime-types (3.3)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.1009)
mini_magick (4.9.5)
memoist (0.16.2)
mini_magick (4.10.1)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
multi_json (1.13.1)
multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.6)
naturally (2.2.0)
nokogiri (1.10.4)
nokogiri (1.10.7)
mini_portile2 (~> 2.4.0)
os (1.0.1)
plist (3.5.0)
@ -128,12 +130,12 @@ GEM
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.6)
simctl (1.6.7)
CFPropertyList
naturally
slack-notifier (2.3.2)
souyuz (0.8.1)
fastlane (>= 2.29.0)
souyuz (0.9.1)
fastlane (>= 1.103.0)
highline (~> 1.7)
nokogiri (~> 1.7)
terminal-notifier (2.0.0)
@ -141,15 +143,15 @@ GEM
unicode-display_width (~> 1.1, >= 1.1.1)
tty-cursor (0.7.0)
tty-screen (0.7.0)
tty-spinner (0.9.1)
tty-spinner (0.9.2)
tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
unicode-display_width (1.6.0)
unicode-display_width (1.6.1)
word_wrap (1.0.0)
xcodeproj (1.12.0)
xcodeproj (1.14.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)

View File

@ -1,4 +1,4 @@
Copyright (c) 2019 ppy Pty Ltd <contact@ppy.sh>.
Copyright (c) 2020 ppy Pty Ltd <contact@ppy.sh>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -21,9 +21,9 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
## Requirements
- A desktop platform with the [.NET Core SDK 3.0](https://www.microsoft.com/net/learn/get-started) or higher installed.
- A desktop platform with the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) or higher installed.
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore30&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
@ -35,7 +35,7 @@ If you are not interested in developing the game, you can still consume our [bin
**Latest build:**
| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [iOS(iOS 10+)](https://testflight.apple.com/join/2tLcjWlF) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
| ------------- | ------------- | ------------- | ------------- |
- **Linux** users are recommended to self-compile until we have official deployment in place.

View File

@ -1,5 +1,5 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.33"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.2.1"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.0"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();

View File

@ -1,4 +1,9 @@
{
"sdk": {
"allowPrerelease": false,
"rollForward": "minor",
"version": "3.1.100"
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "2.0.24"
}

View File

@ -53,7 +53,7 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.125.0" />
</ItemGroup>
</Project>

View File

@ -3,6 +3,7 @@
"path": "osu.sln",
"projects": [
"osu.Desktop\\osu.Desktop.csproj",
"osu.Game.Benchmarks\\osu.Game.Benchmarks.csproj",
"osu.Game.Rulesets.Catch.Tests\\osu.Game.Rulesets.Catch.Tests.csproj",
"osu.Game.Rulesets.Catch\\osu.Game.Rulesets.Catch.csproj",
"osu.Game.Rulesets.Mania.Tests\\osu.Game.Rulesets.Mania.Tests.csproj",

View File

@ -0,0 +1,149 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Text;
using DiscordRPC;
using DiscordRPC.Message;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Users;
using LogLevel = osu.Framework.Logging.LogLevel;
using User = osu.Game.Users.User;
namespace osu.Desktop
{
internal class DiscordRichPresence : Component
{
private const string client_id = "367827983903490050";
private DiscordRpcClient client;
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
private Bindable<User> user;
private readonly IBindable<UserStatus> status = new Bindable<UserStatus>();
private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>();
private readonly RichPresence presence = new RichPresence
{
Assets = new Assets { LargeImageKey = "osu_logo_lazer", }
};
[BackgroundDependencyLoader]
private void load(IAPIProvider provider)
{
client = new DiscordRpcClient(client_id)
{
SkipIdenticalPresence = false // handles better on discord IPC loss, see updateStatus call in onReady.
};
client.OnReady += onReady;
// safety measure for now, until we performance test / improve backoff for failed connections.
client.OnConnectionFailed += (_, __) => client.Deinitialize();
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
(user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u =>
{
status.UnbindBindings();
status.BindTo(u.NewValue.Status);
activity.UnbindBindings();
activity.BindTo(u.NewValue.Activity);
}, true);
ruleset.BindValueChanged(_ => updateStatus());
status.BindValueChanged(_ => updateStatus());
activity.BindValueChanged(_ => updateStatus());
client.Initialize();
}
private void onReady(object _, ReadyMessage __)
{
Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug);
updateStatus();
}
private void updateStatus()
{
if (!client.IsInitialized)
return;
if (status.Value is UserStatusOffline)
{
client.ClearPresence();
return;
}
if (status.Value is UserStatusOnline && activity.Value != null)
{
presence.State = truncate(activity.Value.Status);
presence.Details = truncate(getDetails(activity.Value));
}
else
{
presence.State = "Idle";
presence.Details = string.Empty;
}
// update user information
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty);
// update ruleset
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom";
presence.Assets.SmallImageText = ruleset.Value.Name;
client.SetPresence(presence);
}
private static readonly int ellipsis_length = Encoding.UTF8.GetByteCount(new[] { '…' });
private string truncate(string str)
{
if (Encoding.UTF8.GetByteCount(str) <= 128)
return str;
ReadOnlyMemory<char> strMem = str.AsMemory();
do
{
strMem = strMem[..^1];
} while (Encoding.UTF8.GetByteCount(strMem.Span) + ellipsis_length > 128);
return string.Create(strMem.Length + 1, strMem, (span, mem) =>
{
mem.Span.CopyTo(span);
span[^1] = '…';
});
}
private string getDetails(UserActivity activity)
{
switch (activity)
{
case UserActivity.SoloGame solo:
return solo.Beatmap.ToString();
case UserActivity.Editing edit:
return edit.Beatmap.ToString();
}
return string.Empty;
}
protected override void Dispose(bool isDisposing)
{
client.Dispose();
base.Dispose(isDisposing);
}
}
}

View File

@ -60,6 +60,8 @@ namespace osu.Desktop
else
Add(new SimpleUpdateManager());
}
LoadComponentAsync(new DiscordRichPresence(), Add);
}
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)

View File

@ -22,8 +22,9 @@ namespace osu.Desktop
{
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
bool useSdl = args.Contains("--sdl");
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useSdl: useSdl))
{
host.ExceptionThrown += handleException;

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project">
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<OutputType>WinExe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>click the circles. to the beat.</Description>
@ -29,6 +29,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
<PackageReference Include="DiscordRichPresence" Version="1.0.121" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />

View File

@ -12,7 +12,7 @@
<description>click the circles. to the beat.</description>
<summary>click the circles.</summary>
<releaseNotes>testing</releaseNotes>
<copyright>Copyright (c) 2019 ppy Pty Ltd</copyright>
<copyright>Copyright (c) 2020 ppy Pty Ltd</copyright>
<language>en-AU</language>
</metadata>
<files>

View File

@ -0,0 +1,37 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.IO;
using BenchmarkDotNet.Attributes;
using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.Resources;
namespace osu.Game.Benchmarks
{
public class BenchmarkBeatmapParsing : BenchmarkTest
{
private readonly MemoryStream beatmapStream = new MemoryStream();
public override void SetUp()
{
using (var resources = new DllResourceStore(OsuResources.ResourceAssembly))
using (var archive = resources.GetStream("Beatmaps/241526 Soleily - Renatus.osz"))
using (var reader = new ZipArchiveReader(archive))
reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream);
}
[Benchmark]
public Beatmap BenchmarkBundledBeatmap()
{
beatmapStream.Seek(0, SeekOrigin.Begin);
var reader = new LineBufferedReader(beatmapStream); // no disposal
var decoder = Decoder.GetDecoder<Beatmap>(reader);
return decoder.Decode(reader);
}
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using NUnit.Framework;
namespace osu.Game.Benchmarks
{
[TestFixture]
[MemoryDiagnoser]
public abstract class BenchmarkTest
{
[GlobalSetup]
[OneTimeSetUp]
public virtual void SetUp()
{
}
[Test]
public void RunBenchmark() => BenchmarkRunner.Run(GetType());
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using BenchmarkDotNet.Running;
namespace osu.Game.Benchmarks
{
public static class Program
{
public static void Main(string[] args)
{
BenchmarkSwitcher
.FromAssembly(typeof(Program).Assembly)
.Run(args);
}
}
}

View File

@ -0,0 +1,8 @@
{
"profiles": {
"All Benchmarks": {
"commandName": "Project",
"commandLineArgs": "--filter *"
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup>
</Project>

View File

@ -5,7 +5,7 @@ using UIKit;
namespace osu.Game.Rulesets.Catch.Tests.iOS
{
public class Application
public static class Application
{
public static void Main(string[] args)
{

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Catch.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp3.0/osu.Game.Rulesets.Catch.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp3.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;

View File

@ -9,7 +9,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />

View File

@ -4,7 +4,7 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using System.Collections.Generic;
using System;
using System.Linq;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
@ -14,12 +14,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
{
public CatchBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
public CatchBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
: base(beatmap, ruleset)
{
}
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition);
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
{

View File

@ -16,15 +16,22 @@ using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Difficulty;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using System;
namespace osu.Game.Rulesets.Catch
{
public class CatchRuleset : Ruleset
public class CatchRuleset : Ruleset, ILegacyRuleset
{
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableCatchRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap, this);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
public const string SHORT_NAME = "fruits";
@ -51,7 +58,9 @@ namespace osu.Game.Rulesets.Catch
else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new CatchModSuddenDeath();
if (mods.HasFlag(LegacyMods.Autoplay))
if (mods.HasFlag(LegacyMods.Cinema))
yield return new CatchModCinema();
else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new CatchModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
@ -98,10 +107,16 @@ namespace osu.Game.Rulesets.Catch
new CatchModFlashlight(),
};
case ModType.Conversion:
return new Mod[]
{
new CatchModDifficultyAdjust(),
};
case ModType.Automation:
return new Mod[]
{
new MultiMod(new CatchModAutoplay(), new ModCinema()),
new MultiMod(new CatchModAutoplay(), new CatchModCinema()),
new CatchModRelax(),
};
@ -112,7 +127,7 @@ namespace osu.Game.Rulesets.Catch
};
default:
return new Mod[] { };
return Array.Empty<Mod>();
}
}
@ -120,19 +135,16 @@ namespace osu.Game.Rulesets.Catch
public override string ShortName => SHORT_NAME;
public override string PlayingVerb => "Catching fruit";
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetCatch };
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
public override int? LegacyID => 2;
public int LegacyID => 2;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
public CatchRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
}
}
}

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 0;
case HitResult.Perfect:
return 0.008;
return 0.01;
}
}

View File

@ -18,17 +18,5 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 30;
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
default:
return base.HealthIncreaseFor(result);
case HitResult.Perfect:
return 0.007;
}
}
}
}

View File

@ -23,18 +23,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
default:
return -0.02;
case HitResult.Perfect:
return 0.01;
}
}
/// <summary>
/// Whether fruit on the platter should explode or drop.
/// Note that this is only checked if the owning object is also <see cref="IHasComboInformation.LastInCombo" />

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 0;
case HitResult.Perfect:
return 0.004;
return 0.02;
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModCinema : ModCinema<CatchHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } },
Replay = new CatchAutoGenerator(beatmap).Generate(),
};
}
}

View File

@ -0,0 +1,49 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDifficultyAdjust : ModDifficultyAdjust
{
[SettingSource("Fruit Size", "Override a beatmap's set CS.")]
public BindableNumber<float> CircleSize { get; } = new BindableFloat
{
Precision = 0.1f,
MinValue = 1,
MaxValue = 10,
Default = 5,
Value = 5,
};
[SettingSource("Approach Rate", "Override a beatmap's set AR.")]
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
{
Precision = 0.1f,
MinValue = 1,
MaxValue = 10,
Default = 5,
Value = 5,
};
protected override void TransferSettings(BeatmapDifficulty difficulty)
{
base.TransferSettings(difficulty);
TransferSetting(CircleSize, difficulty.CircleSize);
TransferSetting(ApproachRate, difficulty.ApproachRate);
}
protected override void ApplySettings(BeatmapDifficulty difficulty)
{
base.ApplySettings(difficulty);
difficulty.CircleSize = CircleSize.Value;
difficulty.ApproachRate = ApproachRate.Value;
}
}
}

View File

@ -1,11 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModNightcore : ModNightcore
public class CatchModNightcore : ModNightcore<CatchHitObject>
{
public override double ScoreMultiplier => 1.06;
}

View File

@ -36,7 +36,10 @@ namespace osu.Game.Rulesets.Catch.Mods
//disable keyboard controls
public bool OnPressed(CatchAction action) => true;
public bool OnReleased(CatchAction action) => true;
public void OnReleased(CatchAction action)
{
}
protected override bool OnMouseMove(MouseMoveEvent e)
{

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using osuTK;
using osuTK.Graphics;

View File

@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Catch.Objects
if (value != null)
{
path.ControlPoints.AddRange(value.ControlPoints);
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
}
}

View File

@ -3,7 +3,7 @@
using System;
using System.Linq;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Catch.Beatmaps;

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Input.StateChanges;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Replays;
using osu.Game.Rulesets.Replays;

View File

@ -1,40 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Scoring
{
public class CatchScoreProcessor : ScoreProcessor
{
public CatchScoreProcessor(IBeatmap beatmap)
: base(beatmap)
{
}
private float hpDrainRate;
protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
}
protected override double HealthAdjustmentFactorFor(JudgementResult result)
{
switch (result.Type)
{
case HitResult.Miss:
return hpDrainRate;
default:
return 10.2 - hpDrainRate; // Award less HP as drain rate is increased
}
}
public override HitWindows CreateHitWindows() => new CatchHitWindows();
}
}

View File

@ -7,7 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
@ -103,7 +103,9 @@ namespace osu.Game.Rulesets.Catch.UI
MovableCatcher.X = state.CatcherX.Value;
}
public bool OnReleased(CatchAction action) => false;
public void OnReleased(CatchAction action)
{
}
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
@ -341,24 +343,22 @@ namespace osu.Game.Rulesets.Catch.UI
return false;
}
public bool OnReleased(CatchAction action)
public void OnReleased(CatchAction action)
{
switch (action)
{
case CatchAction.MoveLeft:
currentDirection++;
return true;
break;
case CatchAction.MoveRight:
currentDirection--;
return true;
break;
case CatchAction.Dash:
Dashing = false;
return true;
break;
}
return false;
}
/// <summary>

View File

@ -10,10 +10,8 @@ using osu.Game.Replays;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
@ -32,8 +30,6 @@ namespace osu.Game.Rulesets.Catch.UI
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
}
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(Beatmap);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation);

View File

@ -5,7 +5,7 @@ using UIKit;
namespace osu.Game.Rulesets.Mania.Tests.iOS
{
public class Application
public static class Application
{
public static void Main(string[] args)
{

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Mania.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp3.0/osu.Game.Rulesets.Mania.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp3.1/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;

View File

@ -0,0 +1,46 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
public abstract class SkinnableTestScene : OsuGridTestScene
{
private Skin defaultSkin;
protected SkinnableTestScene()
: base(1, 2)
{
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, SkinManager skinManager)
{
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
}
public void SetContents(Func<Drawable> creationFunction)
{
Cell(0).Child = createProvider(null, creationFunction);
Cell(1).Child = createProvider(defaultSkin, creationFunction);
}
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)
{
var mainProvider = new SkinProvidingContainer(skin);
return mainProvider
.WithChild(new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider))
{
Child = creationFunction()
});
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneDrawableJudgement : SkinnableTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableJudgement),
typeof(DrawableManiaJudgement)
};
public TestSceneDrawableJudgement()
{
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
{
AddStep("Show " + result.GetDescription(), () => SetContents(() =>
new DrawableManiaJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}));
}
}
}
}

View File

@ -0,0 +1,314 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneHoldNoteInput : RateAdjustedBeatmapTestScene
{
private const double time_before_head = 250;
private const double time_head = 1500;
private const double time_during_hold_1 = 2500;
private const double time_tail = 4000;
private const double time_after_tail = 5250;
private List<JudgementResult> judgementResults;
private bool allJudgedFired;
/// <summary>
/// -----[ ]-----
/// o o
/// </summary>
[Test]
public void TestNoInput()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Miss);
assertNoteJudgement(HitResult.Perfect);
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressTooEarlyAndReleaseAfterTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
new ManiaReplayFrame(time_after_tail, ManiaAction.Key1),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressTooEarlyAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
new ManiaReplayFrame(time_tail),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressTooEarlyThenPressAtStartAndReleaseAfterTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
new ManiaReplayFrame(time_before_head + 10),
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressTooEarlyThenPressAtStartAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
new ManiaReplayFrame(time_before_head + 10),
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Perfect);
}
/// <summary>
/// -----[ ]-----
/// xo o
/// </summary>
[Test]
public void TestPressAtStartAndBreak()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_head + 10),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressAtStartThenBreakThenRepressAndReleaseAfterTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_head + 10),
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// xo x o o
/// </summary>
[Test]
public void TestPressAtStartThenBreakThenRepressAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_head + 10),
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
new ManiaReplayFrame(time_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Meh);
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressDuringNoteAndReleaseAfterTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// x o o
/// </summary>
[Test]
public void TestPressDuringNoteAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
new ManiaReplayFrame(time_tail),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Meh);
}
/// <summary>
/// -----[ ]-----
/// xo o
/// </summary>
[Test]
public void TestPressAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_tail, ManiaAction.Key1),
new ManiaReplayFrame(time_tail + 10),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Meh);
}
private void assertHeadJudgement(HitResult result)
=> AddAssert($"head judged as {result}", () => judgementResults[0].Type == result);
private void assertTailJudgement(HitResult result)
=> AddAssert($"tail judged as {result}", () => judgementResults[^2].Type == result);
private void assertNoteJudgement(HitResult result)
=> AddAssert($"hold note judged as {result}", () => judgementResults[^1].Type == result);
private void assertTickJudgement(HitResult result)
=> AddAssert($"tick judged as {result}", () => judgementResults[6].Type == result); // arbitrary tick
private ScoreAccessibleReplayPlayer currentPlayer;
private void performTest(List<ReplayFrame> frames)
{
AddStep("load player", () =>
{
Beatmap.Value = CreateWorkingBeatmap(new Beatmap<ManiaHitObject>
{
HitObjects =
{
new HoldNote
{
StartTime = time_head,
Duration = time_tail - time_head,
Column = 0,
}
},
BeatmapInfo =
{
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 4 },
Ruleset = new ManiaRuleset().RulesetInfo
},
});
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
p.OnLoadComplete += _ =>
{
p.ScoreProcessor.NewJudgement += result =>
{
if (currentPlayer == p) judgementResults.Add(result);
};
p.ScoreProcessor.AllJudged += () =>
{
if (currentPlayer == p) allJudgedFired = true;
};
};
LoadScreen(currentPlayer = p);
allJudgedFired = false;
judgementResults = new List<JudgementResult>();
});
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for all judged", () => allJudgedFired);
}
private class ScoreAccessibleReplayPlayer : ReplayPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
protected override bool PauseOnFocusLost => false;
public ScoreAccessibleReplayPlayer(Score score)
: base(score, false, false)
{
}
}
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestScenePlayer : PlayerTestScene
{
public TestScenePlayer()
: base(new ManiaRuleset())
{
}
}
}

View File

@ -9,7 +9,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />

View File

@ -5,7 +5,7 @@ using osu.Game.Rulesets.Mania.Objects;
using System;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary>
private const int max_notes_for_density = 7;
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
public int TargetColumns;
public bool Dual;
public readonly bool IsForCurrentRuleset;
@ -37,10 +35,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private ManiaBeatmap beatmap;
public ManiaBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
public ManiaBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
: base(beatmap, ruleset)
{
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(new ManiaRuleset().RulesetInfo);
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
@ -69,6 +67,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
}
}
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition || h is ManiaHitObject);
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
{
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
prevNoteTimes.RemoveAt(0);
prevNoteTimes.Add(newNoteTime);
density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count;
density = (prevNoteTimes[^1] - prevNoteTimes[0]) / prevNoteTimes.Count;
}
private double lastTime;

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils;

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
// Todo: This shouldn't exist, mania should not reference the drawable hitobject directly.
if (DrawableObject.IsLoaded)
{
DrawableNote note = position == HoldNotePosition.Start ? DrawableObject.Head : DrawableObject.Tail;
DrawableNote note = position == HoldNotePosition.Start ? (DrawableNote)DrawableObject.Head : DrawableObject.Tail;
Anchor = note.Anchor;
Origin = note.Origin;

View File

@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return true;
}
protected override bool OnMouseUp(MouseUpEvent e)
protected override void OnMouseUp(MouseUpEvent e)
{
EndPlacement();
return base.OnMouseUp(e);
base.OnMouseUp(e);
}
public override void UpdatePosition(Vector2 screenSpacePosition)

View File

@ -13,7 +13,7 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{
public class ManiaSelectionBlueprint : SelectionBlueprint
public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
{
public Vector2 ScreenSpaceDragPosition { get; private set; }
public Vector2 DragPosition { get; private set; }
@ -55,14 +55,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return base.OnMouseDown(e);
}
protected override bool OnDrag(DragEvent e)
protected override void OnDrag(DragEvent e)
{
var result = base.OnDrag(e);
base.OnDrag(e);
ScreenSpaceDragPosition = e.ScreenSpaceMousePosition;
DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition);
return result;
}
public override void Show()

View File

@ -0,0 +1,34 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaBlueprintContainer : ComposeBlueprintContainer
{
public ManiaBlueprintContainer(IEnumerable<DrawableHitObject> drawableHitObjects)
: base(drawableHitObjects)
{
}
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote:
return new HoldNoteSelectionBlueprint(holdNote);
}
return base.CreateBlueprintFor(hitObject);
}
}
}

View File

@ -5,11 +5,8 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
@ -52,26 +49,12 @@ namespace osu.Game.Rulesets.Mania.Edit
return drawableRuleset;
}
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects);
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
{
new NoteCompositionTool(),
new HoldNoteCompositionTool()
};
public override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote:
return new HoldNoteSelectionBlueprint(holdNote);
}
return base.CreateBlueprintFor(hitObject);
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Timing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
@ -70,10 +71,12 @@ namespace osu.Game.Rulesets.Mania.Edit
// When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen.
// This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height.
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
delta -= moveEvent.Blueprint.DrawableObject.Parent.DrawHeight;
delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong
foreach (var b in SelectedBlueprints)
foreach (var selectionBlueprint in SelectedBlueprints)
{
var b = (OverlaySelectionBlueprint)selectionBlueprint;
var hitObject = b.DrawableObject;
var objectParent = (HitObjectContainer)hitObject.Parent;

View File

@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Edit.Masks
{
public abstract class ManiaSelectionBlueprint : SelectionBlueprint
public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
{
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject)

View File

@ -15,11 +15,11 @@ namespace osu.Game.Rulesets.Mania.Judgements
{
switch (result)
{
case HitResult.Miss:
default:
return 0;
default:
return 0.040;
case HitResult.Perfect:
return 0.01;
}
}
}

View File

@ -29,32 +29,5 @@ namespace osu.Game.Rulesets.Mania.Judgements
return 300;
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
case HitResult.Miss:
return -0.125;
case HitResult.Meh:
return 0.005;
case HitResult.Ok:
return 0.010;
case HitResult.Good:
return 0.035;
case HitResult.Great:
return 0.055;
case HitResult.Perfect:
return 0.065;
default:
return 0;
}
}
}
}

View File

@ -25,20 +25,30 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Skinning;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mania
{
public class ManiaRuleset : Ruleset
public class ManiaRuleset : Ruleset, ILegacyRuleset
{
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableManiaRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
public const string SHORT_NAME = "mania";
public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
@ -51,7 +61,9 @@ namespace osu.Game.Rulesets.Mania
else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new ManiaModSuddenDeath();
if (mods.HasFlag(LegacyMods.Autoplay))
if (mods.HasFlag(LegacyMods.Cinema))
yield return new ManiaModCinema();
else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new ManiaModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
@ -143,12 +155,13 @@ namespace osu.Game.Rulesets.Mania
new ManiaModRandom(),
new ManiaModDualStages(),
new ManiaModMirror(),
new ManiaModDifficultyAdjust(),
};
case ModType.Automation:
return new Mod[]
{
new MultiMod(new ManiaModAutoplay(), new ModCinema()),
new MultiMod(new ManiaModAutoplay(), new ManiaModCinema()),
};
case ModType.Fun:
@ -158,7 +171,7 @@ namespace osu.Game.Rulesets.Mania
};
default:
return new Mod[] { };
return Array.Empty<Mod>();
}
}
@ -166,11 +179,13 @@ namespace osu.Game.Rulesets.Mania
public override string ShortName => SHORT_NAME;
public override string PlayingVerb => "Smashing keys";
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania };
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap);
public override int? LegacyID => 3;
public int LegacyID => 3;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
@ -178,11 +193,6 @@ namespace osu.Game.Rulesets.Mania
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
public ManiaRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
}
public override IEnumerable<int> AvailableVariants
{
get
@ -268,7 +278,7 @@ namespace osu.Game.Rulesets.Mania
return stage1Bindings.Concat(stage2Bindings);
}
return new KeyBinding[0];
return Array.Empty<KeyBinding>();
}
public override string GetVariantName(int variant)

View File

@ -0,0 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModCinema : ModCinema<ManiaHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } },
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
};
}
}

View File

@ -3,12 +3,9 @@
using osu.Game.Rulesets.Mods;
namespace osu.Game.Online.API.Requests.Responses
namespace osu.Game.Rulesets.Mania.Mods
{
public class APIMod : IMod
public class ManiaModDifficultyAdjust : ModDifficultyAdjust
{
public string Acronym { get; set; }
public bool Equals(IMod other) => Acronym == other?.Acronym;
}
}

View File

@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "Fade In";
public override string Acronym => "FI";
public override IconUsage Icon => OsuIcon.ModHidden;
public override IconUsage? Icon => OsuIcon.ModHidden;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1;

View File

@ -1,11 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModNightcore : ModNightcore
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
{
public override double ScoreMultiplier => 1;
}

View File

@ -4,7 +4,7 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Random";
public override string Acronym => "RD";
public override ModType Type => ModType.Conversion;
public override IconUsage Icon => OsuIcon.Dice;
public override IconUsage? Icon => OsuIcon.Dice;
public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1;

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
@ -21,11 +20,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public override bool DisplayResult => false;
public DrawableNote Head => headContainer.Child;
public DrawableNote Tail => tailContainer.Child;
public DrawableHoldNoteHead Head => headContainer.Child;
public DrawableHoldNoteTail Tail => tailContainer.Child;
private readonly Container<DrawableHeadNote> headContainer;
private readonly Container<DrawableTailNote> tailContainer;
private readonly Container<DrawableHoldNoteHead> headContainer;
private readonly Container<DrawableHoldNoteTail> tailContainer;
private readonly Container<DrawableHoldNoteTick> tickContainer;
private readonly BodyPiece bodyPiece;
@ -33,12 +32,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// <summary>
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
/// </summary>
private double? holdStartTime;
public double? HoldStartTime { get; private set; }
/// <summary>
/// Whether the hold note has been released too early and shouldn't give full score for the release.
/// </summary>
private bool hasBroken;
public bool HasBroken { get; private set; }
public DrawableHoldNote(HoldNote hitObject)
: base(hitObject)
@ -49,8 +48,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
bodyPiece = new BodyPiece { RelativeSizeAxes = Axes.X },
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
headContainer = new Container<DrawableHeadNote> { RelativeSizeAxes = Axes.Both },
tailContainer = new Container<DrawableTailNote> { RelativeSizeAxes = Axes.Both },
headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both },
tailContainer = new Container<DrawableHoldNoteTail> { RelativeSizeAxes = Axes.Both },
});
AccentColour.BindValueChanged(colour =>
@ -65,11 +64,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
switch (hitObject)
{
case DrawableHeadNote head:
case DrawableHoldNoteHead head:
headContainer.Child = head;
break;
case DrawableTailNote tail:
case DrawableHoldNoteTail tail:
tailContainer.Child = tail;
break;
@ -92,7 +91,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
switch (hitObject)
{
case TailNote _:
return new DrawableTailNote(this)
return new DrawableHoldNoteTail(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@ -100,7 +99,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
};
case Note _:
return new DrawableHeadNote(this)
return new DrawableHoldNoteHead(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@ -110,7 +109,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
case HoldNoteTick tick:
return new DrawableHoldNoteTick(tick)
{
HoldStartTime = () => holdStartTime,
HoldStartTime = () => HoldStartTime,
AccentColour = { BindTarget = AccentColour }
};
}
@ -125,12 +124,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (Tail.AllJudged)
ApplyResult(r => r.Type = HitResult.Perfect);
}
protected override void Update()
{
base.Update();
@ -146,146 +139,62 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.UpdateStateTransforms(state);
}
protected void BeginHold()
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
holdStartTime = Time.Current;
bodyPiece.Hitting = true;
}
if (Tail.AllJudged)
ApplyResult(r => r.Type = HitResult.Perfect);
protected void EndHold()
{
holdStartTime = null;
bodyPiece.Hitting = false;
if (Tail.Result.Type == HitResult.Miss)
HasBroken = true;
}
public bool OnPressed(ManiaAction action)
{
// Make sure the action happened within the body of the hold note
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
if (AllJudged)
return false;
if (action != Action.Value)
return false;
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
// and within the limited range of the above if-statement. This state will be managed by the head note if the
// user has pressed during the hit windows of the head note.
BeginHold();
beginHoldAt(Time.Current - Head.HitObject.StartTime);
Head.UpdateResult();
return true;
}
public bool OnReleased(ManiaAction action)
private void beginHoldAt(double timeOffset)
{
// Make sure that the user started holding the key during the hold note
if (!holdStartTime.HasValue)
return false;
if (timeOffset < -Head.HitObject.HitWindows.WindowFor(HitResult.Miss))
return;
HoldStartTime = Time.Current;
bodyPiece.Hitting = true;
}
public void OnReleased(ManiaAction action)
{
if (AllJudged)
return;
if (action != Action.Value)
return false;
return;
EndHold();
// Make sure a hold was started
if (HoldStartTime == null)
return;
Tail.UpdateResult();
endHold();
// If the key has been released too early, the user should not receive full score for the release
if (!Tail.IsHit)
hasBroken = true;
return true;
HasBroken = true;
}
/// <summary>
/// The head note of a hold.
/// </summary>
private class DrawableHeadNote : DrawableNote
private void endHold()
{
private readonly DrawableHoldNote holdNote;
public DrawableHeadNote(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Head)
{
this.holdNote = holdNote;
}
public override bool OnPressed(ManiaAction action)
{
if (!base.OnPressed(action))
return false;
// If the key has been released too early, the user should not receive full score for the release
if (Result.Type == HitResult.Miss)
holdNote.hasBroken = true;
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
// The body doesn't handle these early early hits, so we have to explicitly set the holding state here
holdNote.BeginHold();
return true;
}
}
/// <summary>
/// The tail note of a hold.
/// </summary>
private class DrawableTailNote : DrawableNote
{
/// <summary>
/// Lenience of release hit windows. This is to make cases where the hold note release
/// is timed alongside presses of other hit objects less awkward.
/// Todo: This shouldn't exist for non-LegacyBeatmapDecoder beatmaps
/// </summary>
private const double release_window_lenience = 1.5;
private readonly DrawableHoldNote holdNote;
public DrawableTailNote(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Tail)
{
this.holdNote = holdNote;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
Debug.Assert(HitObject.HitWindows != null);
// Factor in the release lenience
timeOffset /= release_window_lenience;
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
return;
ApplyResult(r =>
{
if (holdNote.hasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
result = HitResult.Good;
r.Type = result;
});
}
public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
public override bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdNote.holdStartTime.HasValue)
return false;
if (action != Action.Value)
return false;
UpdateResult(true);
// Handled by the hold note, which will set holding = false
return false;
}
HoldStartTime = null;
bodyPiece.Hitting = false;
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// The head of a <see cref="DrawableHoldNote"/>.
/// </summary>
public class DrawableHoldNoteHead : DrawableNote
{
public DrawableHoldNoteHead(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Head)
{
}
public void UpdateResult() => base.UpdateResult(true);
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override void OnReleased(ManiaAction action)
{
}
}
}

View File

@ -0,0 +1,66 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// The tail of a <see cref="DrawableHoldNote"/>.
/// </summary>
public class DrawableHoldNoteTail : DrawableNote
{
/// <summary>
/// Lenience of release hit windows. This is to make cases where the hold note release
/// is timed alongside presses of other hit objects less awkward.
/// Todo: This shouldn't exist for non-LegacyBeatmapDecoder beatmaps
/// </summary>
private const double release_window_lenience = 1.5;
private readonly DrawableHoldNote holdNote;
public DrawableHoldNoteTail(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Tail)
{
this.holdNote = holdNote;
}
public void UpdateResult() => base.UpdateResult(true);
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
Debug.Assert(HitObject.HitWindows != null);
// Factor in the release lenience
timeOffset /= release_window_lenience;
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
return;
ApplyResult(r =>
{
// If the head wasn't hit or the hold note was broken, cap the max score to Meh.
if (result > HitResult.Meh && (!holdNote.Head.IsHit || holdNote.HasBroken))
result = HitResult.Meh;
r.Type = result;
});
}
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override void OnReleased(ManiaAction action)
{
}
}
}

View File

@ -77,6 +77,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return UpdateResult(true);
}
public virtual bool OnReleased(ManiaAction action) => false;
public virtual void OnReleased(ManiaAction action)
{
}
}
}

View File

@ -1,68 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
public class GlowPiece : CompositeDrawable, IHasAccentColour
{
private const float glow_alpha = 0.7f;
private const float glow_radius = 5;
public GlowPiece()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateGlow();
}
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
updateGlow();
}
}
private void updateGlow()
{
if (!IsLoaded)
return;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Opacity(glow_alpha),
Radius = glow_radius,
Hollow = true
};
}
}
}

View File

@ -1,85 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
public class LaneGlowPiece : CompositeDrawable, IHasAccentColour
{
private const float total_height = 100;
private const float glow_height = 50;
private const float glow_alpha = 0.4f;
private const float edge_alpha = 0.3f;
public LaneGlowPiece()
{
BypassAutoSizeAxes = Axes.Both;
RelativeSizeAxes = Axes.X;
Height = total_height;
InternalChildren = new[]
{
new Container
{
Name = "Left edge",
RelativeSizeAxes = Axes.Y,
Width = 1,
Children = createGradient(edge_alpha)
},
new Container
{
Name = "Right edge",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = 1,
Children = createGradient(edge_alpha)
},
new Container
{
Name = "Glow",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = glow_height,
Children = createGradient(glow_alpha)
}
};
}
private Drawable[] createGradient(float alpha) => new Drawable[]
{
new Box
{
Name = "Top",
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingParameters.Additive,
Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha))
},
new Box
{
Name = "Bottom",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingParameters.Additive,
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent)
}
};
public Color4 AccentColour
{
get => Colour;
set => Colour = value;
}
}
}

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Replays
public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
// We don't need to fully convert, just create the converter
var converter = new ManiaBeatmapConverter(beatmap);
var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
// NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
// elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.

View File

@ -1,87 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Scoring
{
internal class ManiaScoreProcessor : ScoreProcessor
{
/// <summary>
/// The hit HP multiplier at OD = 0.
/// </summary>
private const double hp_multiplier_min = 0.75;
/// <summary>
/// The hit HP multiplier at OD = 0.
/// </summary>
private const double hp_multiplier_mid = 0.85;
/// <summary>
/// The hit HP multiplier at OD = 0.
/// </summary>
private const double hp_multiplier_max = 1;
/// <summary>
/// The MISS HP multiplier at OD = 0.
/// </summary>
private const double hp_multiplier_miss_min = 0.5;
/// <summary>
/// The MISS HP multiplier at OD = 5.
/// </summary>
private const double hp_multiplier_miss_mid = 0.75;
/// <summary>
/// The MISS HP multiplier at OD = 10.
/// </summary>
private const double hp_multiplier_miss_max = 1;
/// <summary>
/// The MISS HP multiplier. This is multiplied to the miss hp increase.
/// </summary>
private double hpMissMultiplier = 1;
/// <summary>
/// The HIT HP multiplier. This is multiplied to hit hp increases.
/// </summary>
private double hpMultiplier = 1;
public ManiaScoreProcessor(IBeatmap beatmap)
: base(beatmap)
{
}
protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
}
protected override void SimulateAutoplay(IBeatmap beatmap)
{
while (true)
{
base.SimulateAutoplay(beatmap);
if (!HasFailed)
break;
hpMultiplier *= 1.01;
hpMissMultiplier *= 0.98;
Reset(false);
}
}
protected override double HealthAdjustmentFactorFor(JudgementResult result)
=> result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
public override HitWindows CreateHitWindows() => new ManiaHitWindows();
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Audio;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning
{
public class ManiaLegacySkinTransformer : ISkin
{
private readonly ISkin source;
public ManiaLegacySkinTransformer(ISkin source)
{
this.source = source;
}
public Drawable GetDrawableComponent(ISkinComponent component)
{
switch (component)
{
case GameplaySkinComponent<HitResult> resultComponent:
return getResult(resultComponent);
}
return null;
}
private Drawable getResult(GameplaySkinComponent<HitResult> resultComponent)
{
switch (resultComponent.Component)
{
case HitResult.Miss:
return this.GetAnimation("mania-hit0", true, true);
case HitResult.Meh:
return this.GetAnimation("mania-hit50", true, true);
case HitResult.Ok:
return this.GetAnimation("mania-hit100", true, true);
case HitResult.Good:
return this.GetAnimation("mania-hit200", true, true);
case HitResult.Great:
return this.GetAnimation("mania-hit300", true, true);
case HitResult.Perfect:
return this.GetAnimation("mania-hit300g", true, true);
}
return null;
}
public Texture GetTexture(string componentName) => source.GetTexture(componentName);
public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample);
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) =>
source.GetConfig<TLookup, TValue>(lookup);
}
}

View File

@ -191,7 +191,9 @@ namespace osu.Game.Rulesets.Mania.UI
return true;
}
public bool OnReleased(ManiaAction action) => false;
public void OnReleased(ManiaAction action)
{
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border

View File

@ -98,11 +98,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
return false;
}
public bool OnReleased(ManiaAction action)
public void OnReleased(ManiaAction action)
{
if (action == this.action.Value)
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
return false;
}
}
}

View File

@ -18,30 +18,17 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
{
private const float hit_target_bar_height = 2;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Container hitTargetLine;
private readonly Drawable hitTargetBar;
private readonly Drawable hitTarget;
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
{
InternalChildren = new[]
{
hitTargetBar = new Box
hitTarget = new DefaultHitTarget
{
RelativeSizeAxes = Axes.X,
Height = NotePiece.NOTE_HEIGHT,
Alpha = 0.6f,
Colour = Color4.Black
},
hitTargetLine = new Container
{
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
hitObjectContainer
};
@ -55,17 +42,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
hitTarget.Anchor = hitTarget.Origin = anchor;
}, true);
}
protected override void LoadComplete()
{
base.LoadComplete();
updateColours();
}
private Color4 accentColour;
public Color4 AccentColour
@ -78,21 +58,88 @@ namespace osu.Game.Rulesets.Mania.UI.Components
accentColour = value;
updateColours();
if (hitTarget is IHasAccentColour colouredHitTarget)
colouredHitTarget.AccentColour = accentColour;
}
}
private void updateColours()
private class DefaultHitTarget : CompositeDrawable, IHasAccentColour
{
if (!IsLoaded)
return;
private const float hit_target_bar_height = 2;
hitTargetLine.EdgeEffect = new EdgeEffectParameters
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Container hitTargetLine;
private readonly Drawable hitTargetBar;
public DefaultHitTarget()
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
InternalChildren = new[]
{
hitTargetBar = new Box
{
RelativeSizeAxes = Axes.X,
Height = NotePiece.NOTE_HEIGHT,
Alpha = 0.6f,
Colour = Color4.Black
},
hitTargetLine = new Container
{
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
};
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(dir =>
{
Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
}, true);
}
protected override void LoadComplete()
{
base.LoadComplete();
updateColours();
}
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
updateColours();
}
}
private void updateColours()
{
if (!IsLoaded)
return;
hitTargetLine.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
}
}
}
}

View File

@ -115,11 +115,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
return false;
}
public bool OnReleased(ManiaAction action)
public void OnReleased(ManiaAction action)
{
if (action == this.action.Value)
keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
return false;
}
}
}

View File

@ -14,11 +14,9 @@ using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
@ -67,8 +65,6 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(Beatmap);
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);

View File

@ -5,7 +5,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osuTK;
using osuTK.Graphics;

View File

@ -5,7 +5,7 @@ using UIKit;
namespace osu.Game.Rulesets.Osu.Tests.iOS
{
public class Application
public static class Application
{
public static void Main(string[] args)
{

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Osu.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp3.0/osu.Game.Rulesets.Osu.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp3.1/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Some files were not shown because too many files have changed in this diff Show More