mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 04:02:57 +08:00
Merge branch 'master' into rankings-tables
This commit is contained in:
commit
4221a0126c
@ -111,9 +111,15 @@ csharp_preserve_single_line_statements = true
|
|||||||
|
|
||||||
#Roslyn language styles
|
#Roslyn language styles
|
||||||
|
|
||||||
|
#Style - this. qualification
|
||||||
|
dotnet_style_qualification_for_field = false:warning
|
||||||
|
dotnet_style_qualification_for_property = false:warning
|
||||||
|
dotnet_style_qualification_for_method = false:warning
|
||||||
|
dotnet_style_qualification_for_event = false:warning
|
||||||
|
|
||||||
#Style - type names
|
#Style - type names
|
||||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
|
||||||
dotnet_style_predefined_type_for_member_access = true:silent
|
dotnet_style_predefined_type_for_member_access = true:warning
|
||||||
csharp_style_var_when_type_is_apparent = true:none
|
csharp_style_var_when_type_is_apparent = true:none
|
||||||
csharp_style_var_for_built_in_types = true:none
|
csharp_style_var_for_built_in_types = true:none
|
||||||
csharp_style_var_elsewhere = true:silent
|
csharp_style_var_elsewhere = true:silent
|
||||||
@ -126,51 +132,57 @@ csharp_preferred_modifier_order = public,private,protected,internal,new,abstract
|
|||||||
# Skipped because roslyn cannot separate +-*/ with << >>
|
# Skipped because roslyn cannot separate +-*/ with << >>
|
||||||
|
|
||||||
#Style - expression bodies
|
#Style - expression bodies
|
||||||
csharp_style_expression_bodied_accessors = true:silent
|
csharp_style_expression_bodied_accessors = true:warning
|
||||||
csharp_style_expression_bodied_constructors = false:none
|
csharp_style_expression_bodied_constructors = false:none
|
||||||
csharp_style_expression_bodied_indexers = true:silent
|
csharp_style_expression_bodied_indexers = true:warning
|
||||||
csharp_style_expression_bodied_methods = true:silent
|
csharp_style_expression_bodied_methods = true:silent
|
||||||
csharp_style_expression_bodied_operators = true:silent
|
csharp_style_expression_bodied_operators = true:warning
|
||||||
csharp_style_expression_bodied_properties = true:silent
|
csharp_style_expression_bodied_properties = true:warning
|
||||||
|
csharp_style_expression_bodied_local_functions = true:silent
|
||||||
|
|
||||||
#Style - expression preferences
|
#Style - expression preferences
|
||||||
dotnet_style_object_initializer = true:warning
|
dotnet_style_object_initializer = true:warning
|
||||||
dotnet_style_collection_initializer = true:warning
|
dotnet_style_collection_initializer = true:warning
|
||||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
|
||||||
dotnet_style_prefer_auto_properties = true:silent
|
dotnet_style_prefer_auto_properties = true:warning
|
||||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||||
dotnet_style_prefer_compound_assignment = true:silent
|
dotnet_style_prefer_compound_assignment = true:warning
|
||||||
|
|
||||||
#Style - null/type checks
|
#Style - null/type checks
|
||||||
dotnet_style_coalesce_expression = true:warning
|
dotnet_style_coalesce_expression = true:warning
|
||||||
dotnet_style_null_propagation = true:warning
|
dotnet_style_null_propagation = true:warning
|
||||||
csharp_style_pattern_matching_over_is_with_cast_check = true:silent
|
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
|
||||||
csharp_style_pattern_matching_over_as_with_null_check = true:silent
|
csharp_style_pattern_matching_over_as_with_null_check = true:warning
|
||||||
csharp_style_throw_expression = true:silent
|
csharp_style_throw_expression = true:silent
|
||||||
csharp_style_conditional_delegate_call = true:suggestion
|
csharp_style_conditional_delegate_call = true:warning
|
||||||
|
|
||||||
#Style - unused
|
#Style - unused
|
||||||
|
dotnet_style_readonly_field = true:silent
|
||||||
dotnet_code_quality_unused_parameters = non_public:silent
|
dotnet_code_quality_unused_parameters = non_public:silent
|
||||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||||
csharp_style_unused_value_assignment_preference = discard_variable:silent
|
csharp_style_unused_value_assignment_preference = discard_variable:warning
|
||||||
|
|
||||||
#Style - variable declaration
|
#Style - variable declaration
|
||||||
csharp_style_inlined_variable_declaration = true:silent
|
csharp_style_inlined_variable_declaration = true:warning
|
||||||
csharp_style_deconstructed_variable_declaration = true:silent
|
csharp_style_deconstructed_variable_declaration = true:warning
|
||||||
|
|
||||||
#Style - other C# 7.x features
|
#Style - other C# 7.x features
|
||||||
csharp_style_expression_bodied_local_functions = true:silent
|
|
||||||
dotnet_style_prefer_inferred_tuple_names = true:warning
|
dotnet_style_prefer_inferred_tuple_names = true:warning
|
||||||
csharp_prefer_simple_default_expression = true:warning
|
csharp_prefer_simple_default_expression = true:warning
|
||||||
csharp_style_pattern_local_over_anonymous_function = true:silent
|
csharp_style_pattern_local_over_anonymous_function = true:warning
|
||||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
||||||
|
|
||||||
|
#Style - C# 8 features
|
||||||
|
csharp_prefer_static_local_function = true:warning
|
||||||
|
csharp_prefer_simple_using_statement = true:silent
|
||||||
|
csharp_style_prefer_index_operator = false:none
|
||||||
|
csharp_style_prefer_range_operator = false:none
|
||||||
|
csharp_style_prefer_switch_expression = false:none
|
||||||
|
|
||||||
#Supressing roslyn built-in analyzers
|
#Supressing roslyn built-in analyzers
|
||||||
# Suppress: EC112
|
# Suppress: EC112
|
||||||
|
|
||||||
#Field can be readonly
|
|
||||||
dotnet_diagnostic.IDE0044.severity = silent
|
|
||||||
#Private method is unused
|
#Private method is unused
|
||||||
dotnet_diagnostic.IDE0051.severity = silent
|
dotnet_diagnostic.IDE0051.severity = silent
|
||||||
#Private member is unused
|
#Private member is unused
|
||||||
|
5
CodeAnalysis/BannedSymbols.txt
Normal file
5
CodeAnalysis/BannedSymbols.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
M:System.Object.Equals(System.Object,System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||||
|
M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||||
|
M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||||
|
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
||||||
|
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
@ -1,7 +1,8 @@
|
|||||||
<!-- Contains required properties for osu!framework projects. -->
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup Label="C#">
|
<PropertyGroup Label="C#">
|
||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
|
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
|
||||||
@ -14,12 +15,21 @@
|
|||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="Resources\**\*.*" />
|
<EmbeddedResource Include="Resources\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup Label="Code Analysis">
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="2.9.7" PrivateAssets="All" />
|
||||||
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Documentation">
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<!-- DeepEqual is not netstandard-compatible. This is fine since we run tests with .NET Framework anyway.
|
<!-- DeepEqual is not netstandard-compatible. This is fine since we run tests with .NET Framework anyway.
|
||||||
This is required due to https://github.com/NuGet/Home/issues/5740 -->
|
This is required due to https://github.com/NuGet/Home/issues/5740 -->
|
||||||
<NoWarn>NU1701</NoWarn>
|
<NoWarn>$(NoWarn);NU1701</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Nuget">
|
<PropertyGroup Label="Nuget">
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
<Authors>ppy Pty Ltd</Authors>
|
<Authors>ppy Pty Ltd</Authors>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<PackageProjectUrl>https://github.com/ppy/osu</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ppy/osu</PackageProjectUrl>
|
||||||
|
4
build.ps1 → InspectCode.ps1
Executable file → Normal file
4
build.ps1 → InspectCode.ps1
Executable file → Normal file
@ -22,6 +22,6 @@ if ($Experimental) { $cakeArguments += "-experimental" }
|
|||||||
$cakeArguments += $ScriptArgs
|
$cakeArguments += $ScriptArgs
|
||||||
|
|
||||||
dotnet tool restore
|
dotnet tool restore
|
||||||
dotnet cake ./build/build.cake --bootstrap
|
dotnet cake ./build/InspectCode.cake --bootstrap
|
||||||
dotnet cake ./build/build.cake $cakeArguments
|
dotnet cake ./build/InspectCode.cake $cakeArguments
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
16
README.md
16
README.md
@ -4,7 +4,10 @@
|
|||||||
|
|
||||||
# osu!
|
# osu!
|
||||||
|
|
||||||
[![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
|
[![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu)
|
||||||
|
[![GitHub release](https://img.shields.io/github/release/ppy/osu.svg)]()
|
||||||
|
[![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu)
|
||||||
|
[![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
|
||||||
|
|
||||||
Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename "osu!lazer". Pew pew.
|
Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename "osu!lazer". Pew pew.
|
||||||
|
|
||||||
@ -20,7 +23,8 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
|
|||||||
|
|
||||||
- A desktop platform with the [.NET Core SDK 3.0](https://www.microsoft.com/net/learn/get-started) or higher installed.
|
- A desktop platform with the [.NET Core SDK 3.0](https://www.microsoft.com/net/learn/get-started) or higher installed.
|
||||||
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
|
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
|
||||||
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
|
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore30&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
|
||||||
|
- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
|
||||||
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||||
|
|
||||||
## Running osu!
|
## Running osu!
|
||||||
@ -70,13 +74,19 @@ If you are not interested in debugging osu!, you can add `-c Release` to gain pe
|
|||||||
|
|
||||||
If the build fails, try to restore NuGet packages with `dotnet restore`.
|
If the build fails, try to restore NuGet packages with `dotnet restore`.
|
||||||
|
|
||||||
|
_Due to historical feature gap between .NET Core and Xamarin, running `dotnet` CLI from the root directory will not work for most commands. This can be resolved by specifying a target `.csproj` or the helper project at `build/Desktop.proj`. Configurations have been provided to work around this issue for all supported IDEs mentioned above._
|
||||||
|
|
||||||
### Testing with resource/framework modifications
|
### Testing with resource/framework modifications
|
||||||
|
|
||||||
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages.
|
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages.
|
||||||
|
|
||||||
### Code analysis
|
### Code analysis
|
||||||
|
|
||||||
Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under Windows due to [ReSharper CLI shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
|
Before committing your code, please run a code formatter. It can be achieved with `dotnet format` in command line, or `Format code` command in your IDE.
|
||||||
|
|
||||||
|
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
|
||||||
|
|
||||||
|
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`, which is [only supported on Windows](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
27
appveyor.yml
27
appveyor.yml
@ -1,6 +1,27 @@
|
|||||||
clone_depth: 1
|
clone_depth: 1
|
||||||
version: '{branch}-{build}'
|
version: '{branch}-{build}'
|
||||||
image: Visual Studio 2019
|
image: Visual Studio 2019
|
||||||
test: off
|
dotnet_csproj:
|
||||||
build_script:
|
patch: true
|
||||||
- cmd: PowerShell -Version 2.0 .\build.ps1
|
file: 'osu.Game\osu.Game.csproj' # Use wildcard when it's able to exclude Xamarin projects
|
||||||
|
version: '0.0.{build}'
|
||||||
|
cache:
|
||||||
|
- '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml'
|
||||||
|
before_build:
|
||||||
|
- ps: dotnet --info # Useful when version mismatch between CI and local
|
||||||
|
- ps: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
|
||||||
|
build:
|
||||||
|
project: osu.sln
|
||||||
|
parallel: true
|
||||||
|
verbosity: minimal
|
||||||
|
publish_nuget: true
|
||||||
|
after_build:
|
||||||
|
- ps: dotnet tool restore
|
||||||
|
- ps: dotnet format --dry-run --check
|
||||||
|
- ps: .\InspectCode.ps1
|
||||||
|
test:
|
||||||
|
assemblies:
|
||||||
|
except:
|
||||||
|
- '**\*Android*'
|
||||||
|
- '**\*iOS*'
|
||||||
|
- 'build\**\*'
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
clone_depth: 1
|
clone_depth: 1
|
||||||
version: '{build}'
|
version: '{build}'
|
||||||
image: Visual Studio 2019
|
image: Visual Studio 2019
|
||||||
|
dotnet_csproj:
|
||||||
|
patch: true
|
||||||
|
file: 'osu.Game\osu.Game.csproj' # Use wildcard when it's able to exclude Xamarin projects
|
||||||
|
version: $(APPVEYOR_REPO_TAG_NAME)
|
||||||
|
before_build:
|
||||||
|
- ps: dotnet --info # Useful when version mismatch between CI and local
|
||||||
|
- ps: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
|
||||||
test: off
|
test: off
|
||||||
skip_non_tags: true
|
skip_non_tags: true
|
||||||
build_script:
|
configuration: Release
|
||||||
- cmd: PowerShell -Version 2.0 .\build.ps1
|
build:
|
||||||
|
project: build\Desktop.proj # Skipping Xamarin Release that's slow and covered by fastlane
|
||||||
|
parallel: true
|
||||||
|
verbosity: minimal
|
||||||
|
publish_nuget: true
|
||||||
deploy:
|
deploy:
|
||||||
- provider: Environment
|
- provider: Environment
|
||||||
name: nuget
|
name: nuget
|
||||||
|
17
build.sh
17
build.sh
@ -1,17 +0,0 @@
|
|||||||
echo "Installing Cake.Tool..."
|
|
||||||
dotnet tool restore
|
|
||||||
|
|
||||||
# Parse arguments.
|
|
||||||
CAKE_ARGUMENTS=()
|
|
||||||
for i in "$@"; do
|
|
||||||
case $1 in
|
|
||||||
-s|--script) SCRIPT="$2"; shift ;;
|
|
||||||
--) shift; CAKE_ARGUMENTS+=("$@"); break ;;
|
|
||||||
*) CAKE_ARGUMENTS+=("$1") ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Running build script..."
|
|
||||||
dotnet cake ./build/build.cake --bootstrap
|
|
||||||
dotnet cake ./build/build.cake "${CAKE_ARGUMENTS[@]}"
|
|
@ -7,45 +7,29 @@ var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
|
|||||||
// ARGUMENTS
|
// ARGUMENTS
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
var target = Argument("target", "Build");
|
var target = Argument("target", "CodeAnalysis");
|
||||||
var configuration = Argument("configuration", "Release");
|
var configuration = Argument("configuration", "Release");
|
||||||
|
|
||||||
var rootDirectory = new DirectoryPath("..");
|
var rootDirectory = new DirectoryPath("..");
|
||||||
var sln = rootDirectory.CombineWithFilePath("osu.sln");
|
var sln = rootDirectory.CombineWithFilePath("osu.sln");
|
||||||
var desktopBuilds = rootDirectory.CombineWithFilePath("build/Desktop.proj");
|
|
||||||
var desktopSlnf = rootDirectory.CombineWithFilePath("osu.Desktop.slnf");
|
var desktopSlnf = rootDirectory.CombineWithFilePath("osu.Desktop.slnf");
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// TASKS
|
// TASKS
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Task("Compile")
|
// windows only because both inspectcode and nvika depend on net45
|
||||||
.Does(() => {
|
|
||||||
DotNetCoreBuild(desktopBuilds.FullPath, new DotNetCoreBuildSettings {
|
|
||||||
Configuration = configuration,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Task("Test")
|
|
||||||
.IsDependentOn("Compile")
|
|
||||||
.Does(() => {
|
|
||||||
var testAssemblies = GetFiles(rootDirectory + "/**/*.Tests/bin/**/*.Tests.dll");
|
|
||||||
|
|
||||||
DotNetCoreVSTest(testAssemblies, new DotNetCoreVSTestSettings {
|
|
||||||
Logger = AppVeyor.IsRunningOnAppVeyor ? "Appveyor" : $"trx",
|
|
||||||
Parallel = true,
|
|
||||||
ToolTimeout = TimeSpan.FromMinutes(10),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// windows only because both inspectcore and nvika depend on net45
|
|
||||||
Task("InspectCode")
|
Task("InspectCode")
|
||||||
.WithCriteria(IsRunningOnWindows())
|
.WithCriteria(IsRunningOnWindows())
|
||||||
.IsDependentOn("Compile")
|
|
||||||
.Does(() => {
|
.Does(() => {
|
||||||
InspectCode(desktopSlnf, new InspectCodeSettings {
|
InspectCode(desktopSlnf, new InspectCodeSettings {
|
||||||
CachesHome = "inspectcode",
|
CachesHome = "inspectcode",
|
||||||
OutputFile = "inspectcodereport.xml",
|
OutputFile = "inspectcodereport.xml",
|
||||||
|
ArgumentCustomization = arg => {
|
||||||
|
if (AppVeyor.IsRunningOnAppVeyor) // Don't flood CI output
|
||||||
|
arg.Append("--verbosity:WARN");
|
||||||
|
return arg;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
int returnCode = StartProcess(nVikaToolPath, $@"parsereport ""inspectcodereport.xml"" --treatwarningsaserrors");
|
int returnCode = StartProcess(nVikaToolPath, $@"parsereport ""inspectcodereport.xml"" --treatwarningsaserrors");
|
||||||
@ -61,13 +45,8 @@ Task("CodeFileSanity")
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("DotnetFormat")
|
Task("CodeAnalysis")
|
||||||
.Does(() => DotNetCoreTool(sln.FullPath, "format", "--dry-run --check"));
|
|
||||||
|
|
||||||
Task("Build")
|
|
||||||
.IsDependentOn("CodeFileSanity")
|
.IsDependentOn("CodeFileSanity")
|
||||||
.IsDependentOn("DotnetFormat")
|
.IsDependentOn("InspectCode");
|
||||||
.IsDependentOn("InspectCode")
|
|
||||||
.IsDependentOn("Test");
|
|
||||||
|
|
||||||
RunTarget(target);
|
RunTarget(target);
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"msbuild-sdks": {
|
"msbuild-sdks": {
|
||||||
"Microsoft.Build.Traversal": "2.0.19"
|
"Microsoft.Build.Traversal": "2.0.24"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<LangVersion>8.0</LangVersion>
|
||||||
<OutputPath>bin\$(Configuration)</OutputPath>
|
<OutputPath>bin\$(Configuration)</OutputPath>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
@ -10,7 +11,7 @@
|
|||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||||
<AndroidApplication>True</AndroidApplication>
|
<AndroidApplication>True</AndroidApplication>
|
||||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||||
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
|
||||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
|
||||||
@ -53,6 +54,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1112.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1126.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="sh.ppy.osulazer" android:installLocation="auto" android:versionName="0.1.0">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="sh.ppy.osulazer" android:installLocation="auto" android:versionName="0.1.0">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
@ -112,14 +112,14 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
protected override string LocateBasePath()
|
protected override string LocateBasePath()
|
||||||
{
|
{
|
||||||
bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
||||||
|
|
||||||
string stableInstallPath;
|
string stableInstallPath;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
||||||
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
|
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
|
||||||
|
|
||||||
if (checkExists(stableInstallPath))
|
if (checkExists(stableInstallPath))
|
||||||
return stableInstallPath;
|
return stableInstallPath;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch.Tests.Android" android:installLocation="auto">
|
<!-- using a different name because package name cannot contain 'catch' -->
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto">
|
||||||
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -11,12 +11,12 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.Blue
|
Colour = Color4.Blue
|
||||||
},
|
},
|
||||||
new SpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = "custom"
|
Text = "custom"
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using System;
|
|||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||||
{
|
{
|
||||||
@ -22,15 +23,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
|
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
var curveData = obj as IHasCurve;
|
|
||||||
var positionData = obj as IHasXPosition;
|
var positionData = obj as IHasXPosition;
|
||||||
var comboData = obj as IHasCombo;
|
var comboData = obj as IHasCombo;
|
||||||
var endTime = obj as IHasEndTime;
|
|
||||||
var legacyOffset = obj as IHasLegacyLastTickOffset;
|
|
||||||
|
|
||||||
if (curveData != null)
|
switch (obj)
|
||||||
{
|
{
|
||||||
yield return new JuiceStream
|
case IHasCurve curveData:
|
||||||
|
return new JuiceStream
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
@ -40,30 +39,28 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
|
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset ?? 0
|
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0
|
||||||
};
|
}.Yield();
|
||||||
}
|
|
||||||
else if (endTime != null)
|
case IHasEndTime endTime:
|
||||||
{
|
return new BananaShower
|
||||||
yield return new BananaShower
|
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
Duration = endTime.Duration,
|
Duration = endTime.Duration,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
};
|
}.Yield();
|
||||||
}
|
|
||||||
else
|
default:
|
||||||
{
|
return new Fruit
|
||||||
yield return new Fruit
|
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH
|
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH
|
||||||
};
|
}.Yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osuTK;
|
|
||||||
using osu.Game.Rulesets.Catch.MathUtils;
|
using osu.Game.Rulesets.Catch.MathUtils;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
@ -78,7 +77,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
catchObject.XOffset = 0;
|
catchObject.XOffset = 0;
|
||||||
|
|
||||||
if (catchObject is TinyDroplet)
|
if (catchObject is TinyDroplet)
|
||||||
catchObject.XOffset = MathHelper.Clamp(rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH, -catchObject.X, 1 - catchObject.X);
|
catchObject.XOffset = Math.Clamp(rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH, -catchObject.X, 1 - catchObject.X);
|
||||||
else if (catchObject is Droplet)
|
else if (catchObject is Droplet)
|
||||||
rng.Next(); // osu!stable retrieved a random droplet rotation
|
rng.Next(); // osu!stable retrieved a random droplet rotation
|
||||||
}
|
}
|
||||||
@ -230,7 +229,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
currentObject.DistanceToHyperDash = distanceToHyper;
|
currentObject.DistanceToHyperDash = distanceToHyper;
|
||||||
lastExcess = MathHelper.Clamp(distanceToHyper, 0, halfCatcherWidth);
|
lastExcess = Math.Clamp(distanceToHyper, 0, halfCatcherWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastDirection = thisDirection;
|
lastDirection = thisDirection;
|
||||||
|
@ -10,7 +10,6 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Scoring.Legacy;
|
using osu.Game.Scoring.Legacy;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||||
{
|
{
|
||||||
@ -48,55 +47,53 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// We are heavily relying on aim in catch the beat
|
// We are heavily relying on aim in catch the beat
|
||||||
double value = Math.Pow(5.0f * Math.Max(1.0f, Attributes.StarRating / 0.0049f) - 4.0f, 2.0f) / 100000.0f;
|
double value = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0049) - 4.0, 2.0) / 100000.0;
|
||||||
|
|
||||||
// Longer maps are worth more. "Longer" means how many hits there are which can contribute to combo
|
// Longer maps are worth more. "Longer" means how many hits there are which can contribute to combo
|
||||||
int numTotalHits = totalComboHits();
|
int numTotalHits = totalComboHits();
|
||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
float lengthBonus =
|
double lengthBonus =
|
||||||
0.95f + 0.4f * Math.Min(1.0f, numTotalHits / 3000.0f) +
|
0.95 + 0.4 * Math.Min(1.0, numTotalHits / 3000.0) +
|
||||||
(numTotalHits > 3000 ? (float)Math.Log10(numTotalHits / 3000.0f) * 0.5f : 0.0f);
|
(numTotalHits > 3000 ? Math.Log10(numTotalHits / 3000.0) * 0.5 : 0.0);
|
||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
value *= lengthBonus;
|
value *= lengthBonus;
|
||||||
|
|
||||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||||
value *= Math.Pow(0.97f, misses);
|
value *= Math.Pow(0.97, misses);
|
||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
float beatmapMaxCombo = Attributes.MaxCombo;
|
if (Attributes.MaxCombo > 0)
|
||||||
if (beatmapMaxCombo > 0)
|
value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
||||||
value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
|
||||||
|
|
||||||
float approachRate = (float)Attributes.ApproachRate;
|
double approachRateFactor = 1.0;
|
||||||
float approachRateFactor = 1.0f;
|
if (Attributes.ApproachRate > 9.0)
|
||||||
if (approachRate > 9.0f)
|
approachRateFactor += 0.1 * (Attributes.ApproachRate - 9.0); // 10% for each AR above 9
|
||||||
approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9
|
else if (Attributes.ApproachRate < 8.0)
|
||||||
else if (approachRate < 8.0f)
|
approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); // 2.5% for each AR below 8
|
||||||
approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8
|
|
||||||
|
|
||||||
value *= approachRateFactor;
|
value *= approachRateFactor;
|
||||||
|
|
||||||
if (mods.Any(m => m is ModHidden))
|
if (mods.Any(m => m is ModHidden))
|
||||||
// Hiddens gives nothing on max approach rate, and more the lower it is
|
// Hiddens gives nothing on max approach rate, and more the lower it is
|
||||||
value *= 1.05f + 0.075f * (10.0f - Math.Min(10.0f, approachRate)); // 7.5% for each AR below 10
|
value *= 1.05 + 0.075 * (10.0 - Math.Min(10.0, Attributes.ApproachRate)); // 7.5% for each AR below 10
|
||||||
|
|
||||||
if (mods.Any(m => m is ModFlashlight))
|
if (mods.Any(m => m is ModFlashlight))
|
||||||
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
||||||
value *= 1.35f * lengthBonus;
|
value *= 1.35 * lengthBonus;
|
||||||
|
|
||||||
// Scale the aim value with accuracy _slightly_
|
// Scale the aim value with accuracy _slightly_
|
||||||
value *= Math.Pow(accuracy(), 5.5f);
|
value *= Math.Pow(accuracy(), 5.5);
|
||||||
|
|
||||||
// Custom multipliers for NoFail. SpunOut is not applicable.
|
// Custom multipliers for NoFail. SpunOut is not applicable.
|
||||||
if (mods.Any(m => m is ModNoFail))
|
if (mods.Any(m => m is ModNoFail))
|
||||||
value *= 0.90f;
|
value *= 0.90;
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float accuracy() => totalHits() == 0 ? 0 : MathHelper.Clamp((float)totalSuccessfulHits() / totalHits(), 0f, 1f);
|
private float accuracy() => totalHits() == 0 ? 0 : Math.Clamp((float)totalSuccessfulHits() / totalHits(), 0, 1);
|
||||||
private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed;
|
private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed;
|
||||||
private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit;
|
private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit;
|
||||||
private int totalComboHits() => misses + ticksHit + fruitsHit;
|
private int totalComboHits() => misses + ticksHit + fruitsHit;
|
||||||
|
@ -6,7 +6,6 @@ using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
|||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||||
{
|
{
|
||||||
@ -31,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
if (lastPlayerPosition == null)
|
if (lastPlayerPosition == null)
|
||||||
lastPlayerPosition = catchCurrent.LastNormalizedPosition;
|
lastPlayerPosition = catchCurrent.LastNormalizedPosition;
|
||||||
|
|
||||||
float playerPosition = MathHelper.Clamp(
|
float playerPosition = Math.Clamp(
|
||||||
lastPlayerPosition.Value,
|
lastPlayerPosition.Value,
|
||||||
catchCurrent.NormalizedPosition - (normalized_hitobject_radius - absolute_player_positioning_error),
|
catchCurrent.NormalizedPosition - (normalized_hitobject_radius - absolute_player_positioning_error),
|
||||||
catchCurrent.NormalizedPosition + (normalized_hitobject_radius - absolute_player_positioning_error)
|
catchCurrent.NormalizedPosition + (normalized_hitobject_radius - absolute_player_positioning_error)
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
|
|
||||||
protected override void UpdateStateTransforms(ArmedState state)
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
{
|
{
|
||||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
var endTime = HitObject.GetEndTime();
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(endTime, true))
|
using (BeginAbsoluteSequence(endTime, true))
|
||||||
{
|
{
|
||||||
|
@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
|
|
||||||
const float small_pulp = large_pulp_3 / 2;
|
const float small_pulp = large_pulp_3 / 2;
|
||||||
|
|
||||||
Vector2 positionAt(float angle, float distance) => new Vector2(
|
static Vector2 positionAt(float angle, float distance) => new Vector2(
|
||||||
distance * (float)Math.Sin(angle * Math.PI / 180),
|
distance * MathF.Sin(angle * MathF.PI / 180),
|
||||||
distance * (float)Math.Cos(angle * Math.PI / 180));
|
distance * MathF.Cos(angle * MathF.PI / 180));
|
||||||
|
|
||||||
switch (representation)
|
switch (representation)
|
||||||
{
|
{
|
||||||
@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
border.Alpha = (float)Math.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 colourForRepresentation(FruitVisualRepresentation representation)
|
private Color4 colourForRepresentation(FruitVisualRepresentation representation)
|
||||||
|
@ -116,13 +116,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public double Duration => EndTime - StartTime;
|
public double Duration => EndTime - StartTime;
|
||||||
|
|
||||||
private SliderPath path;
|
public SliderPath Path { get; set; }
|
||||||
|
|
||||||
public SliderPath Path
|
|
||||||
{
|
|
||||||
get => path;
|
|
||||||
set => path = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double Distance => Path.Distance;
|
public double Distance => Path.Distance;
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
var additive = createCatcherSprite();
|
var additive = createCatcherSprite();
|
||||||
|
|
||||||
additive.Anchor = Anchor;
|
additive.Anchor = Anchor;
|
||||||
additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
|
additive.OriginPosition += new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
|
||||||
additive.Position = Position;
|
additive.Position = Position;
|
||||||
additive.Scale = Scale;
|
additive.Scale = Scale;
|
||||||
additive.Colour = HyperDashing ? Color4.Red : Color4.White;
|
additive.Colour = HyperDashing ? Color4.Red : Color4.White;
|
||||||
@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
fruit.Y -= RNG.NextSingle() * diff;
|
fruit.Y -= RNG.NextSingle() * diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
fruit.X = MathHelper.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2);
|
fruit.X = Math.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2);
|
||||||
|
|
||||||
caughtFruit.Add(fruit);
|
caughtFruit.Add(fruit);
|
||||||
}
|
}
|
||||||
@ -378,7 +378,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
double speed = BASE_SPEED * dashModifier * hyperDashModifier;
|
double speed = BASE_SPEED * dashModifier * hyperDashModifier;
|
||||||
|
|
||||||
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
|
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
|
||||||
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1);
|
X = (float)Math.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1);
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Description>catch the fruit. to the beat.</Description>
|
<Description>catch the fruit. to the beat.</Description>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -9,7 +9,6 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
@ -27,7 +26,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
yield return new ConvertValue
|
yield return new ConvertValue
|
||||||
{
|
{
|
||||||
StartTime = hitObject.StartTime,
|
StartTime = hitObject.StartTime,
|
||||||
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
EndTime = hitObject.GetEndTime(),
|
||||||
Column = ((ManiaHitObject)hitObject).Column
|
Column = ((ManiaHitObject)hitObject).Column
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
if (TargetColumns >= 10)
|
if (TargetColumns >= 10)
|
||||||
{
|
{
|
||||||
TargetColumns = TargetColumns / 2;
|
TargetColumns /= 2;
|
||||||
Dual = true;
|
Dual = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
{
|
{
|
||||||
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
||||||
|
|
||||||
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate);
|
||||||
Random = new FastRandom(seed);
|
Random = new FastRandom(seed);
|
||||||
|
|
||||||
return base.ConvertBeatmap(original);
|
return base.ConvertBeatmap(original);
|
||||||
@ -156,37 +156,44 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// <returns>The hit objects generated.</returns>
|
/// <returns>The hit objects generated.</returns>
|
||||||
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, IBeatmap originalBeatmap)
|
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, IBeatmap originalBeatmap)
|
||||||
{
|
{
|
||||||
var endTimeData = original as IHasEndTime;
|
|
||||||
var distanceData = original as IHasDistance;
|
|
||||||
var positionData = original as IHasPosition;
|
|
||||||
|
|
||||||
Patterns.PatternGenerator conversion = null;
|
Patterns.PatternGenerator conversion = null;
|
||||||
|
|
||||||
if (distanceData != null)
|
switch (original)
|
||||||
|
{
|
||||||
|
case IHasDistance _:
|
||||||
{
|
{
|
||||||
var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
|
var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
|
||||||
conversion = generator;
|
conversion = generator;
|
||||||
|
|
||||||
|
var positionData = original as IHasPosition;
|
||||||
|
|
||||||
for (double time = original.StartTime; !Precision.DefinitelyBigger(time, generator.EndTime); time += generator.SegmentDuration)
|
for (double time = original.StartTime; !Precision.DefinitelyBigger(time, generator.EndTime); time += generator.SegmentDuration)
|
||||||
{
|
{
|
||||||
recordNote(time, positionData?.Position ?? Vector2.Zero);
|
recordNote(time, positionData?.Position ?? Vector2.Zero);
|
||||||
computeDensity(time);
|
computeDensity(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (endTimeData != null)
|
|
||||||
|
case IHasEndTime endTimeData:
|
||||||
{
|
{
|
||||||
conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);
|
conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);
|
||||||
|
|
||||||
recordNote(endTimeData.EndTime, new Vector2(256, 192));
|
recordNote(endTimeData.EndTime, new Vector2(256, 192));
|
||||||
computeDensity(endTimeData.EndTime);
|
computeDensity(endTimeData.EndTime);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (positionData != null)
|
|
||||||
|
case IHasPosition positionData:
|
||||||
{
|
{
|
||||||
computeDensity(original.StartTime);
|
computeDensity(original.StartTime);
|
||||||
|
|
||||||
conversion = new HitObjectPatternGenerator(Random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
conversion = new HitObjectPatternGenerator(Random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
||||||
|
|
||||||
recordNote(original.StartTime, positionData.Position);
|
recordNote(original.StartTime, positionData.Position);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversion == null)
|
if (conversion == null)
|
||||||
@ -219,14 +226,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
private Pattern generate()
|
private Pattern generate()
|
||||||
{
|
{
|
||||||
var endTimeData = HitObject as IHasEndTime;
|
|
||||||
var positionData = HitObject as IHasXPosition;
|
var positionData = HitObject as IHasXPosition;
|
||||||
|
|
||||||
int column = GetColumn(positionData?.X ?? 0);
|
int column = GetColumn(positionData?.X ?? 0);
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
if (endTimeData != null)
|
if (HitObject is IHasEndTime endTimeData)
|
||||||
{
|
{
|
||||||
pattern.Add(new HoldNote
|
pattern.Add(new HoldNote
|
||||||
{
|
{
|
||||||
@ -237,7 +243,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
|
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (positionData != null)
|
else if (HitObject is IHasXPosition)
|
||||||
{
|
{
|
||||||
pattern.Add(new Note
|
pattern.Add(new Note
|
||||||
{
|
{
|
||||||
@ -257,9 +263,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
||||||
{
|
{
|
||||||
var curveData = HitObject as IHasCurve;
|
if (!(HitObject is IHasCurve curveData))
|
||||||
|
|
||||||
if (curveData == null)
|
|
||||||
return HitObject.Samples;
|
return HitObject.Samples;
|
||||||
|
|
||||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
||||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
foreach (var obj in originalPattern.HitObjects)
|
foreach (var obj in originalPattern.HitObjects)
|
||||||
{
|
{
|
||||||
if (!Precision.AlmostEquals(EndTime, (obj as IHasEndTime)?.EndTime ?? obj.StartTime))
|
if (!Precision.AlmostEquals(EndTime, obj.GetEndTime()))
|
||||||
intermediatePattern.Add(obj);
|
intermediatePattern.Add(obj);
|
||||||
else
|
else
|
||||||
endTimePattern.Add(obj);
|
endTimePattern.Add(obj);
|
||||||
@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH;
|
static bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH;
|
||||||
|
|
||||||
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
|
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
|
||||||
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
||||||
@ -474,9 +474,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
||||||
{
|
{
|
||||||
var curveData = HitObject as IHasCurve;
|
if (!(HitObject is IHasCurve curveData))
|
||||||
|
|
||||||
if (curveData == null)
|
|
||||||
return HitObject.Samples;
|
return HitObject.Samples;
|
||||||
|
|
||||||
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Rulesets.Mania.MathUtils;
|
|||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
@ -88,15 +89,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
public override IEnumerable<Pattern> Generate()
|
public override IEnumerable<Pattern> Generate()
|
||||||
{
|
{
|
||||||
yield return generate();
|
Pattern generateCore()
|
||||||
}
|
|
||||||
|
|
||||||
private Pattern generate()
|
|
||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (TotalColumns == 1)
|
if (TotalColumns == 1)
|
||||||
{
|
{
|
||||||
addToPattern(pattern, 0);
|
addToPattern(pattern, 0);
|
||||||
@ -168,54 +164,56 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.KeepSingle))
|
if (convertType.HasFlag(PatternType.KeepSingle))
|
||||||
return pattern = generateRandomNotes(1);
|
return generateRandomNotes(1);
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.Mirror))
|
if (convertType.HasFlag(PatternType.Mirror))
|
||||||
{
|
{
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
return pattern = generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
||||||
if (ConversionDifficulty > 4)
|
if (ConversionDifficulty > 4)
|
||||||
return pattern = generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
return generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
||||||
|
|
||||||
return pattern = generateRandomPatternWithMirrored(0.12, 0, 0);
|
return generateRandomPatternWithMirrored(0.12, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlag(PatternType.LowProbability))
|
||||||
return pattern = generateRandomPattern(0.78, 0.42, 0, 0);
|
return generateRandomPattern(0.78, 0.42, 0, 0);
|
||||||
|
|
||||||
return pattern = generateRandomPattern(1, 0.62, 0, 0);
|
return generateRandomPattern(1, 0.62, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 4)
|
if (ConversionDifficulty > 4)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlag(PatternType.LowProbability))
|
||||||
return pattern = generateRandomPattern(0.35, 0.08, 0, 0);
|
return generateRandomPattern(0.35, 0.08, 0, 0);
|
||||||
|
|
||||||
return pattern = generateRandomPattern(0.52, 0.15, 0, 0);
|
return generateRandomPattern(0.52, 0.15, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 2)
|
if (ConversionDifficulty > 2)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlag(PatternType.LowProbability))
|
||||||
return pattern = generateRandomPattern(0.18, 0, 0, 0);
|
return generateRandomPattern(0.18, 0, 0, 0);
|
||||||
|
|
||||||
return pattern = generateRandomPattern(0.45, 0, 0, 0);
|
return generateRandomPattern(0.45, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern = generateRandomPattern(0, 0, 0, 0);
|
return generateRandomPattern(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
var p = generateCore();
|
||||||
foreach (var obj in pattern.HitObjects)
|
|
||||||
|
foreach (var obj in p.HitObjects)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.Stair) && obj.Column == TotalColumns - 1)
|
if (convertType.HasFlag(PatternType.Stair) && obj.Column == TotalColumns - 1)
|
||||||
StairType = PatternType.ReverseStair;
|
StairType = PatternType.ReverseStair;
|
||||||
if (convertType.HasFlag(PatternType.ReverseStair) && obj.Column == RandomStart)
|
if (convertType.HasFlag(PatternType.ReverseStair) && obj.Column == RandomStart)
|
||||||
StairType = PatternType.Stair;
|
StairType = PatternType.Stair;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return p.Yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -303,8 +301,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
bool addToCentre;
|
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out var addToCentre);
|
||||||
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre);
|
|
||||||
|
|
||||||
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
|
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
|
||||||
int nextColumn = GetRandomColumn(upperBound: columnLimit);
|
int nextColumn = GetRandomColumn(upperBound: columnLimit);
|
||||||
@ -384,8 +381,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <returns>The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count.</returns>
|
/// <returns>The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count.</returns>
|
||||||
private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre)
|
private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre)
|
||||||
{
|
{
|
||||||
addToCentre = false;
|
|
||||||
|
|
||||||
switch (TotalColumns)
|
switch (TotalColumns)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -7,7 +7,6 @@ using JetBrains.Annotations;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
@ -54,11 +53,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
if (allowSpecial && TotalColumns == 8)
|
if (allowSpecial && TotalColumns == 8)
|
||||||
{
|
{
|
||||||
const float local_x_divisor = 512f / 7;
|
const float local_x_divisor = 512f / 7;
|
||||||
return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1;
|
return Math.Clamp((int)MathF.Floor(position / local_x_divisor), 0, 6) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
float localXDivisor = 512f / TotalColumns;
|
float localXDivisor = 512f / TotalColumns;
|
||||||
return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, TotalColumns - 1);
|
return Math.Clamp((int)MathF.Floor(position / localXDivisor), 0, TotalColumns - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -113,7 +112,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
drainTime = 10000;
|
drainTime = 10000;
|
||||||
|
|
||||||
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
||||||
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
|
conversionDifficulty = ((difficulty.DrainRate + Math.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
|
||||||
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
||||||
|
|
||||||
return conversionDifficulty.Value;
|
return conversionDifficulty.Value;
|
||||||
@ -139,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <param name="nextColumn">A function to retrieve the next column. If null, a randomisation scheme will be used.</param>
|
/// <param name="nextColumn">A function to retrieve the next column. If null, a randomisation scheme will be used.</param>
|
||||||
/// <param name="validation">A function to perform additional validation checks to determine if a column is a valid candidate for a <see cref="HitObject"/>.</param>
|
/// <param name="validation">A function to perform additional validation checks to determine if a column is a valid candidate for a <see cref="HitObject"/>.</param>
|
||||||
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
|
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
|
||||||
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
|
/// <param name="upperBound">The maximum column index. If null, <see cref="Patterns.PatternGenerator.TotalColumns">TotalColumns</see> is used.</param>
|
||||||
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
|
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
|
||||||
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
|
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
|
||||||
/// <returns>A column which has passed the <paramref name="validation"/> check and for which there are no
|
/// <returns>A column which has passed the <paramref name="validation"/> check and for which there are no
|
||||||
@ -148,9 +147,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
protected int FindAvailableColumn(int initialColumn, int? lowerBound = null, int? upperBound = null, Func<int, int> nextColumn = null, [InstantHandle] Func<int, bool> validation = null,
|
protected int FindAvailableColumn(int initialColumn, int? lowerBound = null, int? upperBound = null, Func<int, int> nextColumn = null, [InstantHandle] Func<int, bool> validation = null,
|
||||||
params Pattern[] patterns)
|
params Pattern[] patterns)
|
||||||
{
|
{
|
||||||
lowerBound = lowerBound ?? RandomStart;
|
lowerBound ??= RandomStart;
|
||||||
upperBound = upperBound ?? TotalColumns;
|
upperBound ??= TotalColumns;
|
||||||
nextColumn = nextColumn ?? (_ => GetRandomColumn(lowerBound, upperBound));
|
nextColumn ??= (_ => GetRandomColumn(lowerBound, upperBound));
|
||||||
|
|
||||||
// Check for the initial column
|
// Check for the initial column
|
||||||
if (isValid(initialColumn))
|
if (isValid(initialColumn))
|
||||||
@ -184,7 +183,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// Returns a random column index in the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
/// Returns a random column index in the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
|
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
|
||||||
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
|
/// <param name="upperBound">The maximum column index. If null, <see cref="Patterns.PatternGenerator.TotalColumns"/> is used.</param>
|
||||||
protected int GetRandomColumn(int? lowerBound = null, int? upperBound = null) => Random.Next(lowerBound ?? RandomStart, upperBound ?? TotalColumns);
|
protected int GetRandomColumn(int? lowerBound = null, int? upperBound = null) => Random.Next(lowerBound ?? RandomStart, upperBound ?? TotalColumns);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
@ -9,7 +10,6 @@ using osu.Game.Rulesets.Mania.Objects;
|
|||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit
|
namespace osu.Game.Rulesets.Mania.Edit
|
||||||
{
|
{
|
||||||
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
|
|
||||||
// Flip the vertical coordinate space when scrolling downwards
|
// Flip the vertical coordinate space when scrolling downwards
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
||||||
targetPosition = targetPosition - referenceParent.DrawHeight;
|
targetPosition -= referenceParent.DrawHeight;
|
||||||
|
|
||||||
float movementDelta = targetPosition - reference.DrawableObject.Position.Y;
|
float movementDelta = targetPosition - reference.DrawableObject.Position.Y;
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
maxColumn = obj.Column;
|
maxColumn = obj.Column;
|
||||||
}
|
}
|
||||||
|
|
||||||
columnDelta = MathHelper.Clamp(columnDelta, -minColumn, composer.TotalColumns - 1 - maxColumn);
|
columnDelta = Math.Clamp(columnDelta, -minColumn, composer.TotalColumns - 1 - maxColumn);
|
||||||
|
|
||||||
foreach (var obj in SelectedHitObjects.OfType<ManiaHitObject>())
|
foreach (var obj in SelectedHitObjects.OfType<ManiaHitObject>())
|
||||||
obj.Column += columnDelta;
|
obj.Column += columnDelta;
|
||||||
|
@ -6,7 +6,6 @@ using System.Linq;
|
|||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Replays
|
namespace osu.Game.Rulesets.Mania.Replays
|
||||||
@ -84,7 +83,7 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
var currentObject = Beatmap.HitObjects[i];
|
var currentObject = Beatmap.HitObjects[i];
|
||||||
var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button
|
var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button
|
||||||
|
|
||||||
double endTime = (currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime;
|
double endTime = currentObject.GetEndTime();
|
||||||
|
|
||||||
bool canDelayKeyUp = nextObjectInColumn == null ||
|
bool canDelayKeyUp = nextObjectInColumn == null ||
|
||||||
nextObjectInColumn.StartTime > endTime + RELEASE_DELAY;
|
nextObjectInColumn.StartTime > endTime + RELEASE_DELAY;
|
||||||
|
@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
foreach (var stage in stages)
|
foreach (var stage in stages)
|
||||||
{
|
{
|
||||||
sum = sum + stage.Columns.Count;
|
sum += stage.Columns.Count;
|
||||||
if (sum > column)
|
if (sum > column)
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Description>smash the keys. to the beat.</Description>
|
<Description>smash the keys. to the beat.</Description>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
@ -41,10 +40,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConvertValue createConvertValue(OsuHitObject obj) => new ConvertValue
|
static ConvertValue createConvertValue(OsuHitObject obj) => new ConvertValue
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
EndTime = (obj as IHasEndTime)?.EndTime ?? obj.StartTime,
|
EndTime = obj.GetEndTime(),
|
||||||
X = obj.StackedPosition.X,
|
X = obj.StackedPosition.X,
|
||||||
Y = obj.StackedPosition.Y
|
Y = obj.StackedPosition.Y
|
||||||
};
|
};
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
||||||
{
|
{
|
||||||
positionOffset = positionOffset ?? Vector2.Zero;
|
positionOffset ??= Vector2.Zero;
|
||||||
|
|
||||||
var circle = new HitCircle
|
var circle = new HitCircle
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,11 @@ using osu.Game.Audio;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
@ -75,14 +77,14 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin);
|
protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin);
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) => new CustomSkinWorkingBeatmap(beatmap, Clock, audio, testBeatmapSkin);
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, audio, testBeatmapSkin);
|
||||||
|
|
||||||
public class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
|
public class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
|
||||||
{
|
{
|
||||||
private readonly ISkinSource skin;
|
private readonly ISkinSource skin;
|
||||||
|
|
||||||
public CustomSkinWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock frameBasedClock, AudioManager audio, ISkinSource skin)
|
public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock frameBasedClock, AudioManager audio, ISkinSource skin)
|
||||||
: base(beatmap, frameBasedClock, audio)
|
: base(beatmap, storyboard, frameBasedClock, audio)
|
||||||
{
|
{
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
}
|
}
|
||||||
@ -124,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
if (!enabled) return null;
|
if (!enabled) return null;
|
||||||
|
|
||||||
return new SpriteText
|
return new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = identifier,
|
Text = identifier,
|
||||||
Font = OsuFont.Default.With(size: 30),
|
Font = OsuFont.Default.With(size: 30),
|
||||||
|
@ -148,9 +148,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<RepeatPoint>().All(assertSamples));
|
AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<RepeatPoint>().All(assertSamples));
|
||||||
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
|
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
|
||||||
|
|
||||||
bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
|
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
|
||||||
|
|
||||||
bool assertSamples(HitObject hitObject)
|
static bool assertSamples(HitObject hitObject)
|
||||||
{
|
{
|
||||||
return hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)
|
return hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)
|
||||||
&& hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
&& hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||||
@ -183,8 +183,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<RepeatPoint>().All(assertSamples));
|
AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<RepeatPoint>().All(assertSamples));
|
||||||
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
|
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
|
||||||
|
|
||||||
bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
|
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
|
||||||
bool assertSamples(HitObject hitObject) => hitObject.Samples.All(s => s.Name != HitSampleInfo.HIT_CLAP && s.Name != HitSampleInfo.HIT_WHISTLE);
|
|
||||||
|
static bool assertSamples(HitObject hitObject) => hitObject.Samples.All(s => s.Name != HitSampleInfo.HIT_CLAP && s.Name != HitSampleInfo.HIT_WHISTLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||||
@ -379,8 +380,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||||
{
|
{
|
||||||
var osuObject = judgedObject as DrawableOsuHitObject;
|
if (!(judgedObject is DrawableOsuHitObject osuObject))
|
||||||
if (osuObject == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OsuSpriteText text;
|
OsuSpriteText text;
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Tests.Visual;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
using static osu.Game.Tests.Visual.OsuTestScene.ClockBackedTestWorkingBeatmap;
|
using static osu.Game.Tests.Visual.OsuTestScene.ClockBackedTestWorkingBeatmap;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
@ -28,9 +29,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
protected override bool Autoplay => true;
|
protected override bool Autoplay => true;
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
{
|
{
|
||||||
var working = new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||||
track = (TrackVirtualManual)working.Track;
|
track = (TrackVirtualManual)working.Track;
|
||||||
return working;
|
return working;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using System.Collections.Generic;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Beatmaps
|
namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||||
{
|
{
|
||||||
@ -23,15 +24,13 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
|
|
||||||
protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
|
protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
var curveData = original as IHasCurve;
|
|
||||||
var endTimeData = original as IHasEndTime;
|
|
||||||
var positionData = original as IHasPosition;
|
var positionData = original as IHasPosition;
|
||||||
var comboData = original as IHasCombo;
|
var comboData = original as IHasCombo;
|
||||||
var legacyOffset = original as IHasLegacyLastTickOffset;
|
|
||||||
|
|
||||||
if (curveData != null)
|
switch (original)
|
||||||
{
|
{
|
||||||
yield return new Slider
|
case IHasCurve curveData:
|
||||||
|
return new Slider
|
||||||
{
|
{
|
||||||
StartTime = original.StartTime,
|
StartTime = original.StartTime,
|
||||||
Samples = original.Samples,
|
Samples = original.Samples,
|
||||||
@ -41,15 +40,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
Position = positionData?.Position ?? Vector2.Zero,
|
Position = positionData?.Position ?? Vector2.Zero,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset,
|
LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset,
|
||||||
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
||||||
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
||||||
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1
|
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1
|
||||||
};
|
}.Yield();
|
||||||
}
|
|
||||||
else if (endTimeData != null)
|
case IHasEndTime endTimeData:
|
||||||
{
|
return new Spinner
|
||||||
yield return new Spinner
|
|
||||||
{
|
{
|
||||||
StartTime = original.StartTime,
|
StartTime = original.StartTime,
|
||||||
Samples = original.Samples,
|
Samples = original.Samples,
|
||||||
@ -57,18 +55,17 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
|
Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
};
|
}.Yield();
|
||||||
}
|
|
||||||
else
|
default:
|
||||||
{
|
return new HitCircle
|
||||||
yield return new HitCircle
|
|
||||||
{
|
{
|
||||||
StartTime = original.StartTime,
|
StartTime = original.StartTime,
|
||||||
Samples = original.Samples,
|
Samples = original.Samples,
|
||||||
Position = positionData?.Position ?? Vector2.Zero,
|
Position = positionData?.Position ?? Vector2.Zero,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
};
|
}.Yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
if (objectN is Spinner)
|
if (objectN is Spinner)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
|
double endTime = stackBaseObject.GetEndTime();
|
||||||
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
|
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
|
||||||
|
|
||||||
if (objectN.StartTime - endTime > stackThreshold)
|
if (objectN.StartTime - endTime > stackThreshold)
|
||||||
@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
OsuHitObject objectN = beatmap.HitObjects[n];
|
OsuHitObject objectN = beatmap.HitObjects[n];
|
||||||
if (objectN is Spinner) continue;
|
if (objectN is Spinner) continue;
|
||||||
|
|
||||||
double endTime = (objectN as IHasEndTime)?.EndTime ?? objectN.StartTime;
|
double endTime = objectN.GetEndTime();
|
||||||
|
|
||||||
if (objectI.StartTime - endTime > stackThreshold)
|
if (objectI.StartTime - endTime > stackThreshold)
|
||||||
//We are no longer within stacking range of the previous object.
|
//We are no longer within stacking range of the previous object.
|
||||||
@ -199,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
if (currHitObject.StackHeight != 0 && !(currHitObject is Slider))
|
if (currHitObject.StackHeight != 0 && !(currHitObject is Slider))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
double startTime = (currHitObject as IHasEndTime)?.EndTime ?? currHitObject.StartTime;
|
double startTime = currHitObject.GetEndTime();
|
||||||
int sliderStack = 0;
|
int sliderStack = 0;
|
||||||
|
|
||||||
for (int j = i + 1; j < beatmap.HitObjects.Count; j++)
|
for (int j = i + 1; j < beatmap.HitObjects.Count; j++)
|
||||||
@ -217,14 +217,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance)
|
if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance)
|
||||||
{
|
{
|
||||||
currHitObject.StackHeight++;
|
currHitObject.StackHeight++;
|
||||||
startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[j].StartTime;
|
startTime = beatmap.HitObjects[j].GetEndTime();
|
||||||
}
|
}
|
||||||
else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, position2) < stack_distance)
|
else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, position2) < stack_distance)
|
||||||
{
|
{
|
||||||
//Case for sliders - bump notes down and right, rather than up and left.
|
//Case for sliders - bump notes down and right, rather than up and left.
|
||||||
sliderStack++;
|
sliderStack++;
|
||||||
beatmap.HitObjects[j].StackHeight -= sliderStack;
|
beatmap.HitObjects[j].StackHeight -= sliderStack;
|
||||||
startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[j].StartTime;
|
startTime = beatmap.HitObjects[j].GetEndTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,22 +55,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Custom multipliers for NoFail and SpunOut.
|
// Custom multipliers for NoFail and SpunOut.
|
||||||
double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
||||||
|
|
||||||
if (mods.Any(m => m is OsuModNoFail))
|
if (mods.Any(m => m is OsuModNoFail))
|
||||||
multiplier *= 0.90f;
|
multiplier *= 0.90;
|
||||||
|
|
||||||
if (mods.Any(m => m is OsuModSpunOut))
|
if (mods.Any(m => m is OsuModSpunOut))
|
||||||
multiplier *= 0.95f;
|
multiplier *= 0.95;
|
||||||
|
|
||||||
double aimValue = computeAimValue();
|
double aimValue = computeAimValue();
|
||||||
double speedValue = computeSpeedValue();
|
double speedValue = computeSpeedValue();
|
||||||
double accuracyValue = computeAccuracyValue();
|
double accuracyValue = computeAccuracyValue();
|
||||||
double totalValue =
|
double totalValue =
|
||||||
Math.Pow(
|
Math.Pow(
|
||||||
Math.Pow(aimValue, 1.1f) +
|
Math.Pow(aimValue, 1.1) +
|
||||||
Math.Pow(speedValue, 1.1f) +
|
Math.Pow(speedValue, 1.1) +
|
||||||
Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f
|
Math.Pow(accuracyValue, 1.1), 1.0 / 1.1
|
||||||
) * multiplier;
|
) * multiplier;
|
||||||
|
|
||||||
if (categoryRatings != null)
|
if (categoryRatings != null)
|
||||||
@ -93,82 +93,82 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
if (mods.Any(m => m is OsuModTouchDevice))
|
if (mods.Any(m => m is OsuModTouchDevice))
|
||||||
rawAim = Math.Pow(rawAim, 0.8);
|
rawAim = Math.Pow(rawAim, 0.8);
|
||||||
|
|
||||||
double aimValue = Math.Pow(5.0f * Math.Max(1.0f, rawAim / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
double aimValue = Math.Pow(5.0 * Math.Max(1.0, rawAim / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||||
|
|
||||||
aimValue *= lengthBonus;
|
aimValue *= lengthBonus;
|
||||||
|
|
||||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||||
aimValue *= Math.Pow(0.97f, countMiss);
|
aimValue *= Math.Pow(0.97, countMiss);
|
||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
if (beatmapMaxCombo > 0)
|
if (beatmapMaxCombo > 0)
|
||||||
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(beatmapMaxCombo, 0.8), 1.0);
|
||||||
|
|
||||||
double approachRateFactor = 1.0f;
|
double approachRateFactor = 1.0;
|
||||||
|
|
||||||
if (Attributes.ApproachRate > 10.33f)
|
if (Attributes.ApproachRate > 10.33)
|
||||||
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
|
approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33);
|
||||||
else if (Attributes.ApproachRate < 8.0f)
|
else if (Attributes.ApproachRate < 8.0)
|
||||||
{
|
{
|
||||||
approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
|
approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
aimValue *= approachRateFactor;
|
aimValue *= approachRateFactor;
|
||||||
|
|
||||||
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
||||||
if (mods.Any(h => h is OsuModHidden))
|
if (mods.Any(h => h is OsuModHidden))
|
||||||
aimValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
|
aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);
|
||||||
|
|
||||||
if (mods.Any(h => h is OsuModFlashlight))
|
if (mods.Any(h => h is OsuModFlashlight))
|
||||||
{
|
{
|
||||||
// Apply object-based bonus for flashlight.
|
// Apply object-based bonus for flashlight.
|
||||||
aimValue *= 1.0f + 0.35f * Math.Min(1.0f, totalHits / 200.0f) +
|
aimValue *= 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) +
|
||||||
(totalHits > 200
|
(totalHits > 200
|
||||||
? 0.3f * Math.Min(1.0f, (totalHits - 200) / 300.0f) +
|
? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) +
|
||||||
(totalHits > 500 ? (totalHits - 500) / 1200.0f : 0.0f)
|
(totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0)
|
||||||
: 0.0f);
|
: 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale the aim value with accuracy _slightly_
|
// Scale the aim value with accuracy _slightly_
|
||||||
aimValue *= 0.5f + accuracy / 2.0f;
|
aimValue *= 0.5 + accuracy / 2.0;
|
||||||
// It is important to also consider accuracy difficulty when doing that
|
// It is important to also consider accuracy difficulty when doing that
|
||||||
aimValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
|
aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
|
||||||
|
|
||||||
return aimValue;
|
return aimValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeSpeedValue()
|
private double computeSpeedValue()
|
||||||
{
|
{
|
||||||
double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.SpeedStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
speedValue *= 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||||
|
|
||||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||||
speedValue *= Math.Pow(0.97f, countMiss);
|
speedValue *= Math.Pow(0.97, countMiss);
|
||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
if (beatmapMaxCombo > 0)
|
if (beatmapMaxCombo > 0)
|
||||||
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(beatmapMaxCombo, 0.8), 1.0);
|
||||||
|
|
||||||
double approachRateFactor = 1.0f;
|
double approachRateFactor = 1.0;
|
||||||
if (Attributes.ApproachRate > 10.33f)
|
if (Attributes.ApproachRate > 10.33)
|
||||||
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
|
approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33);
|
||||||
|
|
||||||
speedValue *= approachRateFactor;
|
speedValue *= approachRateFactor;
|
||||||
|
|
||||||
if (mods.Any(m => m is OsuModHidden))
|
if (mods.Any(m => m is OsuModHidden))
|
||||||
speedValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
|
speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);
|
||||||
|
|
||||||
// Scale the speed value with accuracy _slightly_
|
// Scale the speed value with accuracy _slightly_
|
||||||
speedValue *= 0.02f + accuracy;
|
speedValue *= 0.02 + accuracy;
|
||||||
// It is important to also consider accuracy difficulty when doing that
|
// It is important to also consider accuracy difficulty when doing that
|
||||||
speedValue *= 0.96f + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
|
speedValue *= 0.96 + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
|
||||||
|
|
||||||
return speedValue;
|
return speedValue;
|
||||||
}
|
}
|
||||||
@ -190,15 +190,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
// Lots of arbitrary values from testing.
|
// Lots of arbitrary values from testing.
|
||||||
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
||||||
double accuracyValue = Math.Pow(1.52163f, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
|
double accuracyValue = Math.Pow(1.52163, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83;
|
||||||
|
|
||||||
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
||||||
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
|
accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3));
|
||||||
|
|
||||||
if (mods.Any(m => m is OsuModHidden))
|
if (mods.Any(m => m is OsuModHidden))
|
||||||
accuracyValue *= 1.08f;
|
accuracyValue *= 1.08;
|
||||||
if (mods.Any(m => m is OsuModFlashlight))
|
if (mods.Any(m => m is OsuModFlashlight))
|
||||||
accuracyValue *= 1.02f;
|
accuracyValue *= 1.02;
|
||||||
|
|
||||||
return accuracyValue;
|
return accuracyValue;
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
if (progress % 2 >= 1)
|
if (progress % 2 >= 1)
|
||||||
progress = 1 - progress % 1;
|
progress = 1 - progress % 1;
|
||||||
else
|
else
|
||||||
progress = progress % 1;
|
progress %= 1;
|
||||||
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
|
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
|
||||||
var diff = slider.StackedPosition + slider.Path.PositionAt(progress) - slider.LazyEndPosition.Value;
|
var diff = slider.StackedPosition + slider.Path.PositionAt(progress) - slider.LazyEndPosition.Value;
|
||||||
|
@ -17,7 +17,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
|
|||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||||
|
|
||||||
CornerRadius = Size.X / 2;
|
CornerRadius = Size.X / 2;
|
||||||
|
CornerExponent = 2;
|
||||||
|
|
||||||
InternalChild = new RingPiece();
|
InternalChild = new RingPiece();
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,13 @@ using osu.Game.Rulesets.Edit;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||||
{
|
{
|
||||||
public class PathControlPointPiece : BlueprintPiece<Slider>
|
public class PathControlPointPiece : BlueprintPiece<Slider>
|
||||||
{
|
{
|
||||||
public Action<int> RequestSelection;
|
public Action<int, MouseButtonEvent> RequestSelection;
|
||||||
public Action<Vector2[]> ControlPointsChanged;
|
public Action<Vector2[]> ControlPointsChanged;
|
||||||
|
|
||||||
public readonly BindableBool IsSelected = new BindableBool();
|
public readonly BindableBool IsSelected = new BindableBool();
|
||||||
@ -129,10 +130,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
if (RequestSelection != null)
|
if (RequestSelection == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (e.Button)
|
||||||
{
|
{
|
||||||
RequestSelection.Invoke(Index);
|
case MouseButton.Left:
|
||||||
|
RequestSelection.Invoke(Index, e);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case MouseButton.Right:
|
||||||
|
if (!IsSelected.Value)
|
||||||
|
RequestSelection.Invoke(Index, e);
|
||||||
|
return false; // Allow context menu to show
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -142,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
|
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e) => true;
|
protected override bool OnDragStart(DragStartEvent e) => e.Button == MouseButton.Left;
|
||||||
|
|
||||||
protected override bool OnDrag(DragEvent e)
|
protected override bool OnDrag(DragEvent e)
|
||||||
{
|
{
|
||||||
|
@ -3,19 +3,25 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Humanizer;
|
||||||
using osu.Framework.Allocation;
|
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.Cursor;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose;
|
using osu.Game.Screens.Edit.Compose;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||||
{
|
{
|
||||||
public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>
|
public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
||||||
{
|
{
|
||||||
public Action<Vector2[]> ControlPointsChanged;
|
public Action<Vector2[]> ControlPointsChanged;
|
||||||
|
|
||||||
@ -73,9 +79,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectPiece(int index)
|
public bool OnPressed(PlatformAction action)
|
||||||
{
|
{
|
||||||
if (inputManager.CurrentState.Keyboard.ControlPressed)
|
switch (action.ActionMethod)
|
||||||
|
{
|
||||||
|
case PlatformActionMethod.Delete:
|
||||||
|
return deleteSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete;
|
||||||
|
|
||||||
|
private void selectPiece(int index, MouseButtonEvent e)
|
||||||
|
{
|
||||||
|
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
||||||
Pieces[index].IsSelected.Toggle();
|
Pieces[index].IsSelected.Toggle();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -84,11 +103,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(PlatformAction action)
|
private bool deleteSelected()
|
||||||
{
|
{
|
||||||
switch (action.ActionMethod)
|
|
||||||
{
|
|
||||||
case PlatformActionMethod.Delete:
|
|
||||||
var newControlPoints = new List<Vector2>();
|
var newControlPoints = new List<Vector2>();
|
||||||
|
|
||||||
foreach (var piece in Pieces)
|
foreach (var piece in Pieces)
|
||||||
@ -114,20 +130,33 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
newControlPoints[i] = newControlPoints[i] - first;
|
newControlPoints[i] = newControlPoints[i] - first;
|
||||||
|
|
||||||
// The slider's position defines the position of the first control point, and all further control points are relative to that point
|
// The slider's position defines the position of the first control point, and all further control points are relative to that point
|
||||||
slider.Position = slider.Position + first;
|
slider.Position += first;
|
||||||
|
|
||||||
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
|
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
|
||||||
foreach (var piece in Pieces)
|
foreach (var piece in Pieces)
|
||||||
piece.IsSelected.Value = false;
|
piece.IsSelected.Value = false;
|
||||||
|
|
||||||
ControlPointsChanged?.Invoke(newControlPoints.ToArray());
|
ControlPointsChanged?.Invoke(newControlPoints.ToArray());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
public MenuItem[] ContextMenuItems
|
||||||
}
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!Pieces.Any(p => p.IsHovered))
|
||||||
|
return null;
|
||||||
|
|
||||||
public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete;
|
int selectedPoints = Pieces.Count(p => p.IsSelected.Value);
|
||||||
|
|
||||||
|
if (selectedPoints == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new MenuItem[]
|
||||||
|
{
|
||||||
|
new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints)}", MenuItemType.Destructive, () => deleteSelected())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -13,7 +14,6 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private float calculateGap(float value) => MathHelper.Clamp(value, 0, target_clamp) * targetBreakMultiplier;
|
private float calculateGap(float value) => Math.Clamp(value, 0, target_clamp) * targetBreakMultiplier;
|
||||||
|
|
||||||
// lagrange polinominal for (0,0) (0.6,0.4) (1,1) should make a good curve
|
// lagrange polinominal for (0,0) (0.6,0.4) (1,1) should make a good curve
|
||||||
private static float applyAdjustmentCurve(float value) => 0.6f * value * value + 0.4f * value;
|
private static float applyAdjustmentCurve(float value) => 0.6f * value * value + 0.4f * value;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -55,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
var destination = e.MousePosition;
|
var destination = e.MousePosition;
|
||||||
|
|
||||||
FlashlightPosition = Interpolation.ValueAt(
|
FlashlightPosition = Interpolation.ValueAt(
|
||||||
MathHelper.Clamp(Clock.ElapsedFrameTime, 0, follow_delay), position, destination, 0, follow_delay, Easing.Out);
|
Math.Clamp(Clock.ElapsedFrameTime, 0, follow_delay), position, destination, 0, follow_delay, Easing.Out);
|
||||||
|
|
||||||
return base.OnMouseMove(e);
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
|
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
|
||||||
|
|
||||||
var slider = hitObject as Slider;
|
if (!(hitObject is Slider slider))
|
||||||
if (slider == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
|
@ -6,9 +6,9 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||||
{
|
{
|
||||||
void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier;
|
static void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier;
|
||||||
|
|
||||||
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
|
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
|
||||||
{
|
{
|
||||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
|
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
|
||||||
|
|
||||||
// new duration from completed fade in to end (before fading out)
|
// new duration from completed fade in to end (before fading out)
|
||||||
var longFadeDuration = ((h as IHasEndTime)?.EndTime ?? h.StartTime) - fadeOutStartTime;
|
var longFadeDuration = h.GetEndTime() - fadeOutStartTime;
|
||||||
|
|
||||||
switch (drawable)
|
switch (drawable)
|
||||||
{
|
{
|
||||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
|
float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
|
||||||
|
|
||||||
Vector2 originalPosition = drawable.Position;
|
Vector2 originalPosition = drawable.Position;
|
||||||
Vector2 appearOffset = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * appearDistance;
|
Vector2 appearOffset = new Vector2(MathF.Cos(theta), MathF.Sin(theta)) * appearDistance;
|
||||||
|
|
||||||
//the - 1 and + 1 prevents the hit objects to appear in the wrong position.
|
//the - 1 and + 1 prevents the hit objects to appear in the wrong position.
|
||||||
double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
|
double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
|
||||||
|
@ -25,11 +25,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.FollowPoint), _ => new Container
|
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.FollowPoint), _ => new CircularContainer
|
||||||
{
|
{
|
||||||
Masking = true,
|
Masking = true,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
CornerRadius = width / 2,
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
|
@ -6,7 +6,7 @@ using JetBrains.Annotations;
|
|||||||
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.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
|
|
||||||
Vector2 startPosition = osuStart.EndPosition;
|
Vector2 startPosition = osuStart.EndPosition;
|
||||||
Vector2 endPosition = osuEnd.Position;
|
Vector2 endPosition = osuEnd.Position;
|
||||||
double startTime = (osuStart as IHasEndTime)?.EndTime ?? osuStart.StartTime;
|
double startTime = osuStart.GetEndTime();
|
||||||
double endTime = osuEnd.StartTime;
|
double endTime = osuEnd.StartTime;
|
||||||
|
|
||||||
Vector2 distanceVector = endPosition - startPosition;
|
Vector2 distanceVector = endPosition - startPosition;
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||||
|
|
||||||
private OsuInputManager osuActionInputManager;
|
private OsuInputManager osuActionInputManager;
|
||||||
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
|
internal OsuInputManager OsuActionInputManager => osuActionInputManager ??= GetContainingInputManager() as OsuInputManager;
|
||||||
|
|
||||||
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
|
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
|
float aimRotation = MathHelper.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
|
||||||
while (Math.Abs(aimRotation - Rotation) > 180)
|
while (Math.Abs(aimRotation - Rotation) > 180)
|
||||||
aimRotation += aimRotation < Rotation ? 360 : -360;
|
aimRotation += aimRotation < Rotation ? 360 : -360;
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
|
// If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
|
||||||
Rotation = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 100), Rotation, aimRotation, 0, 50, Easing.OutQuint);
|
Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), Rotation, aimRotation, 0, 50, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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 osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -165,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
Tracking.Value = Ball.Tracking;
|
Tracking.Value = Ball.Tracking;
|
||||||
|
|
||||||
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
double completionProgress = Math.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
||||||
|
|
||||||
Ball.UpdateProgress(completionProgress);
|
Ball.UpdateProgress(completionProgress);
|
||||||
Body.UpdateProgress(completionProgress);
|
Body.UpdateProgress(completionProgress);
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
double completionProgress = Math.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
||||||
|
|
||||||
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
||||||
if (!IsHit)
|
if (!IsHit)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -136,7 +137,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
positionBindable.BindTo(HitObject.PositionBindable);
|
positionBindable.BindTo(HitObject.PositionBindable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1);
|
public float Progress => Math.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1);
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
{
|
{
|
||||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||||
Masking = true;
|
Masking = true;
|
||||||
|
|
||||||
CornerRadius = Size.X / 2;
|
CornerRadius = Size.X / 2;
|
||||||
|
CornerExponent = 2;
|
||||||
|
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
@ -18,10 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
InternalChild = new Container
|
InternalChild = new CircularContainer
|
||||||
{
|
{
|
||||||
Masking = true,
|
Masking = true,
|
||||||
CornerRadius = Size.X / 2,
|
|
||||||
BorderThickness = 10,
|
BorderThickness = 10,
|
||||||
BorderColour = Color4.White,
|
BorderColour = Color4.White,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
var spanProgress = slider.ProgressAt(completionProgress);
|
var spanProgress = slider.ProgressAt(completionProgress);
|
||||||
|
|
||||||
double start = 0;
|
double start = 0;
|
||||||
double end = SnakingIn.Value ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1;
|
double end = SnakingIn.Value ? Math.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1;
|
||||||
|
|
||||||
if (span >= slider.SpanCount() - 1)
|
if (span >= slider.SpanCount() - 1)
|
||||||
{
|
{
|
||||||
|
@ -20,9 +20,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
const int count = 18;
|
const float count = 18;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (float i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Add(new Container
|
Add(new Container
|
||||||
{
|
{
|
||||||
@ -40,10 +40,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Size = new Vector2(60, 10),
|
Size = new Vector2(60, 10),
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Position = new Vector2(
|
Position = new Vector2(
|
||||||
0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f,
|
0.5f + MathF.Sin(i / count * 2 * MathF.PI) / 2 * 0.86f,
|
||||||
0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f
|
0.5f + MathF.Cos(i / count * 2 * MathF.PI) / 2 * 0.86f
|
||||||
),
|
),
|
||||||
Rotation = -(float)i / count * 360 + 90,
|
Rotation = -i / count * 360 + 90,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
|
@ -10,7 +10,7 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu.Scoring;
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
|
|
||||||
private void addDelayedMovements(OsuHitObject h, OsuHitObject prev)
|
private void addDelayedMovements(OsuHitObject h, OsuHitObject prev)
|
||||||
{
|
{
|
||||||
double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime;
|
double endTime = prev.GetEndTime();
|
||||||
|
|
||||||
HitWindows hitWindows = null;
|
HitWindows hitWindows = null;
|
||||||
|
|
||||||
@ -185,14 +185,14 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
{
|
{
|
||||||
Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos;
|
Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos;
|
||||||
float distFromCentre = spinCentreOffset.Length;
|
float distFromCentre = spinCentreOffset.Length;
|
||||||
float distToTangentPoint = (float)Math.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS);
|
float distToTangentPoint = MathF.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS);
|
||||||
|
|
||||||
if (distFromCentre > SPIN_RADIUS)
|
if (distFromCentre > SPIN_RADIUS)
|
||||||
{
|
{
|
||||||
// Previous cursor position was outside spin circle, set startPosition to the tangent point.
|
// Previous cursor position was outside spin circle, set startPosition to the tangent point.
|
||||||
|
|
||||||
// Angle between centre offset and tangent point offset.
|
// Angle between centre offset and tangent point offset.
|
||||||
float angle = (float)Math.Asin(SPIN_RADIUS / distFromCentre);
|
float angle = MathF.Asin(SPIN_RADIUS / distFromCentre);
|
||||||
|
|
||||||
if (angle > 0)
|
if (angle > 0)
|
||||||
{
|
{
|
||||||
@ -204,8 +204,8 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rotate by angle so it's parallel to tangent line
|
// Rotate by angle so it's parallel to tangent line
|
||||||
spinCentreOffset.X = spinCentreOffset.X * (float)Math.Cos(angle) - spinCentreOffset.Y * (float)Math.Sin(angle);
|
spinCentreOffset.X = spinCentreOffset.X * MathF.Cos(angle) - spinCentreOffset.Y * MathF.Sin(angle);
|
||||||
spinCentreOffset.Y = spinCentreOffset.X * (float)Math.Sin(angle) + spinCentreOffset.Y * (float)Math.Cos(angle);
|
spinCentreOffset.Y = spinCentreOffset.X * MathF.Sin(angle) + spinCentreOffset.Y * MathF.Cos(angle);
|
||||||
|
|
||||||
// Set length to distToTangentPoint
|
// Set length to distToTangentPoint
|
||||||
spinCentreOffset.Normalize();
|
spinCentreOffset.Normalize();
|
||||||
@ -275,7 +275,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
var startFrame = new OsuReplayFrame(h.StartTime, new Vector2(startPosition.X, startPosition.Y), action);
|
var startFrame = new OsuReplayFrame(h.StartTime, new Vector2(startPosition.X, startPosition.Y), action);
|
||||||
|
|
||||||
// TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime.
|
// TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime.
|
||||||
double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY;
|
double hEndTime = h.GetEndTime() + KEY_UP_DELAY;
|
||||||
int endDelay = h is Spinner ? 1 : 0;
|
int endDelay = h is Spinner ? 1 : 0;
|
||||||
var endFrame = new OsuReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y));
|
var endFrame = new OsuReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y));
|
||||||
|
|
||||||
@ -331,7 +331,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
Vector2 difference = startPosition - SPINNER_CENTRE;
|
Vector2 difference = startPosition - SPINNER_CENTRE;
|
||||||
|
|
||||||
float radius = difference.Length;
|
float radius = difference.Length;
|
||||||
float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X);
|
float angle = radius == 0 ? 0 : MathF.Atan2(difference.Y, difference.X);
|
||||||
|
|
||||||
double t;
|
double t;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Description>click the circles. to the beat.</Description>
|
<Description>click the circles. to the beat.</Description>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
yield return new ConvertValue
|
yield return new ConvertValue
|
||||||
{
|
{
|
||||||
StartTime = hitObject.StartTime,
|
StartTime = hitObject.StartTime,
|
||||||
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
EndTime = hitObject.GetEndTime(),
|
||||||
IsRim = hitObject is RimHit,
|
IsRim = hitObject is RimHit,
|
||||||
IsCentre = hitObject is CentreHit,
|
IsCentre = hitObject is CentreHit,
|
||||||
IsDrumRoll = hitObject is DrumRoll,
|
IsDrumRoll = hitObject is DrumRoll,
|
||||||
|
@ -73,20 +73,17 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
protected override IEnumerable<TaikoHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
|
protected override IEnumerable<TaikoHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
var distanceData = obj as IHasDistance;
|
|
||||||
var repeatsData = obj as IHasRepeats;
|
|
||||||
var endTimeData = obj as IHasEndTime;
|
|
||||||
var curveData = obj as IHasCurve;
|
|
||||||
|
|
||||||
// Old osu! used hit sounding to determine various hit type information
|
// Old osu! used hit sounding to determine various hit type information
|
||||||
IList<HitSampleInfo> samples = obj.Samples;
|
IList<HitSampleInfo> samples = obj.Samples;
|
||||||
|
|
||||||
bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
|
bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
|
||||||
|
|
||||||
if (distanceData != null)
|
switch (obj)
|
||||||
|
{
|
||||||
|
case IHasDistance distanceData:
|
||||||
{
|
{
|
||||||
// Number of spans of the object - one for the initial length and for each repeat
|
// Number of spans of the object - one for the initial length and for each repeat
|
||||||
int spans = repeatsData?.SpanCount() ?? 1;
|
int spans = (obj as IHasRepeats)?.SpanCount() ?? 1;
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
||||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
|
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
|
||||||
@ -117,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||||
{
|
{
|
||||||
List<IList<HitSampleInfo>> allSamples = curveData != null ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
|
List<IList<HitSampleInfo>> allSamples = obj is IHasCurve curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@ -160,8 +157,11 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4
|
TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (endTimeData != null)
|
|
||||||
|
case IHasEndTime endTimeData:
|
||||||
{
|
{
|
||||||
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
|
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
|
||||||
|
|
||||||
@ -172,8 +172,11 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
Duration = endTimeData.Duration,
|
Duration = endTimeData.Duration,
|
||||||
RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier)
|
RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
default:
|
||||||
{
|
{
|
||||||
bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
|
bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||||
|
|
||||||
@ -195,6 +198,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
IsStrong = strong
|
IsStrong = strong
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0;
|
double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0;
|
||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
double lengthBonus = 1 + 0.1f * Math.Min(1.0, totalHits / 1500.0);
|
double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
||||||
strainValue *= lengthBonus;
|
strainValue *= lengthBonus;
|
||||||
|
|
||||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
else
|
else
|
||||||
rollingHits--;
|
rollingHits--;
|
||||||
|
|
||||||
rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour);
|
rollingHits = Math.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour);
|
||||||
|
|
||||||
Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1);
|
Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1);
|
||||||
MainPiece.FadeAccent(newColour, 100);
|
MainPiece.FadeAccent(newColour, 100);
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -179,7 +178,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
var completion = (float)numHits / HitObject.RequiredHits;
|
var completion = (float)numHits / HitObject.RequiredHits;
|
||||||
|
|
||||||
expandingRing
|
expandingRing
|
||||||
.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50)
|
.FadeTo(expandingRing.Alpha + Math.Clamp(completion / 16, 0.1f, 0.6f), 50)
|
||||||
.Then()
|
.Then()
|
||||||
.FadeTo(completion / 8, 2000, Easing.OutQuint);
|
.FadeTo(completion / 8, 2000, Easing.OutQuint);
|
||||||
|
|
||||||
|
@ -10,27 +10,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
|
|||||||
{
|
{
|
||||||
public class TaikoPiece : BeatSyncedContainer, IHasAccentColour
|
public class TaikoPiece : BeatSyncedContainer, IHasAccentColour
|
||||||
{
|
{
|
||||||
private Color4 accentColour;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The colour of the inner circle and outer glows.
|
/// The colour of the inner circle and outer glows.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual Color4 AccentColour
|
public virtual Color4 AccentColour { get; set; }
|
||||||
{
|
|
||||||
get => accentColour;
|
|
||||||
set => accentColour = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool kiaiMode;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether Kiai mode effects are enabled for this circle piece.
|
/// Whether Kiai mode effects are enabled for this circle piece.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool KiaiMode
|
public virtual bool KiaiMode { get; set; }
|
||||||
{
|
|
||||||
get => kiaiMode;
|
|
||||||
set => kiaiMode = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaikoPiece()
|
public TaikoPiece()
|
||||||
{
|
{
|
||||||
|
@ -43,11 +43,9 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
IHasEndTime endTimeData = h as IHasEndTime;
|
IHasEndTime endTimeData = h as IHasEndTime;
|
||||||
double endTime = endTimeData?.EndTime ?? h.StartTime;
|
double endTime = endTimeData?.EndTime ?? h.StartTime;
|
||||||
|
|
||||||
Swell swell = h as Swell;
|
switch (h)
|
||||||
DrumRoll drumRoll = h as DrumRoll;
|
{
|
||||||
Hit hit = h as Hit;
|
case Swell swell:
|
||||||
|
|
||||||
if (swell != null)
|
|
||||||
{
|
{
|
||||||
int d = 0;
|
int d = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -83,16 +81,22 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
if (++count == req)
|
if (++count == req)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (drumRoll != null)
|
|
||||||
|
case DrumRoll drumRoll:
|
||||||
{
|
{
|
||||||
foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>())
|
foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>())
|
||||||
{
|
{
|
||||||
Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre));
|
Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre));
|
||||||
hitButton = !hitButton;
|
hitButton = !hitButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (hit != null)
|
|
||||||
|
case Hit hit:
|
||||||
{
|
{
|
||||||
TaikoAction[] actions;
|
TaikoAction[] actions;
|
||||||
|
|
||||||
@ -110,9 +114,12 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
}
|
}
|
||||||
|
|
||||||
Frames.Add(new TaikoReplayFrame(h.StartTime, actions));
|
Frames.Add(new TaikoReplayFrame(h.StartTime, actions));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
default:
|
||||||
throw new InvalidOperationException("Unknown hit object type.");
|
throw new InvalidOperationException("Unknown hit object type.");
|
||||||
|
}
|
||||||
|
|
||||||
var nextHitObject = GetNextObject(i); // Get the next object that requires pressing the same button
|
var nextHitObject = GetNextObject(i); // Get the next object that requires pressing the same button
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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 osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
float aspectAdjust = MathHelper.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect;
|
float aspectAdjust = Math.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect;
|
||||||
Size = new Vector2(1, default_relative_height * aspectAdjust);
|
Size = new Vector2(1, default_relative_height * aspectAdjust);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Description>bash the drum. to the beat.</Description>
|
<Description>bash the drum. to the beat.</Description>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -24,6 +24,7 @@
|
|||||||
<Compile Include="..\osu.Game.Tests\**\Beatmaps\**\*.cs">
|
<Compile Include="..\osu.Game.Tests\**\Beatmaps\**\*.cs">
|
||||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Remove="..\osu.Game.Tests\Beatmaps\Formats\OsuJsonDecoderTest.cs" />
|
||||||
<Compile Include="..\osu.Game.Tests\**\Chat\**\*.cs">
|
<Compile Include="..\osu.Game.Tests\**\Chat\**\*.cs">
|
||||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -68,10 +69,5 @@
|
|||||||
<Name>osu.Game</Name>
|
<Name>osu.Game</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="DeepEqual">
|
|
||||||
<Version>2.0.0</Version>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
@ -413,7 +413,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual("soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
|
Assert.AreEqual("soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -431,7 +431,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -451,7 +451,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
|
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -59,7 +59,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var _ = int.Parse(input);
|
_ = int.Parse(input);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -411,6 +411,48 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task TestImportWithDuplicateHashes()
|
||||||
|
{
|
||||||
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var osu = loadOsu(host);
|
||||||
|
|
||||||
|
var temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
|
string extractedFolder = $"{temp}_extracted";
|
||||||
|
Directory.CreateDirectory(extractedFolder);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var zip = ZipArchive.Open(temp))
|
||||||
|
zip.WriteToDirectory(extractedFolder);
|
||||||
|
|
||||||
|
using (var zip = ZipArchive.Create())
|
||||||
|
{
|
||||||
|
zip.AddAllFromDirectory(extractedFolder);
|
||||||
|
zip.AddEntry("duplicate.osu", Directory.GetFiles(extractedFolder, "*.osu").First());
|
||||||
|
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
|
||||||
|
}
|
||||||
|
|
||||||
|
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
||||||
|
|
||||||
|
ensureLoaded(osu);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Directory.Delete(extractedFolder, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
host.Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task TestImportNestedStructure()
|
public async Task TestImportNestedStructure()
|
||||||
{
|
{
|
||||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
|||||||
Assert.IsNull(filterCriteria.BPM.Max);
|
Assert.IsNull(filterCriteria.BPM.Max);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object[] lengthQueryExamples =
|
private static readonly object[] length_query_examples =
|
||||||
{
|
{
|
||||||
new object[] { "6ms", TimeSpan.FromMilliseconds(6), TimeSpan.FromMilliseconds(1) },
|
new object[] { "6ms", TimeSpan.FromMilliseconds(6), TimeSpan.FromMilliseconds(1) },
|
||||||
new object[] { "23s", TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(1) },
|
new object[] { "23s", TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(1) },
|
||||||
@ -97,7 +97,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
|||||||
};
|
};
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[TestCaseSource(nameof(lengthQueryExamples))]
|
[TestCaseSource(nameof(length_query_examples))]
|
||||||
public void TestApplyLengthQueries(string lengthQuery, TimeSpan expectedLength, TimeSpan scale)
|
public void TestApplyLengthQueries(string lengthQuery, TimeSpan expectedLength, TimeSpan scale)
|
||||||
{
|
{
|
||||||
string query = $"length={lengthQuery} time";
|
string query = $"length={lengthQuery} time";
|
||||||
|
2
osu.Game.Tests/Resources/skin-20.ini
Normal file
2
osu.Game.Tests/Resources/skin-20.ini
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[General]
|
||||||
|
Version: 2
|
2
osu.Game.Tests/Resources/skin-latest.ini
Normal file
2
osu.Game.Tests/Resources/skin-latest.ini
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[General]
|
||||||
|
Version: latest
|
@ -59,5 +59,32 @@ namespace osu.Game.Tests.Skins
|
|||||||
Assert.AreEqual("TestValue", config.ConfigDictionary["TestLookup"]);
|
Assert.AreEqual("TestValue", config.ConfigDictionary["TestLookup"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeSpecifiedVersion()
|
||||||
|
{
|
||||||
|
var decoder = new LegacySkinDecoder();
|
||||||
|
using (var resStream = TestResources.OpenResource("skin-20.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeLatestVersion()
|
||||||
|
{
|
||||||
|
var decoder = new LegacySkinDecoder();
|
||||||
|
using (var resStream = TestResources.OpenResource("skin-latest.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
Assert.AreEqual(LegacySkinConfiguration.LATEST_VERSION, decoder.Decode(stream).LegacyVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeNoVersion()
|
||||||
|
{
|
||||||
|
var decoder = new LegacySkinDecoder();
|
||||||
|
using (var resStream = TestResources.OpenResource("skin-empty.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
Assert.IsNull(decoder.Decode(stream).LegacyVersion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,14 @@ namespace osu.Game.Tests.Skins
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLegacyVersionLookup()
|
||||||
|
{
|
||||||
|
AddStep("Set source1 version 2.3", () => source1.Configuration.LegacyVersion = 2.3m);
|
||||||
|
AddStep("Set source2 version null", () => source2.Configuration.LegacyVersion = null);
|
||||||
|
AddAssert("Check legacy version lookup", () => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == 2.3m);
|
||||||
|
}
|
||||||
|
|
||||||
public enum LookupType
|
public enum LookupType
|
||||||
{
|
{
|
||||||
Test
|
Test
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
@ -13,7 +14,9 @@ namespace osu.Game.Tests.Visual.Components
|
|||||||
{
|
{
|
||||||
public class TestScenePreviewTrackManager : OsuTestScene, IPreviewTrackOwner
|
public class TestScenePreviewTrackManager : OsuTestScene, IPreviewTrackOwner
|
||||||
{
|
{
|
||||||
private readonly PreviewTrackManager trackManager = new TestPreviewTrackManager();
|
private readonly TestPreviewTrackManager trackManager = new TestPreviewTrackManager();
|
||||||
|
|
||||||
|
private AudioManager audio;
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
{
|
{
|
||||||
@ -24,8 +27,10 @@ namespace osu.Game.Tests.Visual.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
|
this.audio = audio;
|
||||||
|
|
||||||
Add(trackManager);
|
Add(trackManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +113,60 @@ namespace osu.Game.Tests.Visual.Components
|
|||||||
AddAssert("track stopped", () => !track.IsRunning);
|
AddAssert("track stopped", () => !track.IsRunning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that <see cref="PreviewTrackManager.CurrentTrack"/> changes correctly.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestCurrentTrackChanges()
|
||||||
|
{
|
||||||
|
PreviewTrack track = null;
|
||||||
|
TestTrackOwner owner = null;
|
||||||
|
|
||||||
|
AddStep("get track", () => Add(owner = new TestTrackOwner(track = getTrack())));
|
||||||
|
AddUntilStep("wait loaded", () => track.IsLoaded);
|
||||||
|
AddStep("start track", () => track.Start());
|
||||||
|
AddAssert("current is track", () => trackManager.CurrentTrack == track);
|
||||||
|
AddStep("pause manager updates", () => trackManager.AllowUpdate = false);
|
||||||
|
AddStep("stop any playing", () => trackManager.StopAnyPlaying(owner));
|
||||||
|
AddAssert("current not changed", () => trackManager.CurrentTrack == track);
|
||||||
|
AddStep("resume manager updates", () => trackManager.AllowUpdate = true);
|
||||||
|
AddAssert("current is null", () => trackManager.CurrentTrack == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that <see cref="PreviewTrackManager"/> mutes game-wide audio tracks correctly.
|
||||||
|
/// </summary>
|
||||||
|
[TestCase(false)]
|
||||||
|
[TestCase(true)]
|
||||||
|
public void TestEnsureMutingCorrectly(bool stopAnyPlaying)
|
||||||
|
{
|
||||||
|
PreviewTrack track = null;
|
||||||
|
TestTrackOwner owner = null;
|
||||||
|
|
||||||
|
AddStep("ensure volume not zero", () =>
|
||||||
|
{
|
||||||
|
if (audio.Volume.Value == 0)
|
||||||
|
audio.Volume.Value = 1;
|
||||||
|
|
||||||
|
if (audio.VolumeTrack.Value == 0)
|
||||||
|
audio.VolumeTrack.Value = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("game not muted", () => audio.Tracks.AggregateVolume.Value != 0);
|
||||||
|
|
||||||
|
AddStep("get track", () => Add(owner = new TestTrackOwner(track = getTrack())));
|
||||||
|
AddUntilStep("wait loaded", () => track.IsLoaded);
|
||||||
|
AddStep("start track", () => track.Start());
|
||||||
|
AddAssert("game is muted", () => audio.Tracks.AggregateVolume.Value == 0);
|
||||||
|
|
||||||
|
if (stopAnyPlaying)
|
||||||
|
AddStep("stop any playing", () => trackManager.StopAnyPlaying(owner));
|
||||||
|
else
|
||||||
|
AddStep("stop track", () => track.Stop());
|
||||||
|
|
||||||
|
AddAssert("game not muted", () => audio.Tracks.AggregateVolume.Value != 0);
|
||||||
|
}
|
||||||
|
|
||||||
private TestPreviewTrack getTrack() => (TestPreviewTrack)trackManager.Get(null);
|
private TestPreviewTrack getTrack() => (TestPreviewTrack)trackManager.Get(null);
|
||||||
|
|
||||||
private TestPreviewTrack getOwnedTrack()
|
private TestPreviewTrack getOwnedTrack()
|
||||||
@ -144,8 +203,20 @@ namespace osu.Game.Tests.Visual.Components
|
|||||||
|
|
||||||
public class TestPreviewTrackManager : PreviewTrackManager
|
public class TestPreviewTrackManager : PreviewTrackManager
|
||||||
{
|
{
|
||||||
|
public bool AllowUpdate = true;
|
||||||
|
|
||||||
|
public new PreviewTrack CurrentTrack => base.CurrentTrack;
|
||||||
|
|
||||||
protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore);
|
protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore);
|
||||||
|
|
||||||
|
public override bool UpdateSubTree()
|
||||||
|
{
|
||||||
|
if (!AllowUpdate)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return base.UpdateSubTree();
|
||||||
|
}
|
||||||
|
|
||||||
public class TestPreviewTrack : TrackManagerPreviewTrack
|
public class TestPreviewTrack : TrackManagerPreviewTrack
|
||||||
{
|
{
|
||||||
private readonly ITrackStore trackManager;
|
private readonly ITrackStore trackManager;
|
||||||
|
@ -7,6 +7,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -29,9 +30,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
|
AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
{
|
{
|
||||||
var working = base.CreateWorkingBeatmap(beatmap);
|
var working = base.CreateWorkingBeatmap(beatmap, storyboard);
|
||||||
|
|
||||||
track = (ClockBackedTestWorkingBeatmap.TrackVirtualManual)working.Track;
|
track = (ClockBackedTestWorkingBeatmap.TrackVirtualManual)working.Track;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Catch.Scoring;
|
using osu.Game.Rulesets.Catch.Scoring;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
using osu.Game.Rulesets.Osu.Scoring;
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
@ -85,9 +85,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new SpriteText { Text = $@"Great: {hitWindows?.WindowFor(HitResult.Great)}" },
|
new OsuSpriteText { Text = $@"Great: {hitWindows?.WindowFor(HitResult.Great)}" },
|
||||||
new SpriteText { Text = $@"Good: {hitWindows?.WindowFor(HitResult.Good)}" },
|
new OsuSpriteText { Text = $@"Good: {hitWindows?.WindowFor(HitResult.Good)}" },
|
||||||
new SpriteText { Text = $@"Meh: {hitWindows?.WindowFor(HitResult.Meh)}" },
|
new OsuSpriteText { Text = $@"Meh: {hitWindows?.WindowFor(HitResult.Meh)}" },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -95,6 +95,19 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false);
|
seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(true)]
|
||||||
|
[TestCase(false)]
|
||||||
|
public void TestBeforeGameplayStart(bool withBreaks)
|
||||||
|
{
|
||||||
|
setClock(true);
|
||||||
|
|
||||||
|
if (withBreaks)
|
||||||
|
loadBreaksStep("multiple breaks", testBreaks);
|
||||||
|
|
||||||
|
seekAndAssertBreak("seek to break intro time", -100, true);
|
||||||
|
seekAndAssertBreak("seek to break intro time", 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
private void addShowBreakStep(double seconds)
|
private void addShowBreakStep(double seconds)
|
||||||
{
|
{
|
||||||
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List<BreakPeriod>
|
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List<BreakPeriod>
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -35,9 +36,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private Track track;
|
private Track track;
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
{
|
{
|
||||||
var working = new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||||
track = working.Track;
|
track = working.Track;
|
||||||
return working;
|
return working;
|
||||||
}
|
}
|
||||||
|
113
osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs
Normal file
113
osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneLeadIn : RateAdjustedBeatmapTestScene
|
||||||
|
{
|
||||||
|
private LeadInPlayer player;
|
||||||
|
|
||||||
|
private const double lenience_ms = 10;
|
||||||
|
|
||||||
|
private const double first_hit_object = 2170;
|
||||||
|
|
||||||
|
[TestCase(1000, 0)]
|
||||||
|
[TestCase(2000, 0)]
|
||||||
|
[TestCase(3000, first_hit_object - 3000)]
|
||||||
|
[TestCase(10000, first_hit_object - 10000)]
|
||||||
|
public void TestLeadInProducesCorrectStartTime(double leadIn, double expectedStartTime)
|
||||||
|
{
|
||||||
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||||
|
{
|
||||||
|
BeatmapInfo = { AudioLeadIn = leadIn }
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert($"first frame is {expectedStartTime}", () =>
|
||||||
|
{
|
||||||
|
Debug.Assert(player.FirstFrameClockTime != null);
|
||||||
|
return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(1000, 0)]
|
||||||
|
[TestCase(0, 0)]
|
||||||
|
[TestCase(-1000, -1000)]
|
||||||
|
[TestCase(-10000, -10000)]
|
||||||
|
public void TestStoryboardProducesCorrectStartTime(double firstStoryboardEvent, double expectedStartTime)
|
||||||
|
{
|
||||||
|
var storyboard = new Storyboard();
|
||||||
|
|
||||||
|
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
|
||||||
|
sprite.TimelineGroup.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1);
|
||||||
|
|
||||||
|
storyboard.GetLayer("Background").Add(sprite);
|
||||||
|
|
||||||
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard);
|
||||||
|
|
||||||
|
AddAssert($"first frame is {expectedStartTime}", () =>
|
||||||
|
{
|
||||||
|
Debug.Assert(player.FirstFrameClockTime != null);
|
||||||
|
return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPlayerWithBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
|
{
|
||||||
|
AddStep("create player", () =>
|
||||||
|
{
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(beatmap, storyboard);
|
||||||
|
LoadScreen(player = new LeadInPlayer());
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("player loaded", () => player.IsLoaded && player.Alpha == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LeadInPlayer : TestPlayer
|
||||||
|
{
|
||||||
|
public LeadInPlayer()
|
||||||
|
: base(false, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public double? FirstFrameClockTime;
|
||||||
|
|
||||||
|
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
|
||||||
|
|
||||||
|
public double GameplayStartTime => DrawableRuleset.GameplayStartTime;
|
||||||
|
|
||||||
|
public double FirstHitObjectTime => DrawableRuleset.Objects.First().StartTime;
|
||||||
|
|
||||||
|
public double GameplayClockTime => GameplayClockContainer.GameplayClock.CurrentTime;
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
|
if (!FirstFrameClockTime.HasValue)
|
||||||
|
{
|
||||||
|
FirstFrameClockTime = GameplayClockContainer.GameplayClock.CurrentTime;
|
||||||
|
AddInternal(new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = $"GameplayStartTime: {DrawableRuleset.GameplayStartTime} "
|
||||||
|
+ $"FirstHitObjectTime: {FirstHitObjectTime} "
|
||||||
|
+ $"LeadInTime: {Beatmap.Value.BeatmapInfo.AudioLeadIn} "
|
||||||
|
+ $"FirstFrameClockTime: {FirstFrameClockTime}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using osu.Framework.Lists;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -42,9 +43,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
{
|
{
|
||||||
var working = base.CreateWorkingBeatmap(beatmap);
|
var working = base.CreateWorkingBeatmap(beatmap, storyboard);
|
||||||
workingWeakReferences.Add(working);
|
workingWeakReferences.Add(working);
|
||||||
return working;
|
return working;
|
||||||
}
|
}
|
||||||
|
@ -335,16 +335,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private class TestSkinComponent : ISkinComponent
|
private class TestSkinComponent : ISkinComponent
|
||||||
{
|
{
|
||||||
private readonly string name;
|
|
||||||
|
|
||||||
public TestSkinComponent(string name)
|
public TestSkinComponent(string name)
|
||||||
{
|
{
|
||||||
this.name = name;
|
LookupName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ComponentGroup => string.Empty;
|
public string ComponentGroup => string.Empty;
|
||||||
|
|
||||||
public string LookupName => name;
|
public string LookupName { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Framework.Timing;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -18,25 +18,37 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
private SkipOverlay skip;
|
private SkipOverlay skip;
|
||||||
private int requestCount;
|
private int requestCount;
|
||||||
|
|
||||||
|
private double increment;
|
||||||
|
|
||||||
|
private GameplayClockContainer gameplayClockContainer;
|
||||||
|
private GameplayClock gameplayClock;
|
||||||
|
|
||||||
|
private const double skip_time = 6000;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
requestCount = 0;
|
requestCount = 0;
|
||||||
Child = new Container
|
increment = skip_time;
|
||||||
|
|
||||||
|
Child = gameplayClockContainer = new GameplayClockContainer(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new Mod[] { }, 0)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Clock = new FramedOffsetClock(Clock)
|
|
||||||
{
|
|
||||||
Offset = -Clock.CurrentTime,
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
skip = new SkipOverlay(6000)
|
skip = new SkipOverlay(skip_time)
|
||||||
{
|
{
|
||||||
RequestSeek = _ => requestCount++
|
RequestSkip = () =>
|
||||||
|
{
|
||||||
|
requestCount++;
|
||||||
|
gameplayClockContainer.Seek(gameplayClock.CurrentTime + increment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
gameplayClockContainer.Start();
|
||||||
|
gameplayClock = gameplayClockContainer.GameplayClock;
|
||||||
});
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -64,19 +76,35 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestClickOnlyActuatesOnce()
|
public void TestClickOnlyActuatesOnce()
|
||||||
{
|
{
|
||||||
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
|
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
|
||||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
AddStep("click", () =>
|
||||||
|
{
|
||||||
|
increment = skip_time - gameplayClock.CurrentTime - GameplayClockContainer.MINIMUM_SKIP_TIME / 2;
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
checkRequestCount(1);
|
checkRequestCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestClickOnlyActuatesMultipleTimes()
|
||||||
|
{
|
||||||
|
AddStep("set increment lower", () => increment = 3000);
|
||||||
|
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
checkRequestCount(2);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDoesntFadeOnMouseDown()
|
public void TestDoesntFadeOnMouseDown()
|
||||||
{
|
{
|
||||||
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
|
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
|
||||||
AddStep("button down", () => InputManager.PressButton(MouseButton.Left));
|
AddStep("button down", () => InputManager.PressButton(MouseButton.Left));
|
||||||
AddUntilStep("wait for overlay disapper", () => !skip.IsAlive);
|
AddUntilStep("wait for overlay disappear", () => !skip.IsPresent);
|
||||||
AddAssert("ensure button didn't disappear", () => skip.Children.First().Alpha > 0);
|
AddAssert("ensure button didn't disappear", () => skip.Children.First().Alpha > 0);
|
||||||
AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left));
|
AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
checkRequestCount(0);
|
checkRequestCount(0);
|
||||||
|
@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
typeof(BeatmapAvailability),
|
typeof(BeatmapAvailability),
|
||||||
typeof(BeatmapRulesetSelector),
|
typeof(BeatmapRulesetSelector),
|
||||||
typeof(BeatmapRulesetTabItem),
|
typeof(BeatmapRulesetTabItem),
|
||||||
|
typeof(NotSupporterPlaceholder)
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override bool UseOnlineAPI => true;
|
protected override bool UseOnlineAPI => true;
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddStep("set second set", () => successRate.Beatmap = secondBeatmap);
|
AddStep("set second set", () => successRate.Beatmap = secondBeatmap);
|
||||||
AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics);
|
AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics);
|
||||||
|
|
||||||
BeatmapInfo createBeatmap() => new BeatmapInfo
|
static BeatmapInfo createBeatmap() => new BeatmapInfo
|
||||||
{
|
{
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Taiko;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneLeaderboardModSelector : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(LeaderboardModSelector),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneLeaderboardModSelector()
|
||||||
|
{
|
||||||
|
LeaderboardModSelector modSelector;
|
||||||
|
FillFlowContainer<SpriteText> selectedMods;
|
||||||
|
var ruleset = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
|
Add(selectedMods = new FillFlowContainer<SpriteText>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopLeft,
|
||||||
|
Origin = Anchor.TopLeft,
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(modSelector = new LeaderboardModSelector
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Ruleset = { BindTarget = ruleset }
|
||||||
|
});
|
||||||
|
|
||||||
|
modSelector.SelectedMods.ItemsAdded += mods =>
|
||||||
|
{
|
||||||
|
mods.ForEach(mod => selectedMods.Add(new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = mod.Acronym,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
modSelector.SelectedMods.ItemsRemoved += mods =>
|
||||||
|
{
|
||||||
|
mods.ForEach(mod =>
|
||||||
|
{
|
||||||
|
foreach (var selected in selectedMods)
|
||||||
|
{
|
||||||
|
if (selected.Text == mod.Acronym)
|
||||||
|
{
|
||||||
|
selectedMods.Remove(selected);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("osu ruleset", () => ruleset.Value = new OsuRuleset().RulesetInfo);
|
||||||
|
AddStep("mania ruleset", () => ruleset.Value = new ManiaRuleset().RulesetInfo);
|
||||||
|
AddStep("taiko ruleset", () => ruleset.Value = new TaikoRuleset().RulesetInfo);
|
||||||
|
AddStep("catch ruleset", () => ruleset.Value = new CatchRuleset().RulesetInfo);
|
||||||
|
AddStep("Deselect all", () => modSelector.DeselectAll());
|
||||||
|
AddStep("null ruleset", () => ruleset.Value = null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs
Normal file
23
osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneNewsOverlay : OsuTestScene
|
||||||
|
{
|
||||||
|
private NewsOverlay news;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
Add(news = new NewsOverlay());
|
||||||
|
AddStep(@"Show", news.Show);
|
||||||
|
AddStep(@"Hide", news.Hide);
|
||||||
|
|
||||||
|
AddStep(@"Show front page", () => news.ShowFrontPage());
|
||||||
|
AddStep(@"Custom article", () => news.Current.Value = "Test Article 101");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Overlays.Rankings;
|
using osu.Game.Overlays.Rankings;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -45,7 +46,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Size = new Vector2(30, 20),
|
Size = new Vector2(30, 20),
|
||||||
Country = countryA,
|
Country = countryA,
|
||||||
},
|
},
|
||||||
text = new SpriteText
|
text = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
// 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 osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Overlays.Comments;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneTotalCommentsCounter : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(TotalCommentsCounter),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneTotalCommentsCounter()
|
||||||
|
{
|
||||||
|
var count = new BindableInt();
|
||||||
|
|
||||||
|
Add(new TotalCommentsCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Current = { BindTarget = count }
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep(@"Set 100", () => count.Value = 100);
|
||||||
|
AddStep(@"Set 0", () => count.Value = 0);
|
||||||
|
AddStep(@"Set random", () => count.Value = RNG.Next(0, int.MaxValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user