mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:33:30 +08:00
Merge branch 'master' into add-pp-playtime
This commit is contained in:
commit
295d1d15c8
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project">
|
<configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Debug/net6.0/osu.Game.Benchmarks.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Debug/net8.0/osu.Game.Benchmarks.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="CatchRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="CatchRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net6.0/osu.Game.Rulesets.Catch.Tests.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net8.0/osu.Game.Rulesets.Catch.Tests.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<browser url="http://localhost:5000" />
|
<browser url="http://localhost:5000" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="ManiaRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="ManiaRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net6.0/osu.Game.Rulesets.Mania.Tests.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net8.0/osu.Game.Rulesets.Mania.Tests.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<browser url="http://localhost:5000" />
|
<browser url="http://localhost:5000" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="OsuRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="OsuRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net6.0/osu.Game.Rulesets.Osu.Tests.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net8.0/osu.Game.Rulesets.Osu.Tests.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<browser url="http://localhost:5000" />
|
<browser url="http://localhost:5000" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="TaikoRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="TaikoRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net6.0/osu.Game.Rulesets.Taiko.Tests.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net8.0/osu.Game.Rulesets.Taiko.Tests.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<browser url="http://localhost:5000" />
|
<browser url="http://localhost:5000" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Tournament" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="Tournament" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net6.0/osu!.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net8.0/osu!.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="--tournament" />
|
<option name="PROGRAM_PARAMETERS" value="--tournament" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Tournament (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="Tournament (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/net6.0/osu.Game.Tournament.Tests.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/net8.0/osu.Game.Tournament.Tests.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<browser url="http://localhost:5000" />
|
<browser url="http://localhost:5000" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net6.0/osu!.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net8.0/osu!.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="osu! (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="osu! (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net6.0/osu.Game.Tests.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net8.0/osu.Game.Tests.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="osu! (Second Client)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="osu! (Second Client)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net6.0/osu!.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net8.0/osu!.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="--debug-client-id=1" />
|
<option name="PROGRAM_PARAMETERS" value="--debug-client-id=1" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net6.0" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net8.0" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
<option name="PROJECT_TFM" value="net6.0" />
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
|
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Desktop/bin/Debug/net6.0/osu!.dll"
|
"${workspaceRoot}/osu.Desktop/bin/Debug/net8.0/osu!.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build osu! (Debug)",
|
"preLaunchTask": "Build osu! (Debug)",
|
||||||
@ -19,7 +19,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Desktop/bin/Release/net6.0/osu!.dll"
|
"${workspaceRoot}/osu.Desktop/bin/Release/net8.0/osu!.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build osu! (Release)",
|
"preLaunchTask": "Build osu! (Release)",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Game.Tests/bin/Debug/net6.0/osu.Game.Tests.dll"
|
"${workspaceRoot}/osu.Game.Tests/bin/Debug/net8.0/osu.Game.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build tests (Debug)",
|
"preLaunchTask": "Build tests (Debug)",
|
||||||
@ -43,7 +43,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Game.Tests/bin/Release/net6.0/osu.Game.Tests.dll"
|
"${workspaceRoot}/osu.Game.Tests/bin/Release/net8.0/osu.Game.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build tests (Release)",
|
"preLaunchTask": "Build tests (Release)",
|
||||||
@ -55,7 +55,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Desktop/bin/Debug/net6.0/osu!.dll",
|
"${workspaceRoot}/osu.Desktop/bin/Debug/net8.0/osu!.dll",
|
||||||
"--tournament"
|
"--tournament"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Desktop/bin/Release/net6.0/osu!.dll",
|
"${workspaceRoot}/osu.Desktop/bin/Release/net8.0/osu!.dll",
|
||||||
"--tournament"
|
"--tournament"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
@ -81,7 +81,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/net6.0/osu.Game.Tournament.Tests.dll",
|
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/net8.0/osu.Game.Tournament.Tests.dll",
|
||||||
"--tournament"
|
"--tournament"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
@ -94,7 +94,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/net6.0/osu.Game.Tournament.Tests.dll",
|
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/net8.0/osu.Game.Tournament.Tests.dll",
|
||||||
"--tournament"
|
"--tournament"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
@ -105,7 +105,7 @@
|
|||||||
"name": "Benchmark",
|
"name": "Benchmark",
|
||||||
"type": "coreclr",
|
"type": "coreclr",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/net6.0/osu.Game.Benchmarks.dll",
|
"program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/net8.0/osu.Game.Benchmarks.dll",
|
||||||
"args": [
|
"args": [
|
||||||
"--filter",
|
"--filter",
|
||||||
"*"
|
"*"
|
||||||
|
@ -35,7 +35,7 @@ If you are just looking to give the game a whirl, you can grab the latest releas
|
|||||||
|
|
||||||
You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download).
|
You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download).
|
||||||
|
|
||||||
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
If your platform is unsupported or not listed above, there is still a chance you can run the release or manually build it by following the instructions below.
|
||||||
|
|
||||||
**For iOS/iPadOS users**: The iOS testflight link fills up very fast (Apple has a hard limit of 10,000 users). We reset it occasionally. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements. Our goal is to get the game on mobile app stores in early 2024.
|
**For iOS/iPadOS users**: The iOS testflight link fills up very fast (Apple has a hard limit of 10,000 users). We reset it occasionally. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements. Our goal is to get the game on mobile app stores in early 2024.
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ You can see some examples of custom rulesets by visiting the [custom ruleset dir
|
|||||||
|
|
||||||
Please make sure you have the following prerequisites:
|
Please make sure you have the following prerequisites:
|
||||||
|
|
||||||
- A desktop platform with the [.NET 6.0 SDK](https://dotnet.microsoft.com/download) installed.
|
- A desktop platform with the [.NET 8.0 SDK](https://dotnet.microsoft.com/download) installed.
|
||||||
|
|
||||||
When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/), or [Visual Studio Code](https://code.visualstudio.com/) with the [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) and [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) plugin installed.
|
When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/), or [Visual Studio Code](https://code.visualstudio.com/) with the [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) and [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) plugin installed.
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Rulesets.EmptyFreeformRuleset.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Rulesets.EmptyFreeformRuleset.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Rulesets.EmptyFreeformRuleset.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Rulesets.EmptyFreeformRuleset.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Rulesets.Pippidon.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Rulesets.Pippidon.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Rulesets.Pippidon.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Rulesets.Pippidon.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Rulesets.EmptyScrolling.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Rulesets.EmptyScrolling.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Rulesets.EmptyScrolling.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Rulesets.EmptyScrolling.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Rulesets.Pippidon.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Rulesets.Pippidon.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Rulesets.Pippidon.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Rulesets.Pippidon.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.205.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.221.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
@ -47,8 +47,8 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
var windowsVersion = Environment.OSVersion.Version;
|
var windowsVersion = Environment.OSVersion.Version;
|
||||||
|
|
||||||
// While .NET 6 still supports Windows 7 and above, we are limited by realm currently, as they choose to only support 8.1 and higher.
|
// While .NET 8 only supports Windows 10 and above, running on Windows 7/8.1 may still work. We are limited by realm currently, as they choose to only support 8.1 and higher.
|
||||||
// See https://www.mongodb.com/docs/realm/sdk/dotnet/#supported-platforms
|
// See https://www.mongodb.com/docs/realm/sdk/dotnet/compatibility/
|
||||||
if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2))
|
if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2))
|
||||||
{
|
{
|
||||||
// If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider
|
// If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 75 KiB |
48
osu.Game.Benchmarks/BenchmarkStringComparison.cs
Normal file
48
osu.Game.Benchmarks/BenchmarkStringComparison.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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 BenchmarkDotNet.Attributes;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Benchmarks
|
||||||
|
{
|
||||||
|
public class BenchmarkStringComparison
|
||||||
|
{
|
||||||
|
private string[] strings = null!;
|
||||||
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public void GlobalSetUp()
|
||||||
|
{
|
||||||
|
strings = new string[10000];
|
||||||
|
|
||||||
|
for (int i = 0; i < strings.Length; ++i)
|
||||||
|
strings[i] = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
for (int i = 0; i < strings.Length; ++i)
|
||||||
|
{
|
||||||
|
if (i % 2 == 0)
|
||||||
|
strings[i] = strings[i].ToUpperInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void OrdinalIgnoreCase() => compare(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void OrdinalSortByCase() => compare(OrdinalSortByCaseStringComparer.DEFAULT);
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void InvariantCulture() => compare(StringComparer.InvariantCulture);
|
||||||
|
|
||||||
|
private void compare(IComparer<string> comparer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < strings.Length; ++i)
|
||||||
|
{
|
||||||
|
for (int j = i + 1; j < strings.Length; ++j)
|
||||||
|
_ = comparer.Compare(strings[i], strings[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Rulesets.Catch.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Rulesets.Catch.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Rulesets.Catch.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Rulesets.Catch.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -1,111 +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.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Catch.Judgements;
|
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Catch.Skinning;
|
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
|
||||||
using osu.Game.Skinning;
|
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using Direction = osu.Game.Rulesets.Catch.UI.Direction;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
|
||||||
{
|
|
||||||
public partial class TestSceneCatchSkinConfiguration : OsuTestScene
|
|
||||||
{
|
|
||||||
private Catcher catcher;
|
|
||||||
|
|
||||||
private readonly Container container;
|
|
||||||
|
|
||||||
public TestSceneCatchSkinConfiguration()
|
|
||||||
{
|
|
||||||
Add(container = new Container { RelativeSizeAxes = Axes.Both });
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(false)]
|
|
||||||
[TestCase(true)]
|
|
||||||
public void TestCatcherPlateFlipping(bool flip)
|
|
||||||
{
|
|
||||||
AddStep("setup catcher", () =>
|
|
||||||
{
|
|
||||||
var skin = new TestSkin { FlipCatcherPlate = flip };
|
|
||||||
container.Child = new SkinProvidingContainer(skin)
|
|
||||||
{
|
|
||||||
Child = catcher = new Catcher(new DroppedObjectContainer())
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
Fruit fruit = new Fruit();
|
|
||||||
|
|
||||||
AddStep("catch fruit", () => catchFruit(fruit, 20));
|
|
||||||
|
|
||||||
float position = 0;
|
|
||||||
|
|
||||||
AddStep("record fruit position", () => position = getCaughtObjectPosition(fruit));
|
|
||||||
|
|
||||||
AddStep("face left", () => catcher.VisualDirection = Direction.Left);
|
|
||||||
|
|
||||||
if (flip)
|
|
||||||
AddAssert("fruit position changed", () => !Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
|
||||||
else
|
|
||||||
AddAssert("fruit position unchanged", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
|
||||||
|
|
||||||
AddStep("face right", () => catcher.VisualDirection = Direction.Right);
|
|
||||||
|
|
||||||
AddAssert("fruit position restored", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
|
||||||
}
|
|
||||||
|
|
||||||
private float getCaughtObjectPosition(Fruit fruit)
|
|
||||||
{
|
|
||||||
var caughtObject = catcher.ChildrenOfType<CaughtObject>().Single(c => c.HitObject == fruit);
|
|
||||||
return caughtObject.Parent!.ToSpaceOfOtherDrawable(caughtObject.Position, catcher).X;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void catchFruit(Fruit fruit, float x)
|
|
||||||
{
|
|
||||||
fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
var drawableFruit = new DrawableFruit(fruit) { X = x };
|
|
||||||
var judgement = fruit.CreateJudgement();
|
|
||||||
catcher.OnNewResult(drawableFruit, new CatchJudgementResult(fruit, judgement)
|
|
||||||
{
|
|
||||||
Type = judgement.MaxResult
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestSkin : TrianglesSkin
|
|
||||||
{
|
|
||||||
public bool FlipCatcherPlate { get; set; }
|
|
||||||
|
|
||||||
public TestSkin()
|
|
||||||
: base(null!)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
|
||||||
{
|
|
||||||
if (lookup is CatchSkinConfiguration config)
|
|
||||||
{
|
|
||||||
if (config == CatchSkinConfiguration.FlipCatcherPlate)
|
|
||||||
return SkinUtils.As<TValue>(new Bindable<bool>(FlipCatcherPlate));
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.GetConfig<TLookup, TValue>(lookup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -293,7 +293,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
private JudgementResult createResult(CatchHitObject hitObject)
|
private JudgementResult createResult(CatchHitObject hitObject)
|
||||||
{
|
{
|
||||||
return new CatchJudgementResult(hitObject, hitObject.CreateJudgement())
|
return new CatchJudgementResult(hitObject, hitObject.Judgement)
|
||||||
{
|
{
|
||||||
Type = catcher.CanCatch(hitObject) ? HitResult.Great : HitResult.Miss
|
Type = catcher.CanCatch(hitObject) ? HitResult.Great : HitResult.Miss
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
drawableRuleset.PlayfieldAdjustmentContainer.Scale = new Vector2(1, -1);
|
drawableRuleset.PlayfieldAdjustmentContainer.Scale = new Vector2(1, -1);
|
||||||
drawableRuleset.PlayfieldAdjustmentContainer.Y = 1 - drawableRuleset.PlayfieldAdjustmentContainer.Y;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +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.
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Skinning
|
|
||||||
{
|
|
||||||
public enum CatchSkinConfiguration
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
|
||||||
/// </summary>
|
|
||||||
FlipCatcherPlate
|
|
||||||
}
|
|
||||||
}
|
|
@ -122,19 +122,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
|
|
||||||
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
|
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
|
||||||
return (IBindable<TValue>)result;
|
return (IBindable<TValue>)result;
|
||||||
|
|
||||||
case CatchSkinConfiguration config:
|
|
||||||
switch (config)
|
|
||||||
{
|
|
||||||
case CatchSkinConfiguration.FlipCatcherPlate:
|
|
||||||
// Don't flip catcher plate contents if the catcher is provided by this legacy skin.
|
|
||||||
if (GetDrawableComponent(new CatchSkinComponentLookup(CatchSkinComponents.Catcher)) != null)
|
|
||||||
return (IBindable<TValue>)new Bindable<bool>();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetConfig<TLookup, TValue>(lookup);
|
return base.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
@ -17,24 +17,36 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public CatchPlayfieldAdjustmentContainer()
|
public CatchPlayfieldAdjustmentContainer()
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre;
|
const float base_game_width = 1024f;
|
||||||
Origin = Anchor.TopCentre;
|
const float base_game_height = 768f;
|
||||||
|
|
||||||
// playfields in stable are positioned vertically at three fourths the difference between the playfield height and the window height in stable.
|
// extra bottom space for the catcher to not get cut off at tall resolutions lower than 4:3 (e.g. 5:4). number chosen based on testing with maximum catcher scale (i.e. CS 0).
|
||||||
// we can match that in lazer by using relative coordinates for Y and considering window height to be 1, and playfield height to be 0.8.
|
const float extra_bottom_space = 200f;
|
||||||
RelativePositionAxes = Axes.Y;
|
|
||||||
Y = (1 - playfield_size_adjust) / 4 * 3;
|
|
||||||
|
|
||||||
Size = new Vector2(playfield_size_adjust);
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
InternalChild = new Container
|
InternalChild = new Container
|
||||||
{
|
{
|
||||||
|
// This container limits vertical visibility of the playfield to ensure fairness between wide and tall resolutions (i.e. tall resolutions should not see more fruits).
|
||||||
|
// Note that the container still extends across the screen horizontally, so that hit explosions at the sides of the playfield do not get cut off.
|
||||||
|
Name = "Visible area",
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.X,
|
||||||
FillMode = FillMode.Fit,
|
Height = base_game_height + extra_bottom_space,
|
||||||
FillAspectRatio = 4f / 3,
|
Y = extra_bottom_space / 2,
|
||||||
Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both, }
|
Masking = true,
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
Name = "Playable area",
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
// playfields in stable are positioned vertically at three fourths the difference between the playfield height and the window height in stable.
|
||||||
|
Y = base_game_height * ((1 - playfield_size_adjust) / 4 * 3),
|
||||||
|
Size = new Vector2(base_game_width, base_game_height) * playfield_size_adjust,
|
||||||
|
Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both }
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,11 +112,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public Vector2 BodyScale => Scale * body.Scale;
|
public Vector2 BodyScale => Scale * body.Scale;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
|
||||||
/// </summary>
|
|
||||||
private bool flipCatcherPlate;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the area that can be used to attempt catches during gameplay.
|
/// Width of the area that can be used to attempt catches during gameplay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -339,8 +334,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
||||||
DEFAULT_HYPER_DASH_COLOUR;
|
DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
|
||||||
flipCatcherPlate = skin.GetConfig<CatchSkinConfiguration, bool>(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true;
|
|
||||||
|
|
||||||
runHyperDashStateTransition(HyperDashing);
|
runHyperDashStateTransition(HyperDashing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,8 +345,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
body.Scale = scaleFromDirection;
|
body.Scale = scaleFromDirection;
|
||||||
// Inverse of catcher scale is applied here, as catcher gets scaled by circle size and so do the incoming fruit.
|
// Inverse of catcher scale is applied here, as catcher gets scaled by circle size and so do the incoming fruit.
|
||||||
caughtObjectContainer.Scale = (1 / Scale.X) * (flipCatcherPlate ? scaleFromDirection : Vector2.One);
|
caughtObjectContainer.Scale = new Vector2(1 / Scale.X);
|
||||||
hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Rulesets.Mania.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Rulesets.Mania.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Rulesets.Mania.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Rulesets.Mania.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||||
@ -11,9 +21,80 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
[TestCase(0.5f)]
|
[Test]
|
||||||
[TestCase(0.1f)]
|
public void TestMinCoverageFullWidth()
|
||||||
[TestCase(0.7f)]
|
{
|
||||||
public void TestCoverage(float coverage) => CreateModTest(new ModTestData { Mod = new ManiaModFadeIn { Coverage = { Value = coverage } }, PassCondition = () => true });
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMinCoverageHalfWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set playfield width to 0.5", () => Player.Width = 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMaxCoverageFullWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set combo to 480", () => Player.ScoreProcessor.Combo.Value = 480);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMaxCoverageHalfWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set combo to 480", () => Player.ScoreProcessor.Combo.Value = 480);
|
||||||
|
AddStep("set playfield width to 0.5", () => Player.Width = 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoCoverageDuringBreak()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(),
|
||||||
|
Breaks = { new BreakPeriod(2000, 28000) }
|
||||||
|
},
|
||||||
|
PassCondition = () => Player.IsBreakTime.Value && checkCoverage(0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool checkCoverage(float expected)
|
||||||
|
{
|
||||||
|
Drawable? cover = this.ChildrenOfType<PlayfieldCoveringWrapper>().FirstOrDefault();
|
||||||
|
Drawable? filledArea = cover?.ChildrenOfType<Box>().LastOrDefault();
|
||||||
|
|
||||||
|
if (filledArea == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float scale = cover!.DrawHeight / (768 - Stage.HIT_TARGET_POSITION);
|
||||||
|
|
||||||
|
// A bit of lenience because the test may end up hitting hitobjects before any assertions.
|
||||||
|
return Precision.AlmostEquals(filledArea.DrawHeight / scale, expected, 0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||||
@ -11,9 +21,80 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
[TestCase(0.5f)]
|
[Test]
|
||||||
[TestCase(0.2f)]
|
public void TestMinCoverageFullWidth()
|
||||||
[TestCase(0.8f)]
|
{
|
||||||
public void TestCoverage(float coverage) => CreateModTest(new ModTestData { Mod = new ManiaModHidden { Coverage = { Value = coverage } }, PassCondition = () => true });
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMinCoverageHalfWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set playfield width to 0.5", () => Player.Width = 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMaxCoverageFullWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set combo to 480", () => Player.ScoreProcessor.Combo.Value = 480);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMaxCoverageHalfWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set combo to 480", () => Player.ScoreProcessor.Combo.Value = 480);
|
||||||
|
AddStep("set playfield width to 0.5", () => Player.Width = 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoCoverageDuringBreak()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(),
|
||||||
|
Breaks = { new BreakPeriod(2000, 28000) }
|
||||||
|
},
|
||||||
|
PassCondition = () => Player.IsBreakTime.Value && checkCoverage(0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool checkCoverage(float expected)
|
||||||
|
{
|
||||||
|
Drawable? cover = this.ChildrenOfType<PlayfieldCoveringWrapper>().FirstOrDefault();
|
||||||
|
Drawable? filledArea = cover?.ChildrenOfType<Box>().LastOrDefault();
|
||||||
|
|
||||||
|
if (filledArea == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float scale = cover!.DrawHeight / (768 - Stage.HIT_TARGET_POSITION);
|
||||||
|
|
||||||
|
// A bit of lenience because the test may end up hitting hitobjects before any assertions.
|
||||||
|
return Precision.AlmostEquals(filledArea.DrawHeight / scale, expected, 0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,18 +39,18 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
public void TestScrollingDownwards()
|
public void TestScrollingDownwards()
|
||||||
{
|
{
|
||||||
AddStep("set down scroll", () => scrollingContainer.Direction = ScrollingDirection.Down);
|
AddStep("set down scroll", () => scrollingContainer.Direction = ScrollingDirection.Down);
|
||||||
AddStep("set coverage = 0.5", () => cover.Coverage = 0.5f);
|
AddStep("set coverage = 0.5", () => cover.Coverage.Value = 0.5f);
|
||||||
AddStep("set coverage = 0.8f", () => cover.Coverage = 0.8f);
|
AddStep("set coverage = 0.8f", () => cover.Coverage.Value = 0.8f);
|
||||||
AddStep("set coverage = 0.2f", () => cover.Coverage = 0.2f);
|
AddStep("set coverage = 0.2f", () => cover.Coverage.Value = 0.2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestScrollingUpwards()
|
public void TestScrollingUpwards()
|
||||||
{
|
{
|
||||||
AddStep("set up scroll", () => scrollingContainer.Direction = ScrollingDirection.Up);
|
AddStep("set up scroll", () => scrollingContainer.Direction = ScrollingDirection.Up);
|
||||||
AddStep("set coverage = 0.5", () => cover.Coverage = 0.5f);
|
AddStep("set coverage = 0.5", () => cover.Coverage.Value = 0.5f);
|
||||||
AddStep("set coverage = 0.8f", () => cover.Coverage = 0.8f);
|
AddStep("set coverage = 0.8f", () => cover.Coverage.Value = 0.8f);
|
||||||
AddStep("set coverage = 0.2f", () => cover.Coverage = 0.2f);
|
AddStep("set coverage = 0.2f", () => cover.Coverage.Value = 0.2f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
@ -17,9 +18,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private Playfield playfield { get; set; } = null!;
|
private Playfield playfield { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IScrollingInfo scrollingInfo { get; set; } = null!;
|
|
||||||
|
|
||||||
protected ScrollingHitObjectContainer HitObjectContainer => ((ManiaPlayfield)playfield).GetColumn(HitObject.Column).HitObjectContainer;
|
protected ScrollingHitObjectContainer HitObjectContainer => ((ManiaPlayfield)playfield).GetColumn(HitObject.Column).HitObjectContainer;
|
||||||
|
|
||||||
protected ManiaSelectionBlueprint(T hitObject)
|
protected ManiaSelectionBlueprint(T hitObject)
|
||||||
@ -28,14 +26,31 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
RelativeSizeAxes = Axes.None;
|
RelativeSizeAxes = Axes.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
private readonly IBindable<ScrollingDirection> directionBindable = new Bindable<ScrollingDirection>();
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
var anchor = scrollingInfo.Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
directionBindable.BindTo(scrollingInfo.Direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
directionBindable.BindValueChanged(onDirectionChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
|
{
|
||||||
|
var anchor = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||||
Anchor = Origin = anchor;
|
Anchor = Origin = anchor;
|
||||||
foreach (var child in InternalChildren)
|
foreach (var child in InternalChildren)
|
||||||
child.Anchor = child.Origin = anchor;
|
child.Anchor = child.Origin = anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
Position = Parent!.ToLocalSpace(HitObjectContainer.ScreenSpacePositionAtTime(HitObject.StartTime)) - AnchorPosition;
|
Position = Parent!.ToLocalSpace(HitObjectContainer.ScreenSpacePositionAtTime(HitObject.StartTime)) - AnchorPosition;
|
||||||
Width = HitObjectContainer.DrawWidth;
|
Width = HitObjectContainer.DrawWidth;
|
||||||
|
@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new ManiaModHardRock(),
|
new ManiaModHardRock(),
|
||||||
new MultiMod(new ManiaModSuddenDeath(), new ManiaModPerfect()),
|
new MultiMod(new ManiaModSuddenDeath(), new ManiaModPerfect()),
|
||||||
new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()),
|
new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()),
|
||||||
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
|
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden(), new ManiaModCover()),
|
||||||
new ManiaModFlashlight(),
|
new ManiaModFlashlight(),
|
||||||
new ModAccuracyChallenge(),
|
new ModAccuracyChallenge(),
|
||||||
};
|
};
|
||||||
|
44
osu.Game.Rulesets.Mania/Mods/ManiaModCover.cs
Normal file
44
osu.Game.Rulesets.Mania/Mods/ManiaModCover.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public class ManiaModCover : ManiaModWithPlayfieldCover
|
||||||
|
{
|
||||||
|
public override string Name => "Cover";
|
||||||
|
public override string Acronym => "CO";
|
||||||
|
|
||||||
|
public override LocalisableString Description => @"Decrease the playfield's viewing area.";
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
protected override CoverExpandDirection ExpandDirection => Direction.Value;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
|
||||||
|
{
|
||||||
|
typeof(ManiaModHidden),
|
||||||
|
typeof(ManiaModFadeIn)
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
public override bool Ranked => false;
|
||||||
|
|
||||||
|
[SettingSource("Coverage", "The proportion of playfield height that notes will be hidden for.")]
|
||||||
|
public override BindableNumber<float> Coverage { get; } = new BindableFloat(0.5f)
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 0.2f,
|
||||||
|
MaxValue = 0.8f,
|
||||||
|
Default = 0.5f,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Direction", "The direction on which the cover is applied")]
|
||||||
|
public Bindable<CoverExpandDirection> Direction { get; } = new Bindable<CoverExpandDirection>();
|
||||||
|
}
|
||||||
|
}
|
@ -3,29 +3,24 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModFadeIn : ManiaModPlayfieldCover
|
public class ManiaModFadeIn : ManiaModHidden
|
||||||
{
|
{
|
||||||
public override string Name => "Fade In";
|
public override string Name => "Fade In";
|
||||||
public override string Acronym => "FI";
|
public override string Acronym => "FI";
|
||||||
public override LocalisableString Description => @"Keys appear out of nowhere!";
|
public override LocalisableString Description => @"Keys appear out of nowhere!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModHidden)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
|
||||||
|
{
|
||||||
|
typeof(ManiaModHidden),
|
||||||
|
typeof(ManiaModCover)
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
|
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
|
||||||
|
|
||||||
public override BindableNumber<float> Coverage { get; } = new BindableFloat(0.5f)
|
|
||||||
{
|
|
||||||
Precision = 0.1f,
|
|
||||||
MinValue = 0.1f,
|
|
||||||
MaxValue = 0.7f,
|
|
||||||
Default = 0.5f,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,27 +3,104 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.Skinning;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModHidden : ManiaModPlayfieldCover
|
public partial class ManiaModHidden : ManiaModWithPlayfieldCover, IApplicableToPlayer, IUpdatableByPlayfield
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// osu!stable is referenced to 768px.
|
||||||
|
/// </summary>
|
||||||
|
private const float reference_playfield_height = 768;
|
||||||
|
|
||||||
|
public const float MIN_COVERAGE = 160f;
|
||||||
|
public const float MAX_COVERAGE = 400f;
|
||||||
|
private const float coverage_increase_per_combo = 0.5f;
|
||||||
|
|
||||||
public override LocalisableString Description => @"Keys fade out before you hit them!";
|
public override LocalisableString Description => @"Keys fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override BindableNumber<float> Coverage { get; } = new BindableFloat(0.5f)
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
typeof(ManiaModFadeIn),
|
||||||
MinValue = 0.2f,
|
typeof(ManiaModCover)
|
||||||
MaxValue = 0.8f,
|
}).ToArray();
|
||||||
Default = 0.5f,
|
|
||||||
};
|
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModFadeIn)).ToArray();
|
|
||||||
|
|
||||||
|
public override BindableNumber<float> Coverage { get; } = new BindableFloat(MIN_COVERAGE);
|
||||||
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
||||||
|
|
||||||
|
private readonly IBindable<bool> isBreakTime = new Bindable<bool>();
|
||||||
|
private readonly BindableInt combo = new BindableInt();
|
||||||
|
|
||||||
|
public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
||||||
|
{
|
||||||
|
base.ApplyToScoreProcessor(scoreProcessor);
|
||||||
|
|
||||||
|
combo.UnbindAll();
|
||||||
|
combo.BindTo(scoreProcessor.Combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToPlayer(Player player)
|
||||||
|
{
|
||||||
|
isBreakTime.UnbindAll();
|
||||||
|
isBreakTime.BindTo(player.IsBreakTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(Playfield playfield)
|
||||||
|
{
|
||||||
|
Coverage.Value = isBreakTime.Value
|
||||||
|
? 0
|
||||||
|
: Math.Min(MAX_COVERAGE, MIN_COVERAGE + combo.Value * coverage_increase_per_combo) / reference_playfield_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override PlayfieldCoveringWrapper CreateCover(Drawable content) => new LegacyPlayfieldCover(content);
|
||||||
|
|
||||||
|
private partial class LegacyPlayfieldCover : PlayfieldCoveringWrapper
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private ISkinSource skin { get; set; } = null!;
|
||||||
|
|
||||||
|
private IBindable<float>? hitPosition;
|
||||||
|
|
||||||
|
public LegacyPlayfieldCover(Drawable content)
|
||||||
|
: base(content)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
skin.SourceChanged += onSkinChanged;
|
||||||
|
onSkinChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSkinChanged()
|
||||||
|
{
|
||||||
|
hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override float GetHeight(float coverage)
|
||||||
|
{
|
||||||
|
// In osu!stable, the cover is applied in absolute (x768) coordinates from the hit position.
|
||||||
|
float availablePlayfieldHeight = Math.Abs(reference_playfield_height - (hitPosition?.Value ?? Stage.HIT_TARGET_POSITION));
|
||||||
|
|
||||||
|
if (availablePlayfieldHeight == 0)
|
||||||
|
return base.GetHeight(coverage);
|
||||||
|
|
||||||
|
return base.GetHeight(coverage) * reference_playfield_height / availablePlayfieldHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ using System.Linq;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -15,7 +14,7 @@ using osu.Game.Rulesets.UI;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public abstract class ManiaModPlayfieldCover : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
public abstract class ManiaModWithPlayfieldCover : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
||||||
|
|
||||||
@ -24,7 +23,9 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract CoverExpandDirection ExpandDirection { get; }
|
protected abstract CoverExpandDirection ExpandDirection { get; }
|
||||||
|
|
||||||
[SettingSource("Coverage", "The proportion of playfield height that notes will be hidden for.")]
|
/// <summary>
|
||||||
|
/// The relative area that should be completely covered. This does not include the fade.
|
||||||
|
/// </summary>
|
||||||
public abstract BindableNumber<float> Coverage { get; }
|
public abstract BindableNumber<float> Coverage { get; }
|
||||||
|
|
||||||
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
||||||
@ -37,15 +38,17 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
Container hocParent = (Container)hoc.Parent!;
|
Container hocParent = (Container)hoc.Parent!;
|
||||||
|
|
||||||
hocParent.Remove(hoc, false);
|
hocParent.Remove(hoc, false);
|
||||||
hocParent.Add(new PlayfieldCoveringWrapper(hoc).With(c =>
|
hocParent.Add(CreateCover(hoc).With(c =>
|
||||||
{
|
{
|
||||||
c.RelativeSizeAxes = Axes.Both;
|
c.RelativeSizeAxes = Axes.Both;
|
||||||
c.Direction = ExpandDirection;
|
c.Direction = ExpandDirection;
|
||||||
c.Coverage = Coverage.Value;
|
c.Coverage.BindTo(Coverage);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual PlayfieldCoveringWrapper CreateCover(Drawable content) => new PlayfieldCoveringWrapper(content);
|
||||||
|
|
||||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
}
|
}
|
@ -243,6 +243,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
|
|
||||||
bodySprite.FillMode = FillMode.Stretch;
|
bodySprite.FillMode = FillMode.Stretch;
|
||||||
// i dunno this looks about right??
|
// i dunno this looks about right??
|
||||||
|
// the guard against zero draw height is intended for zero-length hold notes. yes, such cases have been spotted in the wild.
|
||||||
|
if (sprite.DrawHeight > 0)
|
||||||
bodySprite.Scale = new Vector2(1, scaleDirection * 32800 / sprite.DrawHeight);
|
bodySprite.Scale = new Vector2(1, scaleDirection * 32800 / sprite.DrawHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
@ -149,7 +148,18 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the total amount of columns across all stages in this playfield.
|
/// Retrieves the total amount of columns across all stages in this playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int TotalColumns => stages.Sum(s => s.Columns.Length);
|
public int TotalColumns
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int sum = 0;
|
||||||
|
|
||||||
|
foreach (var stage in stages)
|
||||||
|
sum += stage.Columns.Length;
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Stage getStageByColumn(int column)
|
private Stage getStageByColumn(int column)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
@ -8,17 +10,24 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using Container = osu.Framework.Graphics.Containers.Container;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see cref="Container"/> that has its contents partially hidden by an adjustable "cover". This is intended to be used in a playfield.
|
/// A <see cref="Framework.Graphics.Containers.Container"/> that has its contents partially hidden by an adjustable "cover". This is intended to be used in a playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PlayfieldCoveringWrapper : CompositeDrawable
|
public partial class PlayfieldCoveringWrapper : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The relative area that should be completely covered. This does not include the fade.
|
||||||
|
/// </summary>
|
||||||
|
public readonly BindableFloat Coverage = new BindableFloat();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The complete cover, including gradient and fill.
|
/// The complete cover, including gradient and fill.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -36,6 +45,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
private readonly IBindable<ScrollingDirection> scrollDirection = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> scrollDirection = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
private float currentCoverageHeight;
|
||||||
|
|
||||||
public PlayfieldCoveringWrapper(Drawable content)
|
public PlayfieldCoveringWrapper(Drawable content)
|
||||||
{
|
{
|
||||||
InternalChild = new BufferedContainer
|
InternalChild = new BufferedContainer
|
||||||
@ -94,21 +105,46 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
scrollDirection.BindValueChanged(onScrollDirectionChanged, true);
|
scrollDirection.BindValueChanged(onScrollDirectionChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
updateCoverSize(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
updateCoverSize(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCoverSize(bool instant)
|
||||||
|
{
|
||||||
|
float targetCoverage;
|
||||||
|
float targetAlpha;
|
||||||
|
|
||||||
|
if (instant)
|
||||||
|
{
|
||||||
|
targetCoverage = Coverage.Value;
|
||||||
|
targetAlpha = Coverage.Value > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetCoverage = (float)Interpolation.DampContinuously(currentCoverageHeight, Coverage.Value, 25, Math.Abs(Time.Elapsed));
|
||||||
|
targetAlpha = (float)Interpolation.DampContinuously(gradient.Alpha, Coverage.Value > 0 ? 1 : 0, 25, Math.Abs(Time.Elapsed));
|
||||||
|
}
|
||||||
|
|
||||||
|
filled.Height = GetHeight(targetCoverage);
|
||||||
|
gradient.Y = -GetHeight(targetCoverage);
|
||||||
|
gradient.Alpha = targetAlpha;
|
||||||
|
|
||||||
|
currentCoverageHeight = targetCoverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual float GetHeight(float coverage) => coverage;
|
||||||
|
|
||||||
private void onScrollDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
private void onScrollDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
=> cover.Rotation = direction.NewValue == ScrollingDirection.Up ? 0 : 180f;
|
=> cover.Rotation = direction.NewValue == ScrollingDirection.Up ? 0 : 180f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The relative area that should be completely covered. This does not include the fade.
|
|
||||||
/// </summary>
|
|
||||||
public float Coverage
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
filled.Height = value;
|
|
||||||
gradient.Y = -value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The direction in which the cover expands.
|
/// The direction in which the cover expands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -123,11 +159,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cover expands along the scrolling direction.
|
/// The cover expands along the scrolling direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Description("Along scroll")]
|
||||||
AlongScroll,
|
AlongScroll,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cover expands against the scrolling direction.
|
/// The cover expands against the scrolling direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Description("Against scroll")]
|
||||||
AgainstScroll
|
AgainstScroll
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Rulesets.Osu.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Rulesets.Osu.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Rulesets.Osu.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Rulesets.Osu.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
// multipled by 2 to nullify the score multiplier. (autoplay mod selected)
|
// multipled by 2 to nullify the score multiplier. (autoplay mod selected)
|
||||||
long totalScore = scoreProcessor.TotalScore.Value * 2;
|
long totalScore = scoreProcessor.TotalScore.Value * 2;
|
||||||
return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * scoreProcessor.GetBaseScoreForResult(new SpinnerTick().CreateJudgement().MaxResult);
|
return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * scoreProcessor.GetBaseScoreForResult(new SpinnerTick().Judgement.MaxResult);
|
||||||
});
|
});
|
||||||
|
|
||||||
addSeekStep(0);
|
addSeekStep(0);
|
||||||
|
@ -40,7 +40,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||||
double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
|
double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
|
||||||
double speedNotes = ((Speed)skills[2]).RelevantNoteCount();
|
double speedNotes = ((Speed)skills[2]).RelevantNoteCount();
|
||||||
double flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
|
|
||||||
|
double flashlightRating = 0.0;
|
||||||
|
|
||||||
|
if (mods.Any(h => h is OsuModFlashlight))
|
||||||
|
flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
|
||||||
|
|
||||||
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
|
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
|
||||||
|
|
||||||
@ -126,13 +130,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||||
{
|
{
|
||||||
return new Skill[]
|
var skills = new List<Skill>
|
||||||
{
|
{
|
||||||
new Aim(mods, true),
|
new Aim(mods, true),
|
||||||
new Aim(mods, false),
|
new Aim(mods, false),
|
||||||
new Speed(mods),
|
new Speed(mods)
|
||||||
new Flashlight(mods)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (mods.Any(h => h is OsuModFlashlight))
|
||||||
|
skills.Add(new Flashlight(mods));
|
||||||
|
|
||||||
|
return skills.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||||
|
@ -232,7 +232,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
|
|
||||||
IList<HitObject> nestedObjects = slider.NestedHitObjects;
|
IList<HitObject> nestedObjects = slider.NestedHitObjects;
|
||||||
|
|
||||||
SliderTick? lastRealTick = slider.NestedHitObjects.OfType<SliderTick>().LastOrDefault();
|
SliderTick? lastRealTick = null;
|
||||||
|
|
||||||
|
foreach (var hitobject in slider.NestedHitObjects)
|
||||||
|
{
|
||||||
|
if (hitobject is SliderTick tick)
|
||||||
|
lastRealTick = tick;
|
||||||
|
}
|
||||||
|
|
||||||
if (lastRealTick?.StartTime > trackingEndTime)
|
if (lastRealTick?.StartTime > trackingEndTime)
|
||||||
{
|
{
|
||||||
|
@ -78,9 +78,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
|
|||||||
|
|
||||||
Scale = new Vector2(hitObject.Scale);
|
Scale = new Vector2(hitObject.Scale);
|
||||||
|
|
||||||
if (hitObject is IHasComboInformation combo)
|
|
||||||
ring.BorderColour = combo.GetComboColour(skin);
|
|
||||||
|
|
||||||
double editorTime = editorClock.CurrentTime;
|
double editorTime = editorClock.CurrentTime;
|
||||||
double hitObjectTime = hitObject.StartTime;
|
double hitObjectTime = hitObject.StartTime;
|
||||||
bool hasReachedObject = editorTime >= hitObjectTime;
|
bool hasReachedObject = editorTime >= hitObjectTime;
|
||||||
@ -92,6 +89,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
|
|||||||
|
|
||||||
ring.Scale = new Vector2(1 + 0.1f * ringScale);
|
ring.Scale = new Vector2(1 + 0.1f * ringScale);
|
||||||
content.Alpha = 0.9f * (1 - alpha);
|
content.Alpha = 0.9f * (1 - alpha);
|
||||||
|
|
||||||
|
// TODO: should only update colour on skin/combo/object change.
|
||||||
|
if (hitObject is IHasComboInformation combo && content.Alpha > 0)
|
||||||
|
ring.BorderColour = combo.GetComboColour(skin);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
content.Alpha = 0;
|
content.Alpha = 0;
|
||||||
|
@ -416,8 +416,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
DrawableObject.SliderBody?.ToScreenSpace(DrawableObject.SliderBody.PathEndOffset) ?? BodyPiece.ToScreenSpace(BodyPiece.PathEndLocation)
|
DrawableObject.SliderBody?.ToScreenSpace(DrawableObject.SliderBody.PathEndOffset) ?? BodyPiece.ToScreenSpace(BodyPiece.PathEndLocation)
|
||||||
};
|
};
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
|
||||||
BodyPiece.ReceivePositionalInputAt(screenSpacePos) || ControlPointVisualiser?.Pieces.Any(p => p.ReceivePositionalInputAt(screenSpacePos)) == true;
|
{
|
||||||
|
if (BodyPiece.ReceivePositionalInputAt(screenSpacePos))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (ControlPointVisualiser == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var p in ControlPointVisualiser.Pieces)
|
||||||
|
{
|
||||||
|
if (p.ReceivePositionalInputAt(screenSpacePos))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual SliderCircleOverlay CreateCircleOverlay(Slider slider, SliderPosition position) => new SliderCircleOverlay(slider, position);
|
protected virtual SliderCircleOverlay CreateCircleOverlay(Slider slider, SliderPosition position) => new SliderCircleOverlay(slider, position);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -38,12 +38,18 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
private ReplayState<OsuAction> state = null!;
|
private ReplayState<OsuAction> state = null!;
|
||||||
private double lastStateChangeTime;
|
private double lastStateChangeTime;
|
||||||
|
|
||||||
|
private DrawableOsuRuleset ruleset = null!;
|
||||||
|
private IPressHandler pressHandler = null!;
|
||||||
|
|
||||||
private bool hasReplay;
|
private bool hasReplay;
|
||||||
|
private bool legacyReplay;
|
||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
|
ruleset = (DrawableOsuRuleset)drawableRuleset;
|
||||||
|
|
||||||
// grab the input manager for future use.
|
// grab the input manager for future use.
|
||||||
osuInputManager = ((DrawableOsuRuleset)drawableRuleset).KeyBindingInputManager;
|
osuInputManager = ruleset.KeyBindingInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyToPlayer(Player player)
|
public void ApplyToPlayer(Player player)
|
||||||
@ -51,15 +57,22 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
if (osuInputManager.ReplayInputHandler != null)
|
if (osuInputManager.ReplayInputHandler != null)
|
||||||
{
|
{
|
||||||
hasReplay = true;
|
hasReplay = true;
|
||||||
|
|
||||||
|
Debug.Assert(ruleset.ReplayScore != null);
|
||||||
|
legacyReplay = ruleset.ReplayScore.ScoreInfo.IsLegacyScore;
|
||||||
|
|
||||||
|
pressHandler = legacyReplay ? new LegacyReplayPressHandler(this) : new PressHandler(this);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pressHandler = new PressHandler(this);
|
||||||
osuInputManager.AllowGameplayInputs = false;
|
osuInputManager.AllowGameplayInputs = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(Playfield playfield)
|
public void Update(Playfield playfield)
|
||||||
{
|
{
|
||||||
if (hasReplay)
|
if (hasReplay && !legacyReplay)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool requiresHold = false;
|
bool requiresHold = false;
|
||||||
@ -132,11 +145,62 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
if (down)
|
if (down)
|
||||||
{
|
{
|
||||||
state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
|
pressHandler.HandlePress(wasLeft);
|
||||||
wasLeft = !wasLeft;
|
wasLeft = !wasLeft;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pressHandler.HandleRelease(wasLeft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.Apply(osuInputManager.CurrentState, osuInputManager);
|
private interface IPressHandler
|
||||||
|
{
|
||||||
|
void HandlePress(bool wasLeft);
|
||||||
|
void HandleRelease(bool wasLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PressHandler : IPressHandler
|
||||||
|
{
|
||||||
|
private readonly OsuModRelax mod;
|
||||||
|
|
||||||
|
public PressHandler(OsuModRelax mod)
|
||||||
|
{
|
||||||
|
this.mod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandlePress(bool wasLeft)
|
||||||
|
{
|
||||||
|
mod.state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
|
||||||
|
mod.state.Apply(mod.osuInputManager.CurrentState, mod.osuInputManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleRelease(bool wasLeft)
|
||||||
|
{
|
||||||
|
mod.state.Apply(mod.osuInputManager.CurrentState, mod.osuInputManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// legacy replays do not contain key-presses with Relax mod, so they need to be triggered by themselves.
|
||||||
|
private class LegacyReplayPressHandler : IPressHandler
|
||||||
|
{
|
||||||
|
private readonly OsuModRelax mod;
|
||||||
|
|
||||||
|
public LegacyReplayPressHandler(OsuModRelax mod)
|
||||||
|
{
|
||||||
|
this.mod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandlePress(bool wasLeft)
|
||||||
|
{
|
||||||
|
mod.osuInputManager.KeyBindingContainer.TriggerPressed(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleRelease(bool wasLeft)
|
||||||
|
{
|
||||||
|
// this intentionally releases right when `wasLeft` is true because `wasLeft` is set at point of press and not at point of release
|
||||||
|
mod.osuInputManager.KeyBindingContainer.TriggerReleased(wasLeft ? OsuAction.RightButton : OsuAction.LeftButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -149,8 +148,11 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
StackHeightBindable.BindValueChanged(height =>
|
StackHeightBindable.BindValueChanged(height =>
|
||||||
{
|
{
|
||||||
foreach (var nested in NestedHitObjects.OfType<OsuHitObject>())
|
foreach (var nested in NestedHitObjects)
|
||||||
nested.StackHeight = height.NewValue;
|
{
|
||||||
|
if (nested is OsuHitObject osuHitObject)
|
||||||
|
osuHitObject.StackHeight = height.NewValue;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,18 +252,25 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
protected void UpdateNestedSamples()
|
protected void UpdateNestedSamples()
|
||||||
{
|
{
|
||||||
var firstSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)
|
// TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
||||||
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
HitSampleInfo tickSample = (Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault())?.With("slidertick");
|
||||||
var sampleList = new List<HitSampleInfo>();
|
|
||||||
|
|
||||||
if (firstSample != null)
|
foreach (var nested in NestedHitObjects)
|
||||||
sampleList.Add(firstSample.With("slidertick"));
|
{
|
||||||
|
switch (nested)
|
||||||
|
{
|
||||||
|
case SliderTick tick:
|
||||||
|
tick.SamplesBindable.Clear();
|
||||||
|
|
||||||
foreach (var tick in NestedHitObjects.OfType<SliderTick>())
|
if (tickSample != null)
|
||||||
tick.Samples = sampleList;
|
tick.SamplesBindable.Add(tickSample);
|
||||||
|
break;
|
||||||
|
|
||||||
foreach (var repeat in NestedHitObjects.OfType<SliderRepeat>())
|
case SliderRepeat repeat:
|
||||||
repeat.Samples = this.GetNodeSamples(repeat.RepeatIndex + 1);
|
repeat.Samples = this.GetNodeSamples(repeat.RepeatIndex + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (HeadCircle != null)
|
if (HeadCircle != null)
|
||||||
HeadCircle.Samples = this.GetNodeSamples(0);
|
HeadCircle.Samples = this.GetNodeSamples(0);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Lists;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
{
|
{
|
||||||
public partial class OsuInputManager : RulesetInputManager<OsuAction>
|
public partial class OsuInputManager : RulesetInputManager<OsuAction>
|
||||||
{
|
{
|
||||||
public IEnumerable<OsuAction> PressedActions => KeyBindingContainer.PressedActions;
|
public SlimReadOnlyListWrapper<OsuAction> PressedActions => KeyBindingContainer.PressedActions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether gameplay input buttons should be allowed.
|
/// Whether gameplay input buttons should be allowed.
|
||||||
|
@ -25,8 +25,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
{
|
{
|
||||||
Texture = textures.Get(@"Gameplay/osu/approachcircle").WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2);
|
Texture = textures.Get(@"Gameplay/osu/approachcircle").WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2);
|
||||||
|
|
||||||
// account for the sprite being used for the default approach circle being taken from stable,
|
// In triangles and argon skins, we expanded hitcircles to take up the full 128 px which are clickable,
|
||||||
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
|
// but still use the old approach circle sprite. To make it feel correct (ie. disappear as it collides
|
||||||
|
// with the hitcircle, *not when it overlaps the border*) we need to expand it slightly.
|
||||||
Scale = new Vector2(128 / 118f);
|
Scale = new Vector2(128 / 118f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
@ -26,10 +25,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
var texture = skin.GetTexture(@"approachcircle");
|
var texture = skin.GetTexture(@"approachcircle");
|
||||||
Debug.Assert(texture != null);
|
Debug.Assert(texture != null);
|
||||||
Texture = texture.WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2);
|
Texture = texture.WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2);
|
||||||
|
|
||||||
// account for the sprite being used for the default approach circle being taken from stable,
|
|
||||||
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
|
|
||||||
Scale = new Vector2(128 / 118f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
// Note that the scale adjust here is 2 instead of DrawableSliderBall.FOLLOW_AREA to match legacy behaviour.
|
// Note that the scale adjust here is 2 instead of DrawableSliderBall.FOLLOW_AREA to match legacy behaviour.
|
||||||
// This means the actual tracking area for gameplay purposes is larger than the sprite (but skins may be accounting for this).
|
// This means the actual tracking area for gameplay purposes is larger than the sprite (but skins may be accounting for this).
|
||||||
this.ScaleTo(0.5f).ScaleTo(2f, Math.Min(180f, remainingTime), Easing.Out)
|
this.ScaleTo(1f).ScaleTo(2f, Math.Min(180f, remainingTime), Easing.Out)
|
||||||
.FadeTo(0).FadeTo(1f, Math.Min(60f, remainingTime));
|
.FadeTo(0).FadeTo(1f, Math.Min(60f, remainingTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Rulesets.Osu.Configuration;
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -39,6 +40,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||||
{
|
{
|
||||||
|
if ((Clock as IGameplayClock)?.IsRewinding == true)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (showRipples.Value)
|
if (showRipples.Value)
|
||||||
{
|
{
|
||||||
AddInternal(ripplePool.Get(r =>
|
AddInternal(ripplePool.Get(r =>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Rulesets.Taiko.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Rulesets.Taiko.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Rulesets.Taiko.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Rulesets.Taiko.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -126,11 +126,11 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
foreach (var nested in beatmap.HitObjects[0].NestedHitObjects)
|
foreach (var nested in beatmap.HitObjects[0].NestedHitObjects)
|
||||||
{
|
{
|
||||||
var nestedJudgement = nested.CreateJudgement();
|
var nestedJudgement = nested.Judgement;
|
||||||
healthProcessor.ApplyResult(new JudgementResult(nested, nestedJudgement) { Type = nestedJudgement.MaxResult });
|
healthProcessor.ApplyResult(new JudgementResult(nested, nestedJudgement) { Type = nestedJudgement.MaxResult });
|
||||||
}
|
}
|
||||||
|
|
||||||
var judgement = beatmap.HitObjects[0].CreateJudgement();
|
var judgement = beatmap.HitObjects[0].Judgement;
|
||||||
healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], judgement) { Type = judgement.MaxResult });
|
healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], judgement) { Type = judgement.MaxResult });
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
@ -159,11 +159,11 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
foreach (var nested in beatmap.HitObjects[0].NestedHitObjects)
|
foreach (var nested in beatmap.HitObjects[0].NestedHitObjects)
|
||||||
{
|
{
|
||||||
var nestedJudgement = nested.CreateJudgement();
|
var nestedJudgement = nested.Judgement;
|
||||||
healthProcessor.ApplyResult(new JudgementResult(nested, nestedJudgement) { Type = nestedJudgement.MaxResult });
|
healthProcessor.ApplyResult(new JudgementResult(nested, nestedJudgement) { Type = nestedJudgement.MaxResult });
|
||||||
}
|
}
|
||||||
|
|
||||||
var judgement = beatmap.HitObjects[0].CreateJudgement();
|
var judgement = beatmap.HitObjects[0].Judgement;
|
||||||
healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], judgement) { Type = judgement.MaxResult });
|
healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], judgement) { Type = judgement.MaxResult });
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
|
@ -31,4 +31,22 @@
|
|||||||
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
||||||
<PackageReference Include="Moq" Version="4.17.2" />
|
<PackageReference Include="Moq" Version="4.17.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<!-- osu.Framework.Android depends on https://www.nuget.org/packages/Xamarin.AndroidX.Window,
|
||||||
|
which - via a chain of transitive dependencies - also includes https://www.nuget.org/packages/Xamarin.Jetbrains.Annotations,
|
||||||
|
which causes compile failures such as:
|
||||||
|
|
||||||
|
The type 'NotNullAttribute' exists in both 'JetBrains.Annotations, Version=4242.42.42.42, Culture=neutral, PublicKeyToken=1010a0d8d6380325'
|
||||||
|
and 'Xamarin.Jetbrains.Annotations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' [D:\a\osu\osu\osu.Game.Tests.Android\osu.Game.Tests.Android.csproj]
|
||||||
|
|
||||||
|
We cannot easily change the source files, because of how this project works
|
||||||
|
(all source files are basically symlinked from the desktop test project,
|
||||||
|
so changing anything there just for the sake of mobile would be strange).
|
||||||
|
Thus, apply the following "interesting" workaround as borrowed from https://stackoverflow.com/a/65127159 instead. -->
|
||||||
|
<Target Name="AddPackageAliases" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
|
||||||
|
<ItemGroup>
|
||||||
|
<ReferencePath Condition="%(Filename) == 'Xamarin.Jetbrains.Annotations'">
|
||||||
|
<Aliases>XamarinJetbrainsAnnotations</Aliases>
|
||||||
|
</ReferencePath>
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -28,7 +28,12 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestLocalCacheQueriedFirst()
|
public void TestLocalCacheQueriedFirst()
|
||||||
{
|
{
|
||||||
var localLookupResult = new OnlineBeatmapMetadata { BeatmapID = 123456, BeatmapStatus = BeatmapOnlineStatus.Ranked };
|
var localLookupResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 123456,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
};
|
||||||
localCachedMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
localCachedMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
||||||
localCachedMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out localLookupResult))
|
localCachedMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out localLookupResult))
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
@ -42,6 +47,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
metadataLookup.Update(beatmapSet, preferOnlineFetch: false);
|
metadataLookup.Update(beatmapSet, preferOnlineFetch: false);
|
||||||
|
|
||||||
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
|
Assert.That(beatmapSet.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata>.IsAny!), Times.Once);
|
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata>.IsAny!), Times.Once);
|
||||||
apiMetadataSourceMock.Verify(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out It.Ref<OnlineBeatmapMetadata>.IsAny!), Times.Never);
|
apiMetadataSourceMock.Verify(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out It.Ref<OnlineBeatmapMetadata>.IsAny!), Times.Never);
|
||||||
}
|
}
|
||||||
@ -54,7 +60,12 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
localCachedMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out localLookupResult))
|
localCachedMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out localLookupResult))
|
||||||
.Returns(false);
|
.Returns(false);
|
||||||
|
|
||||||
var onlineLookupResult = new OnlineBeatmapMetadata { BeatmapID = 123456, BeatmapStatus = BeatmapOnlineStatus.Ranked };
|
var onlineLookupResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 123456,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
};
|
||||||
apiMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
apiMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
||||||
apiMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out onlineLookupResult))
|
apiMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out onlineLookupResult))
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
@ -66,6 +77,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
metadataLookup.Update(beatmapSet, preferOnlineFetch: false);
|
metadataLookup.Update(beatmapSet, preferOnlineFetch: false);
|
||||||
|
|
||||||
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
|
Assert.That(beatmapSet.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
||||||
apiMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
apiMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
||||||
}
|
}
|
||||||
@ -73,12 +85,22 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPreferOnlineFetch()
|
public void TestPreferOnlineFetch()
|
||||||
{
|
{
|
||||||
var localLookupResult = new OnlineBeatmapMetadata { BeatmapID = 123456, BeatmapStatus = BeatmapOnlineStatus.Ranked };
|
var localLookupResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 123456,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
};
|
||||||
localCachedMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
localCachedMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
||||||
localCachedMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out localLookupResult))
|
localCachedMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out localLookupResult))
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
|
|
||||||
var onlineLookupResult = new OnlineBeatmapMetadata { BeatmapID = 123456, BeatmapStatus = BeatmapOnlineStatus.Graveyard };
|
var onlineLookupResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 123456,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Graveyard,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Graveyard,
|
||||||
|
};
|
||||||
apiMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
apiMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
||||||
apiMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out onlineLookupResult))
|
apiMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out onlineLookupResult))
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
@ -90,6 +112,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
metadataLookup.Update(beatmapSet, preferOnlineFetch: true);
|
metadataLookup.Update(beatmapSet, preferOnlineFetch: true);
|
||||||
|
|
||||||
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Graveyard));
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Graveyard));
|
||||||
|
Assert.That(beatmapSet.Status, Is.EqualTo(BeatmapOnlineStatus.Graveyard));
|
||||||
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Never);
|
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Never);
|
||||||
apiMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
apiMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
||||||
}
|
}
|
||||||
@ -97,7 +120,12 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPreferOnlineFetchFallsBackToLocalCacheIfOnlineSourceUnavailable()
|
public void TestPreferOnlineFetchFallsBackToLocalCacheIfOnlineSourceUnavailable()
|
||||||
{
|
{
|
||||||
var localLookupResult = new OnlineBeatmapMetadata { BeatmapID = 123456, BeatmapStatus = BeatmapOnlineStatus.Ranked };
|
var localLookupResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 123456,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
};
|
||||||
localCachedMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
localCachedMetadataSourceMock.Setup(src => src.Available).Returns(true);
|
||||||
localCachedMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out localLookupResult))
|
localCachedMetadataSourceMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out localLookupResult))
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
@ -111,6 +139,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
metadataLookup.Update(beatmapSet, preferOnlineFetch: true);
|
metadataLookup.Update(beatmapSet, preferOnlineFetch: true);
|
||||||
|
|
||||||
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
|
Assert.That(beatmapSet.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
||||||
apiMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Never);
|
apiMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Never);
|
||||||
}
|
}
|
||||||
@ -135,6 +164,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
metadataLookup.Update(beatmapSet, preferOnlineFetch: false);
|
metadataLookup.Update(beatmapSet, preferOnlineFetch: false);
|
||||||
|
|
||||||
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
Assert.That(beatmapSet.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
Assert.That(beatmap.OnlineID, Is.EqualTo(-1));
|
Assert.That(beatmap.OnlineID, Is.EqualTo(-1));
|
||||||
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
localCachedMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
||||||
apiMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
apiMetadataSourceMock.Verify(src => src.TryLookup(beatmap, out It.Ref<OnlineBeatmapMetadata?>.IsAny!), Times.Once);
|
||||||
@ -163,6 +193,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
||||||
|
|
||||||
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
Assert.That(beatmapSet.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
Assert.That(beatmap.OnlineID, Is.EqualTo(123456));
|
Assert.That(beatmap.OnlineID, Is.EqualTo(123456));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,5 +224,217 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
|
|
||||||
Assert.That(beatmap.OnlineID, Is.EqualTo(123456));
|
Assert.That(beatmap.OnlineID, Is.EqualTo(123456));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestReturnedMetadataHasDifferentOnlineID([Values] bool preferOnlineFetch)
|
||||||
|
{
|
||||||
|
var lookupResult = new OnlineBeatmapMetadata { BeatmapID = 654321, BeatmapStatus = BeatmapOnlineStatus.Ranked };
|
||||||
|
|
||||||
|
var targetMock = preferOnlineFetch ? apiMetadataSourceMock : localCachedMetadataSourceMock;
|
||||||
|
targetMock.Setup(src => src.Available).Returns(true);
|
||||||
|
targetMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out lookupResult))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var beatmap = new BeatmapInfo { OnlineID = 123456 };
|
||||||
|
var beatmapSet = new BeatmapSetInfo(beatmap.Yield());
|
||||||
|
beatmap.BeatmapSet = beatmapSet;
|
||||||
|
|
||||||
|
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
||||||
|
|
||||||
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
Assert.That(beatmap.OnlineID, Is.EqualTo(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMetadataLookupForBeatmapWithoutPopulatedIDAndCorrectHash([Values] bool preferOnlineFetch)
|
||||||
|
{
|
||||||
|
var lookupResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 654321,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
MD5Hash = @"deadbeef",
|
||||||
|
};
|
||||||
|
|
||||||
|
var targetMock = preferOnlineFetch ? apiMetadataSourceMock : localCachedMetadataSourceMock;
|
||||||
|
targetMock.Setup(src => src.Available).Returns(true);
|
||||||
|
targetMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out lookupResult))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var beatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
MD5Hash = @"deadbeef"
|
||||||
|
};
|
||||||
|
var beatmapSet = new BeatmapSetInfo(beatmap.Yield());
|
||||||
|
beatmap.BeatmapSet = beatmapSet;
|
||||||
|
|
||||||
|
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
||||||
|
|
||||||
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
|
Assert.That(beatmap.OnlineID, Is.EqualTo(654321));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMetadataLookupForBeatmapWithoutPopulatedIDAndIncorrectHash([Values] bool preferOnlineFetch)
|
||||||
|
{
|
||||||
|
var lookupResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 654321,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
MD5Hash = @"cafebabe",
|
||||||
|
};
|
||||||
|
|
||||||
|
var targetMock = preferOnlineFetch ? apiMetadataSourceMock : localCachedMetadataSourceMock;
|
||||||
|
targetMock.Setup(src => src.Available).Returns(true);
|
||||||
|
targetMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out lookupResult))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var beatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
MD5Hash = @"deadbeef"
|
||||||
|
};
|
||||||
|
var beatmapSet = new BeatmapSetInfo(beatmap.Yield());
|
||||||
|
beatmap.BeatmapSet = beatmapSet;
|
||||||
|
|
||||||
|
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
||||||
|
|
||||||
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
Assert.That(beatmap.OnlineID, Is.EqualTo(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestReturnedMetadataHasDifferentHash([Values] bool preferOnlineFetch)
|
||||||
|
{
|
||||||
|
var lookupResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 654321,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
MD5Hash = @"deadbeef"
|
||||||
|
};
|
||||||
|
|
||||||
|
var targetMock = preferOnlineFetch ? apiMetadataSourceMock : localCachedMetadataSourceMock;
|
||||||
|
targetMock.Setup(src => src.Available).Returns(true);
|
||||||
|
targetMock.Setup(src => src.TryLookup(It.IsAny<BeatmapInfo>(), out lookupResult))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var beatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
OnlineID = 654321,
|
||||||
|
MD5Hash = @"cafebabe",
|
||||||
|
};
|
||||||
|
var beatmapSet = new BeatmapSetInfo(beatmap.Yield());
|
||||||
|
beatmap.BeatmapSet = beatmapSet;
|
||||||
|
|
||||||
|
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
||||||
|
|
||||||
|
Assert.That(beatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
Assert.That(beatmap.OnlineID, Is.EqualTo(654321));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPartiallyModifiedSet([Values] bool preferOnlineFetch)
|
||||||
|
{
|
||||||
|
var firstResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 654321,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
MD5Hash = @"cafebabe"
|
||||||
|
};
|
||||||
|
var secondResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 666666,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
MD5Hash = @"dededede"
|
||||||
|
};
|
||||||
|
|
||||||
|
var targetMock = preferOnlineFetch ? apiMetadataSourceMock : localCachedMetadataSourceMock;
|
||||||
|
targetMock.Setup(src => src.Available).Returns(true);
|
||||||
|
targetMock.Setup(src => src.TryLookup(It.Is<BeatmapInfo>(bi => bi.OnlineID == 654321), out firstResult))
|
||||||
|
.Returns(true);
|
||||||
|
targetMock.Setup(src => src.TryLookup(It.Is<BeatmapInfo>(bi => bi.OnlineID == 666666), out secondResult))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var firstBeatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
OnlineID = 654321,
|
||||||
|
MD5Hash = @"cafebabe",
|
||||||
|
};
|
||||||
|
var secondBeatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
OnlineID = 666666,
|
||||||
|
MD5Hash = @"deadbeef"
|
||||||
|
};
|
||||||
|
var beatmapSet = new BeatmapSetInfo(new[]
|
||||||
|
{
|
||||||
|
firstBeatmap,
|
||||||
|
secondBeatmap
|
||||||
|
});
|
||||||
|
firstBeatmap.BeatmapSet = beatmapSet;
|
||||||
|
secondBeatmap.BeatmapSet = beatmapSet;
|
||||||
|
|
||||||
|
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
||||||
|
|
||||||
|
Assert.That(firstBeatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
|
Assert.That(firstBeatmap.OnlineID, Is.EqualTo(654321));
|
||||||
|
|
||||||
|
Assert.That(secondBeatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
Assert.That(secondBeatmap.OnlineID, Is.EqualTo(666666));
|
||||||
|
|
||||||
|
Assert.That(beatmapSet.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPartiallyMaliciousSet([Values] bool preferOnlineFetch)
|
||||||
|
{
|
||||||
|
var firstResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapID = 654321,
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
MD5Hash = @"cafebabe"
|
||||||
|
};
|
||||||
|
var secondResult = new OnlineBeatmapMetadata
|
||||||
|
{
|
||||||
|
BeatmapStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
BeatmapSetStatus = BeatmapOnlineStatus.Ranked,
|
||||||
|
MD5Hash = @"dededede"
|
||||||
|
};
|
||||||
|
|
||||||
|
var targetMock = preferOnlineFetch ? apiMetadataSourceMock : localCachedMetadataSourceMock;
|
||||||
|
targetMock.Setup(src => src.Available).Returns(true);
|
||||||
|
targetMock.Setup(src => src.TryLookup(It.Is<BeatmapInfo>(bi => bi.OnlineID == 654321), out firstResult))
|
||||||
|
.Returns(true);
|
||||||
|
targetMock.Setup(src => src.TryLookup(It.Is<BeatmapInfo>(bi => bi.OnlineID == 666666), out secondResult))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var firstBeatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
OnlineID = 654321,
|
||||||
|
MD5Hash = @"cafebabe",
|
||||||
|
};
|
||||||
|
var secondBeatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
OnlineID = 666666,
|
||||||
|
MD5Hash = @"deadbeef"
|
||||||
|
};
|
||||||
|
var beatmapSet = new BeatmapSetInfo(new[]
|
||||||
|
{
|
||||||
|
firstBeatmap,
|
||||||
|
secondBeatmap
|
||||||
|
});
|
||||||
|
firstBeatmap.BeatmapSet = beatmapSet;
|
||||||
|
secondBeatmap.BeatmapSet = beatmapSet;
|
||||||
|
|
||||||
|
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
||||||
|
|
||||||
|
Assert.That(firstBeatmap.Status, Is.EqualTo(BeatmapOnlineStatus.Ranked));
|
||||||
|
Assert.That(firstBeatmap.OnlineID, Is.EqualTo(654321));
|
||||||
|
|
||||||
|
Assert.That(secondBeatmap.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
Assert.That(secondBeatmap.OnlineID, Is.EqualTo(-1));
|
||||||
|
|
||||||
|
Assert.That(beatmapSet.Status, Is.EqualTo(BeatmapOnlineStatus.None));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
var hitObject = new HitObject { StartTime = Time.Current };
|
var hitObject = new HitObject { StartTime = Time.Current };
|
||||||
lifetimeEntry = new HitObjectLifetimeEntry(hitObject)
|
lifetimeEntry = new HitObjectLifetimeEntry(hitObject)
|
||||||
{
|
{
|
||||||
Result = new JudgementResult(hitObject, hitObject.CreateJudgement())
|
Result = new JudgementResult(hitObject, hitObject.Judgement)
|
||||||
{
|
{
|
||||||
Type = HitResult.Great
|
Type = HitResult.Great
|
||||||
}
|
}
|
||||||
|
@ -129,10 +129,10 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
var scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
var scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
||||||
scoreProcessor.ApplyBeatmap(beatmap);
|
scoreProcessor.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], beatmap.HitObjects[0].CreateJudgement()) { Type = HitResult.Ok });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], beatmap.HitObjects[0].Judgement) { Type = HitResult.Ok });
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], beatmap.HitObjects[1].CreateJudgement()) { Type = HitResult.LargeTickHit });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], beatmap.HitObjects[1].Judgement) { Type = HitResult.LargeTickHit });
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[2], beatmap.HitObjects[2].CreateJudgement()) { Type = HitResult.SmallTickMiss });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[2], beatmap.HitObjects[2].Judgement) { Type = HitResult.SmallTickMiss });
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[3], beatmap.HitObjects[3].CreateJudgement()) { Type = HitResult.SmallBonus });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[3], beatmap.HitObjects[3].Judgement) { Type = HitResult.SmallBonus });
|
||||||
|
|
||||||
var score = new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo };
|
var score = new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo };
|
||||||
scoreProcessor.FailScore(score);
|
scoreProcessor.FailScore(score);
|
||||||
@ -169,8 +169,8 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
Assert.That(scoreProcessor.MinimumAccuracy.Value, Is.EqualTo(0));
|
Assert.That(scoreProcessor.MinimumAccuracy.Value, Is.EqualTo(0));
|
||||||
Assert.That(scoreProcessor.MaximumAccuracy.Value, Is.EqualTo(1));
|
Assert.That(scoreProcessor.MaximumAccuracy.Value, Is.EqualTo(1));
|
||||||
|
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], beatmap.HitObjects[0].CreateJudgement()) { Type = HitResult.Ok });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], beatmap.HitObjects[0].Judgement) { Type = HitResult.Ok });
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], beatmap.HitObjects[1].CreateJudgement()) { Type = HitResult.Great });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], beatmap.HitObjects[1].Judgement) { Type = HitResult.Great });
|
||||||
|
|
||||||
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo((double)(100 + 300) / (2 * 300)).Within(Precision.DOUBLE_EPSILON));
|
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo((double)(100 + 300) / (2 * 300)).Within(Precision.DOUBLE_EPSILON));
|
||||||
Assert.That(scoreProcessor.MinimumAccuracy.Value, Is.EqualTo((double)(100 + 300) / (4 * 300)).Within(Precision.DOUBLE_EPSILON));
|
Assert.That(scoreProcessor.MinimumAccuracy.Value, Is.EqualTo((double)(100 + 300) / (4 * 300)).Within(Precision.DOUBLE_EPSILON));
|
||||||
|
@ -274,10 +274,12 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
|||||||
Assert.IsTrue(filterCriteria.OnlineStatus.IsUpperInclusive);
|
Assert.IsTrue(filterCriteria.OnlineStatus.IsUpperInclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[TestCase("creator")]
|
||||||
public void TestApplyCreatorQueries()
|
[TestCase("author")]
|
||||||
|
[TestCase("mapper")]
|
||||||
|
public void TestApplyCreatorQueries(string keyword)
|
||||||
{
|
{
|
||||||
const string query = "beatmap specifically by creator=my_fav";
|
string query = $"beatmap specifically by {keyword}=my_fav";
|
||||||
var filterCriteria = new FilterCriteria();
|
var filterCriteria = new FilterCriteria();
|
||||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||||
Assert.AreEqual("beatmap specifically by", filterCriteria.SearchText.Trim());
|
Assert.AreEqual("beatmap specifically by", filterCriteria.SearchText.Trim());
|
||||||
@ -452,5 +454,111 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly object[] correct_date_query_examples =
|
||||||
|
{
|
||||||
|
new object[] { "600" },
|
||||||
|
new object[] { "0.5s" },
|
||||||
|
new object[] { "120m" },
|
||||||
|
new object[] { "48h120s" },
|
||||||
|
new object[] { "10y24M" },
|
||||||
|
new object[] { "10y60d120s" },
|
||||||
|
new object[] { "0y0M2d" },
|
||||||
|
new object[] { "1y1M2d" }
|
||||||
|
};
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCaseSource(nameof(correct_date_query_examples))]
|
||||||
|
public void TestValidDateQueries(string dateQuery)
|
||||||
|
{
|
||||||
|
string query = $"played<{dateQuery} time";
|
||||||
|
var filterCriteria = new FilterCriteria();
|
||||||
|
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||||
|
Assert.AreEqual(true, filterCriteria.LastPlayed.HasFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly object[] incorrect_date_query_examples =
|
||||||
|
{
|
||||||
|
new object[] { ".5s" },
|
||||||
|
new object[] { "7m27" },
|
||||||
|
new object[] { "7m7m7m" },
|
||||||
|
new object[] { "5s6m" },
|
||||||
|
new object[] { "7d7y" },
|
||||||
|
new object[] { "0:3:6" },
|
||||||
|
new object[] { "0:3:" },
|
||||||
|
new object[] { "\"three days\"" },
|
||||||
|
new object[] { "0.1y0.1M2d" },
|
||||||
|
new object[] { "0.99y0.99M2d" },
|
||||||
|
new object[] { string.Empty }
|
||||||
|
};
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCaseSource(nameof(incorrect_date_query_examples))]
|
||||||
|
public void TestInvalidDateQueries(string dateQuery)
|
||||||
|
{
|
||||||
|
string query = $"played<{dateQuery} time";
|
||||||
|
var filterCriteria = new FilterCriteria();
|
||||||
|
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||||
|
Assert.AreEqual(false, filterCriteria.LastPlayed.HasFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestGreaterDateQuery()
|
||||||
|
{
|
||||||
|
const string query = "played>50";
|
||||||
|
var filterCriteria = new FilterCriteria();
|
||||||
|
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Max, Is.Not.Null);
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Min, Is.Null);
|
||||||
|
// the parser internally references `DateTimeOffset.Now`, so to not make things too annoying for tests, just assume some tolerance
|
||||||
|
// (irrelevant in proportion to the actual filter proscribed).
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Max, Is.EqualTo(DateTimeOffset.Now.AddDays(-50)).Within(TimeSpan.FromSeconds(5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLowerDateQuery()
|
||||||
|
{
|
||||||
|
const string query = "played<50";
|
||||||
|
var filterCriteria = new FilterCriteria();
|
||||||
|
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Max, Is.Null);
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Min, Is.Not.Null);
|
||||||
|
// the parser internally references `DateTimeOffset.Now`, so to not make things too annoying for tests, just assume some tolerance
|
||||||
|
// (irrelevant in proportion to the actual filter proscribed).
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Min, Is.EqualTo(DateTimeOffset.Now.AddDays(-50)).Within(TimeSpan.FromSeconds(5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBothSidesDateQuery()
|
||||||
|
{
|
||||||
|
const string query = "played>3M played<1y6M";
|
||||||
|
var filterCriteria = new FilterCriteria();
|
||||||
|
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Min, Is.Not.Null);
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Max, Is.Not.Null);
|
||||||
|
// the parser internally references `DateTimeOffset.Now`, so to not make things too annoying for tests, just assume some tolerance
|
||||||
|
// (irrelevant in proportion to the actual filter proscribed).
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Min, Is.EqualTo(DateTimeOffset.Now.AddYears(-1).AddMonths(-6)).Within(TimeSpan.FromSeconds(5)));
|
||||||
|
Assert.That(filterCriteria.LastPlayed.Max, Is.EqualTo(DateTimeOffset.Now.AddMonths(-3)).Within(TimeSpan.FromSeconds(5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEqualDateQuery()
|
||||||
|
{
|
||||||
|
const string query = "played=50";
|
||||||
|
var filterCriteria = new FilterCriteria();
|
||||||
|
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||||
|
Assert.AreEqual(false, filterCriteria.LastPlayed.HasFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOutOfRangeDateQuery()
|
||||||
|
{
|
||||||
|
const string query = "played<10000y";
|
||||||
|
var filterCriteria = new FilterCriteria();
|
||||||
|
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||||
|
Assert.AreEqual(true, filterCriteria.LastPlayed.HasFilter);
|
||||||
|
Assert.AreEqual(DateTimeOffset.MinValue.AddMilliseconds(1), filterCriteria.LastPlayed.Min);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
|||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
var judgementResult = new JudgementResult(fourObjectBeatmap.HitObjects[i], fourObjectBeatmap.HitObjects[i].CreateJudgement())
|
var judgementResult = new JudgementResult(fourObjectBeatmap.HitObjects[i], fourObjectBeatmap.HitObjects[i].Judgement)
|
||||||
{
|
{
|
||||||
Type = i == 2 ? minResult : hitResult
|
Type = i == 2 ? minResult : hitResult
|
||||||
};
|
};
|
||||||
@ -141,7 +141,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
|||||||
|
|
||||||
for (int i = 0; i < object_count; ++i)
|
for (int i = 0; i < object_count; ++i)
|
||||||
{
|
{
|
||||||
var judgementResult = new JudgementResult(largeBeatmap.HitObjects[i], largeBeatmap.HitObjects[i].CreateJudgement())
|
var judgementResult = new JudgementResult(largeBeatmap.HitObjects[i], largeBeatmap.HitObjects[i].Judgement)
|
||||||
{
|
{
|
||||||
Type = HitResult.Great
|
Type = HitResult.Great
|
||||||
};
|
};
|
||||||
@ -325,11 +325,11 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
|||||||
scoreProcessor = new TestScoreProcessor();
|
scoreProcessor = new TestScoreProcessor();
|
||||||
scoreProcessor.ApplyBeatmap(beatmap);
|
scoreProcessor.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], beatmap.HitObjects[0].CreateJudgement()) { Type = HitResult.Great });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], beatmap.HitObjects[0].Judgement) { Type = HitResult.Great });
|
||||||
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(1));
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(1));
|
||||||
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo(1));
|
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo(1));
|
||||||
|
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], beatmap.HitObjects[1].CreateJudgement()) { Type = HitResult.ComboBreak });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], beatmap.HitObjects[1].Judgement) { Type = HitResult.ComboBreak });
|
||||||
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(0));
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(0));
|
||||||
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo(1));
|
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo(1));
|
||||||
}
|
}
|
||||||
@ -350,7 +350,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
|||||||
|
|
||||||
for (int i = 0; i < beatmap.HitObjects.Count; i++)
|
for (int i = 0; i < beatmap.HitObjects.Count; i++)
|
||||||
{
|
{
|
||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[i], beatmap.HitObjects[i].CreateJudgement())
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[i], beatmap.HitObjects[i].Judgement)
|
||||||
{
|
{
|
||||||
Type = i == 0 ? HitResult.Miss : HitResult.Great
|
Type = i == 0 ? HitResult.Miss : HitResult.Great
|
||||||
});
|
});
|
||||||
@ -441,10 +441,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
|||||||
private readonly HitResult maxResult;
|
private readonly HitResult maxResult;
|
||||||
private readonly HitResult? minResult;
|
private readonly HitResult? minResult;
|
||||||
|
|
||||||
public override Judgement CreateJudgement()
|
public override Judgement CreateJudgement() => new TestJudgement(maxResult, minResult);
|
||||||
{
|
|
||||||
return new TestJudgement(maxResult, minResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestHitObject(HitResult maxResult, HitResult? minResult = null)
|
public TestHitObject(HitResult maxResult, HitResult? minResult = null)
|
||||||
{
|
{
|
||||||
|
@ -43,13 +43,13 @@ namespace osu.Game.Tests.Rulesets
|
|||||||
|
|
||||||
AddStep("setup provider", () =>
|
AddStep("setup provider", () =>
|
||||||
{
|
{
|
||||||
var rulesetSkinProvider = new RulesetSkinProvidingContainer(Ruleset.Value.CreateInstance(), Beatmap.Value.Beatmap, Beatmap.Value.Skin);
|
requester = new SkinRequester();
|
||||||
|
|
||||||
rulesetSkinProvider.Add(requester = new SkinRequester());
|
|
||||||
|
|
||||||
requester.OnLoadAsync += () => textureOnLoad = requester.GetTexture("test-image");
|
requester.OnLoadAsync += () => textureOnLoad = requester.GetTexture("test-image");
|
||||||
|
|
||||||
Child = rulesetSkinProvider;
|
Child = new RulesetSkinProvidingContainer(Ruleset.Value.CreateInstance(), Beatmap.Value.Beatmap, Beatmap.Value.Skin)
|
||||||
|
{
|
||||||
|
Child = requester
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("requester got correct initial texture", () => textureOnLoad != null);
|
AddAssert("requester got correct initial texture", () => textureOnLoad != null);
|
||||||
|
60
osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultyIcon.cs
Normal file
60
osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultyIcon.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Beatmaps
|
||||||
|
{
|
||||||
|
public partial class TestSceneDifficultyIcon : OsuTestScene
|
||||||
|
{
|
||||||
|
private FillFlowContainer<DifficultyIcon> fill = null!;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Child = fill = new FillFlowContainer<DifficultyIcon>
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Width = 300,
|
||||||
|
Direction = FillDirection.Full,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CreateDifficultyIcon()
|
||||||
|
{
|
||||||
|
AddRepeatStep("create difficulty icon", () =>
|
||||||
|
{
|
||||||
|
var rulesetInfo = new OsuRuleset().RulesetInfo;
|
||||||
|
var beatmapInfo = new TestBeatmap(rulesetInfo).BeatmapInfo;
|
||||||
|
|
||||||
|
beatmapInfo.Difficulty.ApproachRate = RNG.Next(0, 10);
|
||||||
|
beatmapInfo.Difficulty.CircleSize = RNG.Next(0, 10);
|
||||||
|
beatmapInfo.Difficulty.OverallDifficulty = RNG.Next(0, 10);
|
||||||
|
beatmapInfo.Difficulty.DrainRate = RNG.Next(0, 10);
|
||||||
|
beatmapInfo.StarRating = RNG.NextSingle(0, 10);
|
||||||
|
beatmapInfo.BPM = RNG.Next(60, 300);
|
||||||
|
|
||||||
|
fill.Add(new DifficultyIcon(beatmapInfo, rulesetInfo)
|
||||||
|
{
|
||||||
|
Scale = new Vector2(2),
|
||||||
|
});
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
AddStep("no tooltip", () => fill.ForEach(icon => icon.TooltipType = DifficultyIconTooltipType.None));
|
||||||
|
AddStep("basic tooltip", () => fill.ForEach(icon => icon.TooltipType = DifficultyIconTooltipType.StarRating));
|
||||||
|
AddStep("extended tooltip", () => fill.ForEach(icon => icon.TooltipType = DifficultyIconTooltipType.Extended));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -264,15 +264,23 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestMutedNotificationMasterVolume()
|
public void TestMutedNotificationLowMusicVolume()
|
||||||
{
|
{
|
||||||
addVolumeSteps("master volume", () => audioManager.Volume.Value = 0, () => audioManager.Volume.Value == 0.5);
|
addVolumeSteps("master and music volumes", () =>
|
||||||
|
{
|
||||||
|
audioManager.Volume.Value = 0.6;
|
||||||
|
audioManager.VolumeTrack.Value = 0.01;
|
||||||
|
}, () => Precision.AlmostEquals(audioManager.Volume.Value, 0.6) && Precision.AlmostEquals(audioManager.VolumeTrack.Value, 0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestMutedNotificationTrackVolume()
|
public void TestMutedNotificationLowMasterVolume()
|
||||||
{
|
{
|
||||||
addVolumeSteps("music volume", () => audioManager.VolumeTrack.Value = 0, () => audioManager.VolumeTrack.Value == 0.5);
|
addVolumeSteps("master and music volumes", () =>
|
||||||
|
{
|
||||||
|
audioManager.Volume.Value = 0.01;
|
||||||
|
audioManager.VolumeTrack.Value = 0.6;
|
||||||
|
}, () => Precision.AlmostEquals(audioManager.Volume.Value, 0.5) && Precision.AlmostEquals(audioManager.VolumeTrack.Value, 0.6));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -281,9 +289,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
addVolumeSteps("mute button", () =>
|
addVolumeSteps("mute button", () =>
|
||||||
{
|
{
|
||||||
// Importantly, in the case the volume is muted but the user has a volume level set, it should be retained.
|
// Importantly, in the case the volume is muted but the user has a volume level set, it should be retained.
|
||||||
audioManager.VolumeTrack.Value = 0.5f;
|
audioManager.Volume.Value = 0.5;
|
||||||
|
audioManager.VolumeTrack.Value = 0.5;
|
||||||
volumeOverlay.IsMuted.Value = true;
|
volumeOverlay.IsMuted.Value = true;
|
||||||
}, () => !volumeOverlay.IsMuted.Value && audioManager.VolumeTrack.Value == 0.5f);
|
}, () => !volumeOverlay.IsMuted.Value && audioManager.Volume.Value == 0.5 && audioManager.VolumeTrack.Value == 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -14,39 +15,47 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public partial class TestSceneStarCounter : OsuTestScene
|
public partial class TestSceneStarCounter : OsuTestScene
|
||||||
{
|
{
|
||||||
private readonly StarCounter starCounter;
|
private readonly StarCounter starCounter;
|
||||||
|
private readonly StarCounter twentyStarCounter;
|
||||||
private readonly OsuSpriteText starsLabel;
|
private readonly OsuSpriteText starsLabel;
|
||||||
|
|
||||||
public TestSceneStarCounter()
|
public TestSceneStarCounter()
|
||||||
{
|
{
|
||||||
starCounter = new StarCounter
|
Add(new FillFlowContainer
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
AutoSizeAxes = Axes.Both,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
};
|
Origin = Anchor.Centre,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
Add(starCounter);
|
Spacing = new Vector2(20),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
starCounter = new StarCounter(),
|
||||||
|
twentyStarCounter = new StarCounter(20),
|
||||||
starsLabel = new OsuSpriteText
|
starsLabel = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Anchor = Anchor.TopCentre,
|
||||||
Anchor = Anchor.Centre,
|
Origin = Anchor.TopCentre,
|
||||||
Scale = new Vector2(2),
|
Scale = new Vector2(2),
|
||||||
Y = 50,
|
},
|
||||||
};
|
}
|
||||||
|
});
|
||||||
Add(starsLabel);
|
|
||||||
|
|
||||||
setStars(5);
|
setStars(5);
|
||||||
|
|
||||||
AddRepeatStep("random value", () => setStars(RNG.NextSingle() * (starCounter.StarCount + 1)), 10);
|
AddRepeatStep("random value", () => setStars(RNG.NextSingle() * (twentyStarCounter.StarCount + 1)), 10);
|
||||||
AddSliderStep("exact value", 0f, 10f, 5f, setStars);
|
AddSliderStep("exact value", 0f, 20f, 5f, setStars);
|
||||||
AddStep("stop animation", () => starCounter.StopAnimation());
|
AddStep("stop animation", () =>
|
||||||
|
{
|
||||||
|
starCounter.StopAnimation();
|
||||||
|
twentyStarCounter.StopAnimation();
|
||||||
|
});
|
||||||
AddStep("reset", () => setStars(0));
|
AddStep("reset", () => setStars(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStars(float stars)
|
private void setStars(float stars)
|
||||||
{
|
{
|
||||||
starCounter.Current = stars;
|
starCounter.Current = stars;
|
||||||
|
twentyStarCounter.Current = stars;
|
||||||
starsLabel.Text = starCounter.Current.ToString("0.00");
|
starsLabel.Text = starCounter.Current.ToString("0.00");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,13 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Login;
|
using osu.Game.Overlays.Login;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Users.Drawables;
|
using osu.Game.Users.Drawables;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
@ -25,6 +27,9 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
|
|
||||||
private LoginOverlay loginOverlay = null!;
|
private LoginOverlay loginOverlay = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager configManager { get; set; } = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -156,5 +161,36 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
});
|
});
|
||||||
AddAssert("login overlay is hidden", () => loginOverlay.State.Value == Visibility.Hidden);
|
AddAssert("login overlay is hidden", () => loginOverlay.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUncheckingRememberUsernameClearsIt()
|
||||||
|
{
|
||||||
|
AddStep("logout", () => API.Logout());
|
||||||
|
AddStep("set username", () => configManager.SetValue(OsuSetting.Username, "test_user"));
|
||||||
|
AddStep("set remember password", () => configManager.SetValue(OsuSetting.SavePassword, true));
|
||||||
|
AddStep("uncheck remember username", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(loginOverlay.ChildrenOfType<SettingsCheckbox>().First());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("remember username off", () => configManager.Get<bool>(OsuSetting.SaveUsername), () => Is.False);
|
||||||
|
AddAssert("remember password off", () => configManager.Get<bool>(OsuSetting.SavePassword), () => Is.False);
|
||||||
|
AddAssert("username cleared", () => configManager.Get<string>(OsuSetting.Username), () => Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUncheckingRememberPasswordClearsToken()
|
||||||
|
{
|
||||||
|
AddStep("logout", () => API.Logout());
|
||||||
|
AddStep("set token", () => configManager.SetValue(OsuSetting.Token, "test_token"));
|
||||||
|
AddStep("set remember password", () => configManager.SetValue(OsuSetting.SavePassword, true));
|
||||||
|
AddStep("uncheck remember token", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(loginOverlay.ChildrenOfType<SettingsCheckbox>().Last());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("remember password off", () => configManager.Get<bool>(OsuSetting.SavePassword), () => Is.False);
|
||||||
|
AddAssert("token cleared", () => configManager.Get<string>(OsuSetting.Token), () => Is.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,17 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.Solo;
|
||||||
using osu.Game.Overlays.Toolbar;
|
using osu.Game.Overlays.Toolbar;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -87,5 +92,91 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep($"Change state to {state}", () => dummyAPI.SetState(state));
|
AddStep($"Change state to {state}", () => dummyAPI.SetState(state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTransientUserStatisticsDisplay()
|
||||||
|
{
|
||||||
|
AddStep("Log in", () => dummyAPI.Login("wang", "jang"));
|
||||||
|
AddStep("Gain", () =>
|
||||||
|
{
|
||||||
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
|
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||||
|
new ScoreInfo(),
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = 123_456,
|
||||||
|
PP = 1234
|
||||||
|
},
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = 111_111,
|
||||||
|
PP = 1357
|
||||||
|
});
|
||||||
|
});
|
||||||
|
AddStep("Loss", () =>
|
||||||
|
{
|
||||||
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
|
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||||
|
new ScoreInfo(),
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = 111_111,
|
||||||
|
PP = 1357
|
||||||
|
},
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = 123_456,
|
||||||
|
PP = 1234
|
||||||
|
});
|
||||||
|
});
|
||||||
|
AddStep("No change", () =>
|
||||||
|
{
|
||||||
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
|
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||||
|
new ScoreInfo(),
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = 111_111,
|
||||||
|
PP = 1357
|
||||||
|
},
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = 111_111,
|
||||||
|
PP = 1357
|
||||||
|
});
|
||||||
|
});
|
||||||
|
AddStep("Was null", () =>
|
||||||
|
{
|
||||||
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
|
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||||
|
new ScoreInfo(),
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = null,
|
||||||
|
PP = null
|
||||||
|
},
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = 111_111,
|
||||||
|
PP = 1357
|
||||||
|
});
|
||||||
|
});
|
||||||
|
AddStep("Became null", () =>
|
||||||
|
{
|
||||||
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
|
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||||
|
new ScoreInfo(),
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = 111_111,
|
||||||
|
PP = 1357
|
||||||
|
},
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = null,
|
||||||
|
PP = null
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -18,6 +19,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Collections;
|
using osu.Game.Collections;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -221,6 +223,67 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
|
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAttemptPlayBeatmapWrongHashFails()
|
||||||
|
{
|
||||||
|
Screens.Select.SongSelect songSelect = null;
|
||||||
|
|
||||||
|
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely());
|
||||||
|
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
|
||||||
|
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
|
||||||
|
|
||||||
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
|
||||||
|
AddStep("change beatmap files", () =>
|
||||||
|
{
|
||||||
|
foreach (var file in Game.Beatmap.Value.BeatmapSetInfo.Files.Where(f => Path.GetExtension(f.Filename) == ".osu"))
|
||||||
|
{
|
||||||
|
using (var stream = Game.Storage.GetStream(Path.Combine("files", file.File.GetStoragePath()), FileAccess.ReadWrite))
|
||||||
|
stream.WriteByte(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("invalidate cache", () =>
|
||||||
|
{
|
||||||
|
((IWorkingBeatmapCache)Game.BeatmapManager).Invalidate(Game.Beatmap.Value.BeatmapSetInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select next difficulty", () => InputManager.Key(Key.Down));
|
||||||
|
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||||
|
|
||||||
|
AddUntilStep("wait for player loader", () => Game.ScreenStack.CurrentScreen is PlayerLoader);
|
||||||
|
AddUntilStep("wait for song select", () => songSelect.IsCurrentScreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAttemptPlayBeatmapMissingFails()
|
||||||
|
{
|
||||||
|
Screens.Select.SongSelect songSelect = null;
|
||||||
|
|
||||||
|
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely());
|
||||||
|
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
|
||||||
|
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
|
||||||
|
|
||||||
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
|
||||||
|
AddStep("delete beatmap files", () =>
|
||||||
|
{
|
||||||
|
foreach (var file in Game.Beatmap.Value.BeatmapSetInfo.Files.Where(f => Path.GetExtension(f.Filename) == ".osu"))
|
||||||
|
Game.Storage.Delete(Path.Combine("files", file.File.GetStoragePath()));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("invalidate cache", () =>
|
||||||
|
{
|
||||||
|
((IWorkingBeatmapCache)Game.BeatmapManager).Invalidate(Game.Beatmap.Value.BeatmapSetInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select next difficulty", () => InputManager.Key(Key.Down));
|
||||||
|
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||||
|
|
||||||
|
AddUntilStep("wait for player loader", () => Game.ScreenStack.CurrentScreen is PlayerLoader);
|
||||||
|
AddUntilStep("wait for song select", () => songSelect.IsCurrentScreen());
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRetryCountIncrements()
|
public void TestRetryCountIncrements()
|
||||||
{
|
{
|
||||||
@ -986,6 +1049,29 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPresentBeatmapAfterDeletion()
|
||||||
|
{
|
||||||
|
BeatmapSetInfo beatmap = null;
|
||||||
|
|
||||||
|
Screens.Select.SongSelect songSelect = null;
|
||||||
|
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
|
||||||
|
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
|
||||||
|
|
||||||
|
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
|
||||||
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
|
||||||
|
AddStep("delete selected beatmap", () =>
|
||||||
|
{
|
||||||
|
beatmap = Game.Beatmap.Value.BeatmapSetInfo;
|
||||||
|
Game.BeatmapManager.Delete(Game.Beatmap.Value.BeatmapSetInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("nothing selected", () => Game.Beatmap.IsDefault);
|
||||||
|
AddStep("present deleted beatmap", () => Game.PresentBeatmap(beatmap));
|
||||||
|
AddAssert("still nothing selected", () => Game.Beatmap.IsDefault);
|
||||||
|
}
|
||||||
|
|
||||||
private Func<Player> playToResults()
|
private Func<Player> playToResults()
|
||||||
{
|
{
|
||||||
var player = playToCompletion();
|
var player = playToCompletion();
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -301,6 +302,25 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
switchToGameplayScene();
|
switchToGameplayScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRulesetInputDisabledWhenSkinEditorOpen()
|
||||||
|
{
|
||||||
|
advanceToSongSelect();
|
||||||
|
openSkinEditor();
|
||||||
|
|
||||||
|
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
|
||||||
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
|
||||||
|
switchToGameplayScene();
|
||||||
|
AddUntilStep("nested input disabled", () => ((Player)Game.ScreenStack.CurrentScreen).ChildrenOfType<PassThroughInputManager>().All(manager => !manager.UseParentInput));
|
||||||
|
|
||||||
|
toggleSkinEditor();
|
||||||
|
AddUntilStep("nested input enabled", () => ((Player)Game.ScreenStack.CurrentScreen).ChildrenOfType<PassThroughInputManager>().Any(manager => manager.UseParentInput));
|
||||||
|
|
||||||
|
toggleSkinEditor();
|
||||||
|
AddUntilStep("nested input disabled", () => ((Player)Game.ScreenStack.CurrentScreen).ChildrenOfType<PassThroughInputManager>().All(manager => !manager.UseParentInput));
|
||||||
|
}
|
||||||
|
|
||||||
private void advanceToSongSelect()
|
private void advanceToSongSelect()
|
||||||
{
|
{
|
||||||
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
|
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
|
||||||
|
@ -170,6 +170,24 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPostAsOwner()
|
||||||
|
{
|
||||||
|
setUpCommentsResponse(getExampleComments());
|
||||||
|
AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
|
||||||
|
|
||||||
|
setUpPostResponse(true);
|
||||||
|
AddStep("enter text", () => editorTextBox.Current.Value = "comm");
|
||||||
|
AddStep("submit", () => commentsContainer.ChildrenOfType<CommentEditor>().Single().ChildrenOfType<RoundedButton>().First().TriggerClick());
|
||||||
|
|
||||||
|
AddUntilStep("comment sent", () =>
|
||||||
|
{
|
||||||
|
string writtenText = editorTextBox.Current.Value;
|
||||||
|
var comment = commentsContainer.ChildrenOfType<DrawableComment>().LastOrDefault();
|
||||||
|
return comment != null && comment.ChildrenOfType<SpriteText>().Any(y => y.Text == writtenText) && comment.ChildrenOfType<SpriteText>().Any(y => y.Text == "MAPPER");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void setUpCommentsResponse(CommentBundle commentBundle)
|
private void setUpCommentsResponse(CommentBundle commentBundle)
|
||||||
=> AddStep("set up response", () =>
|
=> AddStep("set up response", () =>
|
||||||
{
|
{
|
||||||
@ -183,7 +201,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
private void setUpPostResponse()
|
private void setUpPostResponse(bool asOwner = false)
|
||||||
=> AddStep("set up response", () =>
|
=> AddStep("set up response", () =>
|
||||||
{
|
{
|
||||||
dummyAPI.HandleRequest = request =>
|
dummyAPI.HandleRequest = request =>
|
||||||
@ -191,7 +209,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
if (!(request is CommentPostRequest req))
|
if (!(request is CommentPostRequest req))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
req.TriggerSuccess(new CommentBundle
|
var bundle = new CommentBundle
|
||||||
{
|
{
|
||||||
Comments = new List<Comment>
|
Comments = new List<Comment>
|
||||||
{
|
{
|
||||||
@ -202,9 +220,26 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
LegacyName = "FirstUser",
|
LegacyName = "FirstUser",
|
||||||
CreatedAt = DateTimeOffset.Now,
|
CreatedAt = DateTimeOffset.Now,
|
||||||
VotesCount = 98,
|
VotesCount = 98,
|
||||||
|
CommentableId = 2001,
|
||||||
|
CommentableType = "test",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (asOwner)
|
||||||
|
{
|
||||||
|
bundle.Comments[0].UserId = 1001;
|
||||||
|
bundle.Comments[0].User = new APIUser { Id = 1001, Username = "FirstUser" };
|
||||||
|
bundle.CommentableMeta.Add(new CommentableMeta
|
||||||
|
{
|
||||||
|
Id = 2001,
|
||||||
|
OwnerId = 1001,
|
||||||
|
OwnerTitle = "MAPPER",
|
||||||
|
Type = "test",
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
req.TriggerSuccess(bundle);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -4,62 +4,66 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
|
||||||
using osu.Game.Overlays.Comments;
|
using osu.Game.Overlays.Comments;
|
||||||
|
using osu.Game.Tests.Visual.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
public partial class TestSceneDrawableComment : OsuTestScene
|
public partial class TestSceneDrawableComment : ThemeComparisonTestScene
|
||||||
{
|
{
|
||||||
[Cached]
|
public TestSceneDrawableComment()
|
||||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
: base(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private Container container;
|
protected override Drawable CreateContent() => new OsuScrollContainer(Direction.Vertical)
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void SetUp() => Schedule(() =>
|
|
||||||
{
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colourProvider.Background4,
|
Child = new FillFlowContainer
|
||||||
},
|
|
||||||
container = new Container
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
},
|
Direction = FillDirection.Vertical,
|
||||||
};
|
ChildrenEnumerable = comments.Select(info =>
|
||||||
});
|
|
||||||
|
|
||||||
[TestCaseSource(nameof(comments))]
|
|
||||||
public void TestComment(string description, string text)
|
|
||||||
{
|
{
|
||||||
AddStep(description, () =>
|
var comment = new Comment
|
||||||
{
|
|
||||||
comment.Pinned = description == "Pinned";
|
|
||||||
comment.Message = text;
|
|
||||||
container.Add(new DrawableComment(comment));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly Comment comment = new Comment
|
|
||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
LegacyName = "Test User",
|
UserId = 1000,
|
||||||
|
User = new APIUser { Id = 1000, Username = "Someone" },
|
||||||
CreatedAt = DateTimeOffset.Now,
|
CreatedAt = DateTimeOffset.Now,
|
||||||
VotesCount = 0,
|
VotesCount = 0,
|
||||||
|
Pinned = info[0] == "Pinned",
|
||||||
|
Message = info[1],
|
||||||
|
CommentableId = 2001,
|
||||||
|
CommentableType = "test"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static object[] comments =
|
return new[]
|
||||||
|
{
|
||||||
|
new DrawableComment(comment, Array.Empty<CommentableMeta>()),
|
||||||
|
new DrawableComment(comment, new[]
|
||||||
|
{
|
||||||
|
new CommentableMeta
|
||||||
|
{
|
||||||
|
Id = 2001,
|
||||||
|
OwnerId = comment.UserId,
|
||||||
|
OwnerTitle = "MAPPER",
|
||||||
|
Type = "test",
|
||||||
|
},
|
||||||
|
new CommentableMeta { Title = "Other Meta" },
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}).SelectMany(c => c)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly string[][] comments =
|
||||||
{
|
{
|
||||||
new[] { "Plain", "This is plain comment" },
|
new[] { "Plain", "This is plain comment" },
|
||||||
new[] { "Pinned", "This is pinned comment" },
|
new[] { "Pinned", "This is pinned comment" },
|
||||||
@ -77,16 +81,17 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Taken from https://github.com/ppy/osu/issues/13993#issuecomment-885994077
|
// Taken from https://github.com/ppy/osu/issues/13993#issuecomment-885994077
|
||||||
|
new[] { "Problematic", @"My tablet doesn't work :( It's a Huion 420 and it's apparently incompatible with OpenTablet Driver. The warning I get is: ""DeviceInUseException: Device is currently in use by another kernel module. To fix this issue, please follow the instructions from https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ#arg umentoutofrangeexception-value-0-15"" and it repeats 4 times on the notification before logging subsequent warnings. Checking the logs, it looks for other Huion tablets before sending the notification (e.g. ""2021-07-23 03:52:33 [verbose]: Detect: Searching for tablet 'Huion WH1409 V2' 20 2021-07-23 03:52:33 [error]: DeviceInUseException: Device is currently in use by another kernel module. To fix this issue, please follow the instructions from https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ#arg umentoutofrangeexception-value-0-15"") I use an Arch based installation of Linux and the tablet runs perfectly with Digimend kernel driver, with area configuration, pen pressure, etc. On osu!lazer the cursor disappears until I set it to ""Borderless"" instead of ""Fullscreen"" and even after it shows up, it goes to the bottom left corner as soon as a map starts. I have honestly 0 idea of whats going on at this point.", },
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
"Problematic", @"My tablet doesn't work :(
|
"Code Block", @"User not found! ;_;
|
||||||
It's a Huion 420 and it's apparently incompatible with OpenTablet Driver. The warning I get is: ""DeviceInUseException: Device is currently in use by another kernel module. To fix this issue, please follow the instructions from https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ#arg umentoutofrangeexception-value-0-15"" and it repeats 4 times on the notification before logging subsequent warnings.
|
|
||||||
Checking the logs, it looks for other Huion tablets before sending the notification (e.g.
|
There are a few possible reasons for this:
|
||||||
""2021-07-23 03:52:33 [verbose]: Detect: Searching for tablet 'Huion WH1409 V2'
|
|
||||||
20 2021-07-23 03:52:33 [error]: DeviceInUseException: Device is currently in use by another kernel module. To fix this issue, please follow the instructions from https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ#arg umentoutofrangeexception-value-0-15"")
|
They may have changed their username.
|
||||||
I use an Arch based installation of Linux and the tablet runs perfectly with Digimend kernel driver, with area configuration, pen pressure, etc. On osu!lazer the cursor disappears until I set it to ""Borderless"" instead of ""Fullscreen"" and even after it shows up, it goes to the bottom left corner as soon as a map starts.
|
The account may be temporarily unavailable due to security or abuse issues.
|
||||||
I have honestly 0 idea of whats going on at this point."
|
You may have made a typo!"
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +154,19 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUnrankedPP()
|
||||||
|
{
|
||||||
|
AddStep("Load scores with unranked PP", () =>
|
||||||
|
{
|
||||||
|
var allScores = createScores();
|
||||||
|
allScores.Scores[0].Ranked = false;
|
||||||
|
allScores.UserScore = createUserBest();
|
||||||
|
allScores.UserScore.Score.Ranked = false;
|
||||||
|
scoresContainer.Scores = allScores;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private ulong onlineID = 1;
|
private ulong onlineID = 1;
|
||||||
|
|
||||||
private APIScoresCollection createScores()
|
private APIScoresCollection createScores()
|
||||||
@ -184,6 +197,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
MaxCombo = 1234,
|
MaxCombo = 1234,
|
||||||
TotalScore = 1234567890,
|
TotalScore = 1234567890,
|
||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
|
Ranked = true,
|
||||||
},
|
},
|
||||||
new SoloScoreInfo
|
new SoloScoreInfo
|
||||||
{
|
{
|
||||||
@ -206,6 +220,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
MaxCombo = 1234,
|
MaxCombo = 1234,
|
||||||
TotalScore = 1234789,
|
TotalScore = 1234789,
|
||||||
Accuracy = 0.9997,
|
Accuracy = 0.9997,
|
||||||
|
Ranked = true,
|
||||||
},
|
},
|
||||||
new SoloScoreInfo
|
new SoloScoreInfo
|
||||||
{
|
{
|
||||||
@ -227,6 +242,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
MaxCombo = 1234,
|
MaxCombo = 1234,
|
||||||
TotalScore = 12345678,
|
TotalScore = 12345678,
|
||||||
Accuracy = 0.9854,
|
Accuracy = 0.9854,
|
||||||
|
Ranked = true,
|
||||||
},
|
},
|
||||||
new SoloScoreInfo
|
new SoloScoreInfo
|
||||||
{
|
{
|
||||||
@ -247,6 +263,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
MaxCombo = 1234,
|
MaxCombo = 1234,
|
||||||
TotalScore = 1234567,
|
TotalScore = 1234567,
|
||||||
Accuracy = 0.8765,
|
Accuracy = 0.8765,
|
||||||
|
Ranked = true,
|
||||||
},
|
},
|
||||||
new SoloScoreInfo
|
new SoloScoreInfo
|
||||||
{
|
{
|
||||||
@ -263,6 +280,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
MaxCombo = 1234,
|
MaxCombo = 1234,
|
||||||
TotalScore = 123456,
|
TotalScore = 123456,
|
||||||
Accuracy = 0.6543,
|
Accuracy = 0.6543,
|
||||||
|
Ranked = true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -309,6 +327,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
MaxCombo = 1234,
|
MaxCombo = 1234,
|
||||||
TotalScore = 123456,
|
TotalScore = 123456,
|
||||||
Accuracy = 0.6543,
|
Accuracy = 0.6543,
|
||||||
|
Ranked = true,
|
||||||
},
|
},
|
||||||
Position = 1337,
|
Position = 1337,
|
||||||
};
|
};
|
||||||
|
@ -35,8 +35,6 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
private Action<GetUsersRequest>? handleGetUsersRequest;
|
private Action<GetUsersRequest>? handleGetUsersRequest;
|
||||||
private Action<GetUserRequest>? handleGetUserRequest;
|
private Action<GetUserRequest>? handleGetUserRequest;
|
||||||
|
|
||||||
private IDisposable? subscription;
|
|
||||||
|
|
||||||
private readonly Dictionary<(int userId, string rulesetName), UserStatistics> serverSideStatistics = new Dictionary<(int userId, string rulesetName), UserStatistics>();
|
private readonly Dictionary<(int userId, string rulesetName), UserStatistics> serverSideStatistics = new Dictionary<(int userId, string rulesetName), UserStatistics>();
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
@ -252,26 +250,6 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddAssert("values after are correct", () => update!.After.TotalScore, () => Is.EqualTo(6_000_000));
|
AddAssert("values after are correct", () => update!.After.TotalScore, () => Is.EqualTo(6_000_000));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestStatisticsUpdateNotFiredAfterSubscriptionDisposal()
|
|
||||||
{
|
|
||||||
int userId = getUserId();
|
|
||||||
setUpUser(userId);
|
|
||||||
|
|
||||||
long scoreId = getScoreId();
|
|
||||||
var ruleset = new OsuRuleset().RulesetInfo;
|
|
||||||
|
|
||||||
SoloStatisticsUpdate? update = null;
|
|
||||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
|
||||||
AddStep("unsubscribe", () => subscription!.Dispose());
|
|
||||||
|
|
||||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
|
||||||
|
|
||||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
|
||||||
AddWaitStep("wait a bit", 5);
|
|
||||||
AddAssert("update not received", () => update == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGlobalStatisticsUpdatedAfterRegistrationAddedAndScoreProcessed()
|
public void TestGlobalStatisticsUpdatedAfterRegistrationAddedAndScoreProcessed()
|
||||||
{
|
{
|
||||||
@ -312,13 +290,20 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<SoloStatisticsUpdate> onUpdateReady) =>
|
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<SoloStatisticsUpdate> onUpdateReady) =>
|
||||||
AddStep("register for updates", () => subscription = watcher.RegisterForStatisticsUpdateAfter(
|
AddStep("register for updates", () =>
|
||||||
|
{
|
||||||
|
watcher.RegisterForStatisticsUpdateAfter(
|
||||||
new ScoreInfo(Beatmap.Value.BeatmapInfo, new OsuRuleset().RulesetInfo, new RealmUser())
|
new ScoreInfo(Beatmap.Value.BeatmapInfo, new OsuRuleset().RulesetInfo, new RealmUser())
|
||||||
{
|
{
|
||||||
Ruleset = rulesetInfo,
|
Ruleset = rulesetInfo,
|
||||||
OnlineID = scoreId
|
OnlineID = scoreId
|
||||||
},
|
});
|
||||||
onUpdateReady));
|
watcher.LatestUpdate.BindValueChanged(update =>
|
||||||
|
{
|
||||||
|
if (update.NewValue?.Score.OnlineID == scoreId)
|
||||||
|
onUpdateReady.Invoke(update.NewValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
private void feignScoreProcessing(int userId, RulesetInfo rulesetInfo, long newTotalScore)
|
private void feignScoreProcessing(int userId, RulesetInfo rulesetInfo, long newTotalScore)
|
||||||
=> AddStep("feign score processing", () => serverSideStatistics[(userId, rulesetInfo.ShortName)] = new UserStatistics { TotalScore = newTotalScore });
|
=> AddStep("feign score processing", () => serverSideStatistics[(userId, rulesetInfo.ShortName)] = new UserStatistics { TotalScore = newTotalScore });
|
||||||
|
@ -137,6 +137,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
@"top_ranks",
|
@"top_ranks",
|
||||||
@"medals"
|
@"medals"
|
||||||
},
|
},
|
||||||
|
RankHighest = new APIUser.UserRankHighest
|
||||||
|
{
|
||||||
|
Rank = 1,
|
||||||
|
UpdatedAt = DateTimeOffset.Now,
|
||||||
|
},
|
||||||
Statistics = new UserStatistics
|
Statistics = new UserStatistics
|
||||||
{
|
{
|
||||||
IsRanked = true,
|
IsRanked = true,
|
||||||
|
@ -40,7 +40,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
new APIMod { Acronym = new OsuModHardRock().Acronym },
|
new APIMod { Acronym = new OsuModHardRock().Acronym },
|
||||||
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
|
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
|
||||||
},
|
},
|
||||||
Accuracy = 0.9813
|
Accuracy = 0.9813,
|
||||||
|
Ranked = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var secondScore = new SoloScoreInfo
|
var secondScore = new SoloScoreInfo
|
||||||
@ -62,7 +63,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
new APIMod { Acronym = new OsuModHardRock().Acronym },
|
new APIMod { Acronym = new OsuModHardRock().Acronym },
|
||||||
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
|
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
|
||||||
},
|
},
|
||||||
Accuracy = 0.998546
|
Accuracy = 0.998546,
|
||||||
|
Ranked = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var thirdScore = new SoloScoreInfo
|
var thirdScore = new SoloScoreInfo
|
||||||
@ -79,7 +81,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
DifficultyName = "Insane"
|
DifficultyName = "Insane"
|
||||||
},
|
},
|
||||||
EndedAt = DateTimeOffset.Now,
|
EndedAt = DateTimeOffset.Now,
|
||||||
Accuracy = 0.9726
|
Accuracy = 0.9726,
|
||||||
|
Ranked = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var noPPScore = new SoloScoreInfo
|
var noPPScore = new SoloScoreInfo
|
||||||
@ -95,7 +98,26 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
DifficultyName = "[4K] Cataclysmic Hypernova"
|
DifficultyName = "[4K] Cataclysmic Hypernova"
|
||||||
},
|
},
|
||||||
EndedAt = DateTimeOffset.Now,
|
EndedAt = DateTimeOffset.Now,
|
||||||
Accuracy = 0.55879
|
Accuracy = 0.55879,
|
||||||
|
Ranked = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var lovedScore = new SoloScoreInfo
|
||||||
|
{
|
||||||
|
Rank = ScoreRank.B,
|
||||||
|
Beatmap = new APIBeatmap
|
||||||
|
{
|
||||||
|
BeatmapSet = new APIBeatmapSet
|
||||||
|
{
|
||||||
|
Title = "C18H27NO3(extend)",
|
||||||
|
Artist = "Team Grimoire",
|
||||||
|
},
|
||||||
|
DifficultyName = "[4K] Cataclysmic Hypernova",
|
||||||
|
Status = BeatmapOnlineStatus.Loved,
|
||||||
|
},
|
||||||
|
EndedAt = DateTimeOffset.Now,
|
||||||
|
Accuracy = 0.55879,
|
||||||
|
Ranked = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var unprocessedPPScore = new SoloScoreInfo
|
var unprocessedPPScore = new SoloScoreInfo
|
||||||
@ -112,7 +134,26 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Status = BeatmapOnlineStatus.Ranked,
|
Status = BeatmapOnlineStatus.Ranked,
|
||||||
},
|
},
|
||||||
EndedAt = DateTimeOffset.Now,
|
EndedAt = DateTimeOffset.Now,
|
||||||
Accuracy = 0.55879
|
Accuracy = 0.55879,
|
||||||
|
Ranked = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var unrankedPPScore = new SoloScoreInfo
|
||||||
|
{
|
||||||
|
Rank = ScoreRank.B,
|
||||||
|
Beatmap = new APIBeatmap
|
||||||
|
{
|
||||||
|
BeatmapSet = new APIBeatmapSet
|
||||||
|
{
|
||||||
|
Title = "C18H27NO3(extend)",
|
||||||
|
Artist = "Team Grimoire",
|
||||||
|
},
|
||||||
|
DifficultyName = "[4K] Cataclysmic Hypernova",
|
||||||
|
Status = BeatmapOnlineStatus.Ranked,
|
||||||
|
},
|
||||||
|
EndedAt = DateTimeOffset.Now,
|
||||||
|
Accuracy = 0.55879,
|
||||||
|
Ranked = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Add(new FillFlowContainer
|
Add(new FillFlowContainer
|
||||||
@ -128,7 +169,9 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
new ColourProvidedContainer(OverlayColourScheme.Green, new DrawableProfileScore(firstScore)),
|
new ColourProvidedContainer(OverlayColourScheme.Green, new DrawableProfileScore(firstScore)),
|
||||||
new ColourProvidedContainer(OverlayColourScheme.Green, new DrawableProfileScore(secondScore)),
|
new ColourProvidedContainer(OverlayColourScheme.Green, new DrawableProfileScore(secondScore)),
|
||||||
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileScore(noPPScore)),
|
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileScore(noPPScore)),
|
||||||
|
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileScore(lovedScore)),
|
||||||
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileScore(unprocessedPPScore)),
|
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileScore(unprocessedPPScore)),
|
||||||
|
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileScore(unrankedPPScore)),
|
||||||
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileWeightedScore(firstScore, 0.97)),
|
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileWeightedScore(firstScore, 0.97)),
|
||||||
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileWeightedScore(secondScore, 0.85)),
|
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileWeightedScore(secondScore, 0.85)),
|
||||||
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileWeightedScore(thirdScore, 0.66)),
|
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileWeightedScore(thirdScore, 0.66)),
|
||||||
|
42
osu.Game.Tests/Visual/Ranking/TestSceneGradedCircles.cs
Normal file
42
osu.Game.Tests/Visual/Ranking/TestSceneGradedCircles.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Ranking.Expanded.Accuracy;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
|
{
|
||||||
|
public partial class TestSceneGradedCircles : OsuTestScene
|
||||||
|
{
|
||||||
|
private readonly GradedCircles ring;
|
||||||
|
|
||||||
|
public TestSceneGradedCircles()
|
||||||
|
{
|
||||||
|
ScoreProcessor scoreProcessor = new OsuRuleset().CreateScoreProcessor();
|
||||||
|
double accuracyX = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.X);
|
||||||
|
double accuracyS = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.S);
|
||||||
|
|
||||||
|
double accuracyA = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.A);
|
||||||
|
double accuracyB = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.B);
|
||||||
|
double accuracyC = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.C);
|
||||||
|
|
||||||
|
Add(ring = new GradedCircles(accuracyC, accuracyB, accuracyA, accuracyS, accuracyX)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(400)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
AddSliderStep("Progress", 0.0, 1.0, 1.0, p => ring.Progress = p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -629,7 +629,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
var sets = new List<BeatmapSetInfo>();
|
var sets = new List<BeatmapSetInfo>();
|
||||||
|
|
||||||
const string zzz_string = "zzzzz";
|
const string zzz_lowercase = "zzzzz";
|
||||||
|
const string zzz_uppercase = "ZZZZZ";
|
||||||
|
|
||||||
AddStep("Populuate beatmap sets", () =>
|
AddStep("Populuate beatmap sets", () =>
|
||||||
{
|
{
|
||||||
@ -640,10 +641,16 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
var set = TestResources.CreateTestBeatmapSetInfo();
|
var set = TestResources.CreateTestBeatmapSetInfo();
|
||||||
|
|
||||||
if (i == 4)
|
if (i == 4)
|
||||||
set.Beatmaps.ForEach(b => b.Metadata.Artist = zzz_string);
|
set.Beatmaps.ForEach(b => b.Metadata.Artist = zzz_uppercase);
|
||||||
|
|
||||||
|
if (i == 8)
|
||||||
|
set.Beatmaps.ForEach(b => b.Metadata.Artist = zzz_lowercase);
|
||||||
|
|
||||||
|
if (i == 12)
|
||||||
|
set.Beatmaps.ForEach(b => b.Metadata.Author.Username = zzz_uppercase);
|
||||||
|
|
||||||
if (i == 16)
|
if (i == 16)
|
||||||
set.Beatmaps.ForEach(b => b.Metadata.Author.Username = zzz_string);
|
set.Beatmaps.ForEach(b => b.Metadata.Author.Username = zzz_lowercase);
|
||||||
|
|
||||||
sets.Add(set);
|
sets.Add(set);
|
||||||
}
|
}
|
||||||
@ -652,9 +659,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
loadBeatmaps(sets);
|
loadBeatmaps(sets);
|
||||||
|
|
||||||
AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false));
|
AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false));
|
||||||
AddAssert($"Check {zzz_string} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Author.Username == zzz_string);
|
AddAssert($"Check {zzz_uppercase} is last", () => carousel.BeatmapSets.Last().Metadata.Author.Username == zzz_uppercase);
|
||||||
|
AddAssert($"Check {zzz_lowercase} is second last", () => carousel.BeatmapSets.SkipLast(1).Last().Metadata.Author.Username == zzz_lowercase);
|
||||||
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
|
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
|
||||||
AddAssert($"Check {zzz_string} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Artist == zzz_string);
|
AddAssert($"Check {zzz_uppercase} is last", () => carousel.BeatmapSets.Last().Metadata.Artist == zzz_uppercase);
|
||||||
|
AddAssert($"Check {zzz_lowercase} is second last", () => carousel.BeatmapSets.SkipLast(1).Last().Metadata.Artist == zzz_lowercase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -282,7 +282,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check that the logo is tracking the position of the facade, with an acceptable precision lenience.
|
/// Check that the logo is tracking the position of the facade, with an acceptable precision lenience.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsLogoTracking => Precision.AlmostEquals(Logo.Position, ComputeLogoTrackingPosition());
|
public bool IsLogoTracking => Precision.AlmostEquals(Logo!.Position, ComputeLogoTrackingPosition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -788,7 +788,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("all columns visible", () => this.ChildrenOfType<ModColumn>().All(col => col.IsPresent));
|
AddAssert("all columns visible", () => this.ChildrenOfType<ModColumn>().All(col => col.IsPresent));
|
||||||
|
|
||||||
AddStep("set search", () => modSelectOverlay.SearchTerm = "HD");
|
AddStep("set search", () => modSelectOverlay.SearchTerm = "HD");
|
||||||
AddAssert("one column visible", () => this.ChildrenOfType<ModColumn>().Count(col => col.IsPresent) == 1);
|
AddAssert("two columns visible", () => this.ChildrenOfType<ModColumn>().Count(col => col.IsPresent) == 2);
|
||||||
|
|
||||||
AddStep("filter out everything", () => modSelectOverlay.SearchTerm = "Some long search term with no matches");
|
AddStep("filter out everything", () => modSelectOverlay.SearchTerm = "Some long search term with no matches");
|
||||||
AddAssert("no columns visible", () => this.ChildrenOfType<ModColumn>().All(col => !col.IsPresent));
|
AddAssert("no columns visible", () => this.ChildrenOfType<ModColumn>().All(col => !col.IsPresent));
|
||||||
@ -812,7 +812,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("all columns visible", () => this.ChildrenOfType<ModColumn>().All(col => col.IsPresent));
|
AddAssert("all columns visible", () => this.ChildrenOfType<ModColumn>().All(col => col.IsPresent));
|
||||||
|
|
||||||
AddStep("set search", () => modSelectOverlay.SearchTerm = "fail");
|
AddStep("set search", () => modSelectOverlay.SearchTerm = "fail");
|
||||||
AddAssert("one column visible", () => this.ChildrenOfType<ModColumn>().Count(col => col.IsPresent) == 2);
|
AddAssert("one column visible", () => this.ChildrenOfType<ModColumn>().Count(col => col.IsPresent) == 1);
|
||||||
|
|
||||||
AddStep("hide", () => modSelectOverlay.Hide());
|
AddStep("hide", () => modSelectOverlay.Hide());
|
||||||
AddStep("show", () => modSelectOverlay.Show());
|
AddStep("show", () => modSelectOverlay.Show());
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Input.States;
|
using osu.Framework.Input.States;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
|
||||||
@ -18,13 +17,22 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
public partial class TestSceneOsuDropdown : ThemeComparisonTestScene
|
public partial class TestSceneOsuDropdown : ThemeComparisonTestScene
|
||||||
{
|
{
|
||||||
protected override Drawable CreateContent() =>
|
protected override Drawable CreateContent() =>
|
||||||
new OsuEnumDropdown<BeatmapOnlineStatus>
|
new OsuEnumDropdown<TestEnum>
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Width = 150
|
Width = 150
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private enum TestEnum
|
||||||
|
{
|
||||||
|
[System.ComponentModel.Description("Option")]
|
||||||
|
Option,
|
||||||
|
|
||||||
|
[System.ComponentModel.Description("Really lonnnnnnng option")]
|
||||||
|
ReallyLongOption,
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
// todo: this can be written much better if ThemeComparisonTestScene has a manual input manager
|
// todo: this can be written much better if ThemeComparisonTestScene has a manual input manager
|
||||||
public void TestBackAction()
|
public void TestBackAction()
|
||||||
@ -43,7 +51,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("press back", () => dropdown().OnPressed(new KeyBindingPressEvent<GlobalAction>(new InputState(), GlobalAction.Back)));
|
AddStep("press back", () => dropdown().OnPressed(new KeyBindingPressEvent<GlobalAction>(new InputState(), GlobalAction.Back)));
|
||||||
AddAssert("closed", () => dropdown().ChildrenOfType<Menu>().Single().State == MenuState.Closed);
|
AddAssert("closed", () => dropdown().ChildrenOfType<Menu>().Single().State == MenuState.Closed);
|
||||||
|
|
||||||
OsuEnumDropdown<BeatmapOnlineStatus> dropdown() => this.ChildrenOfType<OsuEnumDropdown<BeatmapOnlineStatus>>().First();
|
OsuEnumDropdown<TestEnum> dropdown() => this.ChildrenOfType<OsuEnumDropdown<TestEnum>>().First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,18 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
public abstract partial class ThemeComparisonTestScene : OsuGridTestScene
|
public abstract partial class ThemeComparisonTestScene : OsuGridTestScene
|
||||||
{
|
{
|
||||||
protected ThemeComparisonTestScene()
|
private readonly bool showWithoutColourProvider;
|
||||||
: base(1, 2)
|
|
||||||
|
protected ThemeComparisonTestScene(bool showWithoutColourProvider = true)
|
||||||
|
: base(1, showWithoutColourProvider ? 2 : 1)
|
||||||
{
|
{
|
||||||
|
this.showWithoutColourProvider = showWithoutColourProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
if (showWithoutColourProvider)
|
||||||
{
|
{
|
||||||
Cell(0, 0).AddRange(new[]
|
Cell(0, 0).AddRange(new[]
|
||||||
{
|
{
|
||||||
@ -32,13 +37,16 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
CreateContent()
|
CreateContent()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void CreateThemedContent(OverlayColourScheme colourScheme)
|
protected void CreateThemedContent(OverlayColourScheme colourScheme)
|
||||||
{
|
{
|
||||||
var colourProvider = new OverlayColourProvider(colourScheme);
|
var colourProvider = new OverlayColourProvider(colourScheme);
|
||||||
|
|
||||||
Cell(0, 1).Clear();
|
int col = showWithoutColourProvider ? 1 : 0;
|
||||||
Cell(0, 1).Add(new DependencyProvidingContainer
|
|
||||||
|
Cell(0, col).Clear();
|
||||||
|
Cell(0, col).Add(new DependencyProvidingContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
CachedDependencies = new (Type, object)[]
|
CachedDependencies = new (Type, object)[]
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Debug/net6.0/osu.Game.Tournament.Tests.dll"
|
"${workspaceRoot}/bin/Debug/net8.0/osu.Game.Tournament.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Debug)",
|
"preLaunchTask": "Build (Debug)",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/bin/Release/net6.0/osu.Game.Tournament.Tests.dll"
|
"${workspaceRoot}/bin/Release/net8.0/osu.Game.Tournament.Tests.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build (Release)",
|
"preLaunchTask": "Build (Release)",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using ManagedBass.Fx;
|
using ManagedBass.Fx;
|
||||||
using osu.Framework.Audio.Mixing;
|
using osu.Framework.Audio.Mixing;
|
||||||
|
using osu.Framework.Caching;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Audio.Effects
|
namespace osu.Game.Audio.Effects
|
||||||
@ -22,6 +23,8 @@ namespace osu.Game.Audio.Effects
|
|||||||
|
|
||||||
private bool isAttached;
|
private bool isAttached;
|
||||||
|
|
||||||
|
private readonly Cached filterApplication = new Cached();
|
||||||
|
|
||||||
private int cutoff;
|
private int cutoff;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -36,7 +39,7 @@ namespace osu.Game.Audio.Effects
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
cutoff = value;
|
cutoff = value;
|
||||||
updateFilter(cutoff);
|
filterApplication.Invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +64,17 @@ namespace osu.Game.Audio.Effects
|
|||||||
Cutoff = getInitialCutoff(type);
|
Cutoff = getInitialCutoff(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (!filterApplication.IsValid)
|
||||||
|
{
|
||||||
|
updateFilter(cutoff);
|
||||||
|
filterApplication.Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int getInitialCutoff(BQFType type)
|
private int getInitialCutoff(BQFType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
@ -38,17 +39,22 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <param name="preferOnlineFetch">Whether metadata from an online source should be preferred. If <c>true</c>, the local cache will be skipped to ensure the freshest data state possible.</param>
|
/// <param name="preferOnlineFetch">Whether metadata from an online source should be preferred. If <c>true</c>, the local cache will be skipped to ensure the freshest data state possible.</param>
|
||||||
public void Update(BeatmapSetInfo beatmapSet, bool preferOnlineFetch)
|
public void Update(BeatmapSetInfo beatmapSet, bool preferOnlineFetch)
|
||||||
{
|
{
|
||||||
|
var lookupResults = new List<OnlineBeatmapMetadata?>();
|
||||||
|
|
||||||
foreach (var beatmapInfo in beatmapSet.Beatmaps)
|
foreach (var beatmapInfo in beatmapSet.Beatmaps)
|
||||||
{
|
{
|
||||||
if (!tryLookup(beatmapInfo, preferOnlineFetch, out var res))
|
if (!tryLookup(beatmapInfo, preferOnlineFetch, out var res))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (res == null)
|
if (res == null || shouldDiscardLookupResult(res, beatmapInfo))
|
||||||
{
|
{
|
||||||
beatmapInfo.ResetOnlineInfo();
|
beatmapInfo.ResetOnlineInfo();
|
||||||
|
lookupResults.Add(null); // mark lookup failure
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lookupResults.Add(res);
|
||||||
|
|
||||||
beatmapInfo.OnlineID = res.BeatmapID;
|
beatmapInfo.OnlineID = res.BeatmapID;
|
||||||
beatmapInfo.OnlineMD5Hash = res.MD5Hash;
|
beatmapInfo.OnlineMD5Hash = res.MD5Hash;
|
||||||
beatmapInfo.LastOnlineUpdate = res.LastUpdated;
|
beatmapInfo.LastOnlineUpdate = res.LastUpdated;
|
||||||
@ -57,19 +63,34 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmapInfo.BeatmapSet.OnlineID = res.BeatmapSetID;
|
beatmapInfo.BeatmapSet.OnlineID = res.BeatmapSetID;
|
||||||
|
|
||||||
// Some metadata should only be applied if there's no local changes.
|
// Some metadata should only be applied if there's no local changes.
|
||||||
if (shouldSaveOnlineMetadata(beatmapInfo))
|
if (beatmapInfo.MatchesOnlineVersion)
|
||||||
{
|
{
|
||||||
beatmapInfo.Status = res.BeatmapStatus;
|
beatmapInfo.Status = res.BeatmapStatus;
|
||||||
beatmapInfo.Metadata.Author.OnlineID = res.AuthorID;
|
beatmapInfo.Metadata.Author.OnlineID = res.AuthorID;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (beatmapInfo.BeatmapSet.Beatmaps.All(shouldSaveOnlineMetadata))
|
if (beatmapSet.Beatmaps.All(b => b.MatchesOnlineVersion)
|
||||||
|
&& lookupResults.All(r => r != null)
|
||||||
|
&& lookupResults.Select(r => r!.BeatmapSetID).Distinct().Count() == 1)
|
||||||
{
|
{
|
||||||
beatmapInfo.BeatmapSet.Status = res.BeatmapSetStatus ?? BeatmapOnlineStatus.None;
|
var representative = lookupResults.First()!;
|
||||||
beatmapInfo.BeatmapSet.DateRanked = res.DateRanked;
|
|
||||||
beatmapInfo.BeatmapSet.DateSubmitted = res.DateSubmitted;
|
beatmapSet.Status = representative.BeatmapSetStatus ?? BeatmapOnlineStatus.None;
|
||||||
|
beatmapSet.DateRanked = representative.DateRanked;
|
||||||
|
beatmapSet.DateSubmitted = representative.DateSubmitted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool shouldDiscardLookupResult(OnlineBeatmapMetadata result, BeatmapInfo beatmapInfo)
|
||||||
|
{
|
||||||
|
if (beatmapInfo.OnlineID > 0 && result.BeatmapID != beatmapInfo.OnlineID)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (beatmapInfo.OnlineID == -1 && result.MD5Hash != beatmapInfo.MD5Hash)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -104,12 +125,6 @@ namespace osu.Game.Beatmaps
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check whether the provided beatmap is in a state where online "ranked" status metadata should be saved against it.
|
|
||||||
/// Handles the case where a user may have locally modified a beatmap in the editor and expects the local status to stick.
|
|
||||||
/// </summary>
|
|
||||||
private static bool shouldSaveOnlineMetadata(BeatmapInfo beatmapInfo) => beatmapInfo.MatchesOnlineVersion || beatmapInfo.Status != BeatmapOnlineStatus.LocallyModified;
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
apiMetadataSource.Dispose();
|
apiMetadataSource.Dispose();
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -31,14 +32,16 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to display a tooltip on hover. Only works if a beatmap was provided at construction time.
|
/// Which type of tooltip to show. Only works if a beatmap was provided at construction time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ShowTooltip { get; set; } = true;
|
public DifficultyIconTooltipType TooltipType { get; set; } = DifficultyIconTooltipType.StarRating;
|
||||||
|
|
||||||
private readonly IBeatmapInfo? beatmap;
|
private readonly IBeatmapInfo? beatmap;
|
||||||
|
|
||||||
private readonly IRulesetInfo ruleset;
|
private readonly IRulesetInfo ruleset;
|
||||||
|
|
||||||
|
private readonly Mod[]? mods;
|
||||||
|
|
||||||
private Drawable background = null!;
|
private Drawable background = null!;
|
||||||
|
|
||||||
private readonly Container iconContainer;
|
private readonly Container iconContainer;
|
||||||
@ -58,11 +61,14 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
/// Creates a new <see cref="DifficultyIcon"/>. Will use provided beatmap's <see cref="BeatmapInfo.StarRating"/> for initial value.
|
/// Creates a new <see cref="DifficultyIcon"/>. Will use provided beatmap's <see cref="BeatmapInfo.StarRating"/> for initial value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmap">The beatmap to be displayed in the tooltip, and to be used for the initial star rating value.</param>
|
/// <param name="beatmap">The beatmap to be displayed in the tooltip, and to be used for the initial star rating value.</param>
|
||||||
|
/// <param name="mods">An array of mods to account for in the calculations</param>
|
||||||
/// <param name="ruleset">An optional ruleset to be used for the icon display, in place of the beatmap's ruleset.</param>
|
/// <param name="ruleset">An optional ruleset to be used for the icon display, in place of the beatmap's ruleset.</param>
|
||||||
public DifficultyIcon(IBeatmapInfo beatmap, IRulesetInfo? ruleset = null)
|
public DifficultyIcon(IBeatmapInfo beatmap, IRulesetInfo? ruleset = null, Mod[]? mods = null)
|
||||||
: this(ruleset ?? beatmap.Ruleset)
|
: this(ruleset ?? beatmap.Ruleset)
|
||||||
{
|
{
|
||||||
this.beatmap = beatmap;
|
this.beatmap = beatmap;
|
||||||
|
this.mods = mods;
|
||||||
|
|
||||||
Current.Value = new StarDifficulty(beatmap.StarRating, 0);
|
Current.Value = new StarDifficulty(beatmap.StarRating, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,6 +133,24 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
GetCustomTooltip() => new DifficultyIconTooltip();
|
GetCustomTooltip() => new DifficultyIconTooltip();
|
||||||
|
|
||||||
DifficultyIconTooltipContent IHasCustomTooltip<DifficultyIconTooltipContent>.
|
DifficultyIconTooltipContent IHasCustomTooltip<DifficultyIconTooltipContent>.
|
||||||
TooltipContent => (ShowTooltip && beatmap != null ? new DifficultyIconTooltipContent(beatmap, Current) : null)!;
|
TooltipContent => (TooltipType != DifficultyIconTooltipType.None && beatmap != null ? new DifficultyIconTooltipContent(beatmap, Current, ruleset, mods, TooltipType) : null)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DifficultyIconTooltipType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No tooltip.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Star rating only.
|
||||||
|
/// </summary>
|
||||||
|
StarRating,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Star rating, OD, HP, CS, AR, length, BPM, and max combo.
|
||||||
|
/// </summary>
|
||||||
|
Extended,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -11,14 +11,25 @@ using osu.Framework.Graphics.Cursor;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables
|
namespace osu.Game.Beatmaps.Drawables
|
||||||
{
|
{
|
||||||
internal partial class DifficultyIconTooltip : VisibilityContainer, ITooltip<DifficultyIconTooltipContent>
|
internal partial class DifficultyIconTooltip : VisibilityContainer, ITooltip<DifficultyIconTooltipContent>
|
||||||
{
|
{
|
||||||
private OsuSpriteText difficultyName;
|
private OsuSpriteText difficultyName = null!;
|
||||||
private StarRatingDisplay starRating;
|
private StarRatingDisplay starRating = null!;
|
||||||
|
private OsuSpriteText overallDifficulty = null!;
|
||||||
|
private OsuSpriteText drainRate = null!;
|
||||||
|
private OsuSpriteText circleSize = null!;
|
||||||
|
private OsuSpriteText approachRate = null!;
|
||||||
|
private OsuSpriteText bpm = null!;
|
||||||
|
private OsuSpriteText length = null!;
|
||||||
|
|
||||||
|
private FillFlowContainer difficultyFillFlowContainer = null!;
|
||||||
|
private FillFlowContainer miscFillFlowContainer = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
@ -31,7 +42,6 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Alpha = 0.9f,
|
|
||||||
Colour = colours.Gray3,
|
Colour = colours.Gray3,
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
@ -49,19 +59,49 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold),
|
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold)
|
||||||
},
|
},
|
||||||
starRating = new StarRatingDisplay(default, StarRatingDisplaySize.Small)
|
starRating = new StarRatingDisplay(default, StarRatingDisplaySize.Small)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre
|
||||||
|
},
|
||||||
|
difficultyFillFlowContainer = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
Alpha = 0,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
circleSize = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
|
||||||
|
drainRate = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
|
||||||
|
overallDifficulty = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
|
||||||
|
approachRate = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
miscFillFlowContainer = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Alpha = 0,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
length = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
|
||||||
|
bpm = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private DifficultyIconTooltipContent displayedContent;
|
private DifficultyIconTooltipContent? displayedContent;
|
||||||
|
|
||||||
public void SetContent(DifficultyIconTooltipContent content)
|
public void SetContent(DifficultyIconTooltipContent content)
|
||||||
{
|
{
|
||||||
@ -72,6 +112,45 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
starRating.Current.BindTarget = displayedContent.Difficulty;
|
starRating.Current.BindTarget = displayedContent.Difficulty;
|
||||||
difficultyName.Text = displayedContent.BeatmapInfo.DifficultyName;
|
difficultyName.Text = displayedContent.BeatmapInfo.DifficultyName;
|
||||||
|
|
||||||
|
if (displayedContent.TooltipType == DifficultyIconTooltipType.StarRating)
|
||||||
|
{
|
||||||
|
difficultyFillFlowContainer.Hide();
|
||||||
|
miscFillFlowContainer.Hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
difficultyFillFlowContainer.Show();
|
||||||
|
miscFillFlowContainer.Show();
|
||||||
|
|
||||||
|
double rate = 1;
|
||||||
|
|
||||||
|
if (displayedContent.Mods != null)
|
||||||
|
{
|
||||||
|
foreach (var mod in displayedContent.Mods.OfType<IApplicableToRate>())
|
||||||
|
rate = mod.ApplyToRate(0, rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
double bpmAdjusted = displayedContent.BeatmapInfo.BPM * rate;
|
||||||
|
|
||||||
|
BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(displayedContent.BeatmapInfo.Difficulty);
|
||||||
|
|
||||||
|
if (displayedContent.Mods != null)
|
||||||
|
{
|
||||||
|
foreach (var mod in displayedContent.Mods.OfType<IApplicableToDifficulty>())
|
||||||
|
mod.ApplyToDifficulty(originalDifficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ruleset ruleset = displayedContent.Ruleset.CreateInstance();
|
||||||
|
BeatmapDifficulty adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate);
|
||||||
|
|
||||||
|
circleSize.Text = @"CS: " + adjustedDifficulty.CircleSize.ToString(@"0.##");
|
||||||
|
drainRate.Text = @" HP: " + adjustedDifficulty.DrainRate.ToString(@"0.##");
|
||||||
|
approachRate.Text = @" AR: " + adjustedDifficulty.ApproachRate.ToString(@"0.##");
|
||||||
|
overallDifficulty.Text = @" OD: " + adjustedDifficulty.OverallDifficulty.ToString(@"0.##");
|
||||||
|
|
||||||
|
length.Text = "Length: " + TimeSpan.FromMilliseconds(displayedContent.BeatmapInfo.Length / rate).ToString(@"mm\:ss");
|
||||||
|
bpm.Text = " BPM: " + Math.Round(bpmAdjusted, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Move(Vector2 pos) => Position = pos;
|
public void Move(Vector2 pos) => Position = pos;
|
||||||
@ -85,11 +164,20 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
{
|
{
|
||||||
public readonly IBeatmapInfo BeatmapInfo;
|
public readonly IBeatmapInfo BeatmapInfo;
|
||||||
public readonly IBindable<StarDifficulty> Difficulty;
|
public readonly IBindable<StarDifficulty> Difficulty;
|
||||||
|
public readonly IRulesetInfo Ruleset;
|
||||||
|
public readonly Mod[]? Mods;
|
||||||
|
public readonly DifficultyIconTooltipType TooltipType;
|
||||||
|
|
||||||
public DifficultyIconTooltipContent(IBeatmapInfo beatmapInfo, IBindable<StarDifficulty> difficulty)
|
public DifficultyIconTooltipContent(IBeatmapInfo beatmapInfo, IBindable<StarDifficulty> difficulty, IRulesetInfo rulesetInfo, Mod[]? mods, DifficultyIconTooltipType tooltipType)
|
||||||
{
|
{
|
||||||
|
if (tooltipType == DifficultyIconTooltipType.None)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(tooltipType), tooltipType, "Cannot instantiate a tooltip without a type");
|
||||||
|
|
||||||
BeatmapInfo = beatmapInfo;
|
BeatmapInfo = beatmapInfo;
|
||||||
Difficulty = difficulty;
|
Difficulty = difficulty;
|
||||||
|
Ruleset = rulesetInfo;
|
||||||
|
Mods = mods;
|
||||||
|
TooltipType = tooltipType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
private readonly OffsetCorrectionClock? userGlobalOffsetClock;
|
private readonly OffsetCorrectionClock? userGlobalOffsetClock;
|
||||||
private readonly OffsetCorrectionClock? platformOffsetClock;
|
private readonly OffsetCorrectionClock? platformOffsetClock;
|
||||||
private readonly OffsetCorrectionClock? userBeatmapOffsetClock;
|
private readonly FramedOffsetClock? userBeatmapOffsetClock;
|
||||||
|
|
||||||
private readonly IFrameBasedClock finalClockSource;
|
private readonly IFrameBasedClock finalClockSource;
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ namespace osu.Game.Beatmaps
|
|||||||
userGlobalOffsetClock = new OffsetCorrectionClock(platformOffsetClock);
|
userGlobalOffsetClock = new OffsetCorrectionClock(platformOffsetClock);
|
||||||
|
|
||||||
// User per-beatmap offset will be applied to this final clock.
|
// User per-beatmap offset will be applied to this final clock.
|
||||||
finalClockSource = userBeatmapOffsetClock = new OffsetCorrectionClock(userGlobalOffsetClock);
|
finalClockSource = userBeatmapOffsetClock = new FramedOffsetClock(userGlobalOffsetClock);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -122,7 +122,7 @@ namespace osu.Game.Beatmaps
|
|||||||
Debug.Assert(userBeatmapOffsetClock != null);
|
Debug.Assert(userBeatmapOffsetClock != null);
|
||||||
Debug.Assert(platformOffsetClock != null);
|
Debug.Assert(platformOffsetClock != null);
|
||||||
|
|
||||||
return userGlobalOffsetClock.RateAdjustedOffset + userBeatmapOffsetClock.RateAdjustedOffset + platformOffsetClock.RateAdjustedOffset;
|
return userGlobalOffsetClock.RateAdjustedOffset + userBeatmapOffsetClock.Offset + platformOffsetClock.RateAdjustedOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
static void addCombo(HitObject hitObject, ref int combo)
|
static void addCombo(HitObject hitObject, ref int combo)
|
||||||
{
|
{
|
||||||
if (hitObject.CreateJudgement().MaxResult.AffectsCombo())
|
if (hitObject.Judgement.MaxResult.AffectsCombo())
|
||||||
combo++;
|
combo++;
|
||||||
|
|
||||||
foreach (var nested in hitObject.NestedHitObjects)
|
foreach (var nested in hitObject.NestedHitObjects)
|
||||||
|
@ -9,6 +9,7 @@ using System.Linq;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics.Rendering;
|
using osu.Framework.Graphics.Rendering;
|
||||||
using osu.Framework.Graphics.Rendering.Dummy;
|
using osu.Framework.Graphics.Rendering.Dummy;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
@ -143,8 +144,6 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
string fileStorePath = BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path);
|
string fileStorePath = BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path);
|
||||||
|
|
||||||
// TODO: check validity of file
|
|
||||||
|
|
||||||
var stream = GetStream(fileStorePath);
|
var stream = GetStream(fileStorePath);
|
||||||
|
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
@ -153,6 +152,12 @@ namespace osu.Game.Beatmaps
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stream.ComputeMD5Hash() != BeatmapInfo.MD5Hash)
|
||||||
|
{
|
||||||
|
Logger.Log($"Beatmap failed to load (file {BeatmapInfo.Path} does not have the expected hash).", level: LogLevel.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
using (var reader = new LineBufferedReader(stream))
|
using (var reader = new LineBufferedReader(stream))
|
||||||
return Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
return Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||||
}
|
}
|
||||||
|
@ -77,12 +77,19 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
SetDefault(OsuSetting.SavePassword, false).ValueChanged += enabled =>
|
SetDefault(OsuSetting.SavePassword, false).ValueChanged += enabled =>
|
||||||
{
|
{
|
||||||
if (enabled.NewValue) SetValue(OsuSetting.SaveUsername, true);
|
if (enabled.NewValue)
|
||||||
|
SetValue(OsuSetting.SaveUsername, true);
|
||||||
|
else
|
||||||
|
GetBindable<string>(OsuSetting.Token).SetDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
SetDefault(OsuSetting.SaveUsername, true).ValueChanged += enabled =>
|
SetDefault(OsuSetting.SaveUsername, true).ValueChanged += enabled =>
|
||||||
{
|
{
|
||||||
if (!enabled.NewValue) SetValue(OsuSetting.SavePassword, false);
|
if (!enabled.NewValue)
|
||||||
|
{
|
||||||
|
GetBindable<string>(OsuSetting.Username).SetDefault();
|
||||||
|
SetValue(OsuSetting.SavePassword, false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SetDefault(OsuSetting.ExternalLinkWarning, true);
|
SetDefault(OsuSetting.ExternalLinkWarning, true);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -19,7 +17,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
{
|
{
|
||||||
public Facade LogoFacade => facade;
|
public Facade LogoFacade => facade;
|
||||||
|
|
||||||
protected OsuLogo Logo { get; private set; }
|
protected OsuLogo? Logo { get; private set; }
|
||||||
|
|
||||||
private readonly InternalFacade facade = new InternalFacade();
|
private readonly InternalFacade facade = new InternalFacade();
|
||||||
|
|
||||||
@ -76,15 +74,15 @@ namespace osu.Game.Graphics.Containers
|
|||||||
/// <remarks>Will only be correct if the logo's <see cref="Drawable.RelativePositionAxes"/> are set to Axes.Both</remarks>
|
/// <remarks>Will only be correct if the logo's <see cref="Drawable.RelativePositionAxes"/> are set to Axes.Both</remarks>
|
||||||
protected Vector2 ComputeLogoTrackingPosition()
|
protected Vector2 ComputeLogoTrackingPosition()
|
||||||
{
|
{
|
||||||
var absolutePos = Logo.Parent!.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre);
|
var absolutePos = Logo!.Parent!.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre);
|
||||||
|
|
||||||
return new Vector2(absolutePos.X / Logo.Parent!.RelativeToAbsoluteFactor.X,
|
return new Vector2(absolutePos.X / Logo.Parent!.RelativeToAbsoluteFactor.X,
|
||||||
absolutePos.Y / Logo.Parent!.RelativeToAbsoluteFactor.Y);
|
absolutePos.Y / Logo.Parent!.RelativeToAbsoluteFactor.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void UpdateAfterChildren()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
if (Logo == null)
|
if (Logo == null)
|
||||||
return;
|
return;
|
||||||
|
@ -10,11 +10,11 @@ using osu.Game.Overlays;
|
|||||||
|
|
||||||
namespace osu.Game.Graphics.Containers.Markdown
|
namespace osu.Game.Graphics.Containers.Markdown
|
||||||
{
|
{
|
||||||
public partial class OsuMarkdownFencedCodeBlock : MarkdownFencedCodeBlock
|
public partial class OsuMarkdownCodeBlock : MarkdownCodeBlock
|
||||||
{
|
{
|
||||||
// TODO : change to monospace font for this component
|
// TODO : change to monospace font for this component
|
||||||
public OsuMarkdownFencedCodeBlock(FencedCodeBlock fencedCodeBlock)
|
public OsuMarkdownCodeBlock(CodeBlock codeBlock)
|
||||||
: base(fencedCodeBlock)
|
: base(codeBlock)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Containers.Markdown
|
|||||||
|
|
||||||
protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new OsuMarkdownHeading(headingBlock);
|
protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new OsuMarkdownHeading(headingBlock);
|
||||||
|
|
||||||
protected override MarkdownFencedCodeBlock CreateFencedCodeBlock(FencedCodeBlock fencedCodeBlock) => new OsuMarkdownFencedCodeBlock(fencedCodeBlock);
|
protected override MarkdownCodeBlock CreateCodeBlock(CodeBlock codeBlock) => new OsuMarkdownCodeBlock(codeBlock);
|
||||||
|
|
||||||
protected override MarkdownSeparator CreateSeparator(ThematicBreakBlock thematicBlock) => new OsuMarkdownSeparator();
|
protected override MarkdownSeparator CreateSeparator(ThematicBreakBlock thematicBlock) => new OsuMarkdownSeparator();
|
||||||
|
|
||||||
|
17
osu.Game/Graphics/Sprites/SpriteIconWithTooltip.cs
Normal file
17
osu.Game/Graphics/Sprites/SpriteIconWithTooltip.cs
Normal 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 osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Sprites
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="SpriteIcon"/> with a publicly settable tooltip text.
|
||||||
|
/// </summary>
|
||||||
|
public partial class SpriteIconWithTooltip : SpriteIcon, IHasTooltip
|
||||||
|
{
|
||||||
|
public LocalisableString TooltipText { get; set; }
|
||||||
|
}
|
||||||
|
}
|
16
osu.Game/Graphics/Sprites/SpriteTextWithTooltip.cs
Normal file
16
osu.Game/Graphics/Sprites/SpriteTextWithTooltip.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 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.Cursor;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Sprites
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="OsuSpriteText"/> with a publicly settable tooltip text.
|
||||||
|
/// </summary>
|
||||||
|
internal partial class SpriteTextWithTooltip : OsuSpriteText, IHasTooltip
|
||||||
|
{
|
||||||
|
public LocalisableString TooltipText { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -186,6 +186,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
: base(item)
|
: base(item)
|
||||||
{
|
{
|
||||||
Foreground.Padding = new MarginPadding(2);
|
Foreground.Padding = new MarginPadding(2);
|
||||||
|
Foreground.AutoSizeAxes = Axes.Y;
|
||||||
|
Foreground.RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
Masking = true;
|
Masking = true;
|
||||||
CornerRadius = corner_radius;
|
CornerRadius = corner_radius;
|
||||||
@ -247,11 +249,12 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
},
|
},
|
||||||
Label = new OsuSpriteText
|
Label = new TruncatingSpriteText
|
||||||
{
|
{
|
||||||
X = 15,
|
Padding = new MarginPadding { Left = 15 },
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
star.ClearTransforms(true);
|
star.ClearTransforms(true);
|
||||||
|
|
||||||
double delay = (current <= newValue ? Math.Max(i - current, 0) : Math.Max(current - 1 - i, 0)) * AnimationDelay;
|
double delay = Math.Max(current <= newValue ? i - current : Math.Min(current, StarCount) - 1 - i, 0) * AnimationDelay;
|
||||||
|
|
||||||
using (star.BeginDelayedSequence(delay))
|
using (star.BeginDelayedSequence(delay))
|
||||||
star.DisplayAt(getStarScale(i, newValue));
|
star.DisplayAt(getStarScale(i, newValue));
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user