mirror of
https://github.com/ppy/osu.git
synced 2025-01-18 06:22:56 +08:00
Merge branch 'master' into refactor-leaderboard
This commit is contained in:
commit
a25444e36c
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -114,7 +114,10 @@ jobs:
|
|||||||
dotnet-version: "8.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Install .NET workloads
|
- name: Install .NET workloads
|
||||||
run: dotnet workload install android
|
# since windows image 20241113.3.0, not specifying a version here
|
||||||
|
# installs the .NET 7 version of android workload for very unknown reasons.
|
||||||
|
# revisit once we upgrade to .NET 9, it's probably fixed there.
|
||||||
|
run: dotnet workload install android --version (dotnet --version)
|
||||||
|
|
||||||
- name: Compile
|
- name: Compile
|
||||||
run: dotnet build -c Debug osu.Android.slnf
|
run: dotnet build -c Debug osu.Android.slnf
|
||||||
|
2
.github/workflows/diffcalc.yml
vendored
2
.github/workflows/diffcalc.yml
vendored
@ -115,7 +115,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Check permissions
|
- name: Check permissions
|
||||||
run: |
|
run: |
|
||||||
ALLOWED_USERS=(smoogipoo peppy bdach frenzibyte)
|
ALLOWED_USERS=(smoogipoo peppy bdach frenzibyte tsunyoku stanriders)
|
||||||
for i in "${ALLOWED_USERS[@]}"; do
|
for i in "${ALLOWED_USERS[@]}"; do
|
||||||
if [[ "${{ github.actor }}" == "$i" ]]; then
|
if [[ "${{ github.actor }}" == "$i" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
# .NET Code Style
|
|
||||||
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
|
|
||||||
|
|
||||||
# IDE0001: Simplify names
|
|
||||||
dotnet_diagnostic.IDE0001.severity = warning
|
|
||||||
|
|
||||||
# IDE0002: Simplify member access
|
|
||||||
dotnet_diagnostic.IDE0002.severity = warning
|
|
||||||
|
|
||||||
# IDE0003: Remove qualification
|
|
||||||
dotnet_diagnostic.IDE0003.severity = warning
|
|
||||||
|
|
||||||
# IDE0004: Remove unnecessary cast
|
|
||||||
dotnet_diagnostic.IDE0004.severity = warning
|
|
||||||
|
|
||||||
# IDE0005: Remove unnecessary imports
|
|
||||||
dotnet_diagnostic.IDE0005.severity = warning
|
|
||||||
|
|
||||||
# IDE0034: Simplify default literal
|
|
||||||
dotnet_diagnostic.IDE0034.severity = warning
|
|
||||||
|
|
||||||
# IDE0036: Sort modifiers
|
|
||||||
dotnet_diagnostic.IDE0036.severity = warning
|
|
||||||
|
|
||||||
# IDE0040: Add accessibility modifier
|
|
||||||
dotnet_diagnostic.IDE0040.severity = warning
|
|
||||||
|
|
||||||
# IDE0049: Use keyword for type name
|
|
||||||
dotnet_diagnostic.IDE0040.severity = warning
|
|
||||||
|
|
||||||
# IDE0055: Fix formatting
|
|
||||||
dotnet_diagnostic.IDE0055.severity = warning
|
|
||||||
|
|
||||||
# IDE0051: Private method is unused
|
|
||||||
dotnet_diagnostic.IDE0051.severity = silent
|
|
||||||
|
|
||||||
# IDE0052: Private member is unused
|
|
||||||
dotnet_diagnostic.IDE0052.severity = silent
|
|
||||||
|
|
||||||
# IDE0073: File header
|
|
||||||
dotnet_diagnostic.IDE0073.severity = warning
|
|
||||||
|
|
||||||
# IDE0130: Namespace mismatch with folder
|
|
||||||
dotnet_diagnostic.IDE0130.severity = warning
|
|
||||||
|
|
||||||
# IDE1006: Naming style
|
|
||||||
dotnet_diagnostic.IDE1006.severity = warning
|
|
||||||
|
|
||||||
#Disable operator overloads requiring alternate named methods
|
|
||||||
dotnet_diagnostic.CA2225.severity = none
|
|
||||||
|
|
||||||
# Banned APIs
|
|
||||||
dotnet_diagnostic.RS0030.severity = error
|
|
||||||
|
|
||||||
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
|
|
||||||
# See: https://github.com/ppy/osu/pull/19677
|
|
||||||
dotnet_diagnostic.OSUF001.severity = none
|
|
@ -14,10 +14,6 @@ M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Gen
|
|||||||
M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks.
|
M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks.
|
||||||
P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks.
|
P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks.
|
||||||
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
|
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
|
||||||
M:System.Char.ToLower(System.Char);char.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
|
|
||||||
M:System.Char.ToUpper(System.Char);char.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
|
|
||||||
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
|
|
||||||
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
|
|
||||||
M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead.
|
M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead.
|
||||||
M:Humanizer.InflectorExtensions.Camelize(System.String);Humanizer's .Camelize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToCamelCase() instead.
|
M:Humanizer.InflectorExtensions.Camelize(System.String);Humanizer's .Camelize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToCamelCase() instead.
|
||||||
M:Humanizer.InflectorExtensions.Underscore(System.String);Humanizer's .Underscore() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToSnakeCase() instead.
|
M:Humanizer.InflectorExtensions.Underscore(System.String);Humanizer's .Underscore() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToSnakeCase() instead.
|
||||||
|
109
CodeAnalysis/osu.globalconfig
Normal file
109
CodeAnalysis/osu.globalconfig
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# .NET Code Style
|
||||||
|
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
|
||||||
|
is_global = true
|
||||||
|
|
||||||
|
# IDE0001: Simplify names
|
||||||
|
dotnet_diagnostic.IDE0001.severity = warning
|
||||||
|
|
||||||
|
# IDE0002: Simplify member access
|
||||||
|
dotnet_diagnostic.IDE0002.severity = warning
|
||||||
|
|
||||||
|
# IDE0003: Remove qualification
|
||||||
|
dotnet_diagnostic.IDE0003.severity = warning
|
||||||
|
|
||||||
|
# IDE0004: Remove unnecessary cast
|
||||||
|
dotnet_diagnostic.IDE0004.severity = warning
|
||||||
|
|
||||||
|
# IDE0005: Remove unnecessary imports
|
||||||
|
dotnet_diagnostic.IDE0005.severity = warning
|
||||||
|
|
||||||
|
# IDE0034: Simplify default literal
|
||||||
|
dotnet_diagnostic.IDE0034.severity = warning
|
||||||
|
|
||||||
|
# IDE0036: Sort modifiers
|
||||||
|
dotnet_diagnostic.IDE0036.severity = warning
|
||||||
|
|
||||||
|
# IDE0040: Add accessibility modifier
|
||||||
|
dotnet_diagnostic.IDE0040.severity = warning
|
||||||
|
|
||||||
|
# IDE0049: Use keyword for type name
|
||||||
|
dotnet_diagnostic.IDE0040.severity = warning
|
||||||
|
|
||||||
|
# IDE0055: Fix formatting
|
||||||
|
dotnet_diagnostic.IDE0055.severity = warning
|
||||||
|
|
||||||
|
# IDE0051: Private method is unused
|
||||||
|
dotnet_diagnostic.IDE0051.severity = silent
|
||||||
|
|
||||||
|
# IDE0052: Private member is unused
|
||||||
|
dotnet_diagnostic.IDE0052.severity = silent
|
||||||
|
|
||||||
|
# IDE0073: File header
|
||||||
|
dotnet_diagnostic.IDE0073.severity = warning
|
||||||
|
|
||||||
|
# IDE0130: Namespace mismatch with folder
|
||||||
|
dotnet_diagnostic.IDE0130.severity = warning
|
||||||
|
|
||||||
|
# IDE1006: Naming style
|
||||||
|
dotnet_diagnostic.IDE1006.severity = warning
|
||||||
|
|
||||||
|
# CA1305: Specify IFormatProvider
|
||||||
|
# Too many noisy warnings for parsing/formatting numbers
|
||||||
|
dotnet_diagnostic.CA1305.severity = none
|
||||||
|
|
||||||
|
# CA1507: Use nameof to express symbol names
|
||||||
|
# Flaggs serialization name attributes
|
||||||
|
dotnet_diagnostic.CA1507.severity = suggestion
|
||||||
|
|
||||||
|
# CA1806: Do not ignore method results
|
||||||
|
# The usages for numeric parsing are explicitly optional
|
||||||
|
dotnet_diagnostic.CA1806.severity = suggestion
|
||||||
|
|
||||||
|
# CA1822: Mark members as static
|
||||||
|
# Potential false positive around reflection/too much noise
|
||||||
|
dotnet_diagnostic.CA1822.severity = none
|
||||||
|
|
||||||
|
# CA1826: Do not use Enumerable method on indexable collections
|
||||||
|
dotnet_diagnostic.CA1826.severity = suggestion
|
||||||
|
|
||||||
|
# CA1859: Use concrete types when possible for improved performance
|
||||||
|
# Involves design considerations
|
||||||
|
dotnet_diagnostic.CA1859.severity = suggestion
|
||||||
|
|
||||||
|
# CA1860: Avoid using 'Enumerable.Any()' extension method
|
||||||
|
dotnet_diagnostic.CA1860.severity = suggestion
|
||||||
|
|
||||||
|
# CA1861: Avoid constant arrays as arguments
|
||||||
|
# Outdated with collection expressions
|
||||||
|
dotnet_diagnostic.CA1861.severity = suggestion
|
||||||
|
|
||||||
|
# CA2007: Consider calling ConfigureAwait on the awaited task
|
||||||
|
dotnet_diagnostic.CA2007.severity = warning
|
||||||
|
|
||||||
|
# CA2016: Forward the 'CancellationToken' parameter to methods
|
||||||
|
# Some overloads are having special handling for debugger
|
||||||
|
dotnet_diagnostic.CA2016.severity = suggestion
|
||||||
|
|
||||||
|
# CA2021: Do not call Enumerable.Cast<T> or Enumerable.OfType<T> with incompatible types
|
||||||
|
# Causing a lot of false positives with generics
|
||||||
|
dotnet_diagnostic.CA2021.severity = none
|
||||||
|
|
||||||
|
# CA2101: Specify marshaling for P/Invoke string arguments
|
||||||
|
# Reports warning for all non-UTF16 usages on DllImport; consider migrating to LibraryImport
|
||||||
|
dotnet_diagnostic.CA2101.severity = none
|
||||||
|
|
||||||
|
# CA2201: Do not raise reserved exception types
|
||||||
|
dotnet_diagnostic.CA2201.severity = warning
|
||||||
|
|
||||||
|
# CA2208: Instantiate argument exceptions correctly
|
||||||
|
dotnet_diagnostic.CA2208.severity = suggestion
|
||||||
|
|
||||||
|
# CA2242: Test for NaN correctly
|
||||||
|
dotnet_diagnostic.CA2242.severity = warning
|
||||||
|
|
||||||
|
# Banned APIs
|
||||||
|
dotnet_diagnostic.RS0030.severity = error
|
||||||
|
|
||||||
|
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
|
||||||
|
# See: https://github.com/ppy/osu/pull/19677
|
||||||
|
dotnet_diagnostic.OSUF001.severity = none
|
@ -1,58 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RuleSet Name="osu! Rule Set" Description=" " ToolsVersion="16.0">
|
|
||||||
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
|
|
||||||
<Rule Id="CA1016" Action="None" />
|
|
||||||
<Rule Id="CA1028" Action="None" />
|
|
||||||
<Rule Id="CA1031" Action="None" />
|
|
||||||
<Rule Id="CA1034" Action="None" />
|
|
||||||
<Rule Id="CA1036" Action="None" />
|
|
||||||
<Rule Id="CA1040" Action="None" />
|
|
||||||
<Rule Id="CA1044" Action="None" />
|
|
||||||
<Rule Id="CA1051" Action="None" />
|
|
||||||
<Rule Id="CA1054" Action="None" />
|
|
||||||
<Rule Id="CA1056" Action="None" />
|
|
||||||
<Rule Id="CA1062" Action="None" />
|
|
||||||
<Rule Id="CA1063" Action="None" />
|
|
||||||
<Rule Id="CA1067" Action="None" />
|
|
||||||
<Rule Id="CA1707" Action="None" />
|
|
||||||
<Rule Id="CA1710" Action="None" />
|
|
||||||
<Rule Id="CA1714" Action="None" />
|
|
||||||
<Rule Id="CA1716" Action="None" />
|
|
||||||
<Rule Id="CA1717" Action="None" />
|
|
||||||
<Rule Id="CA1720" Action="None" />
|
|
||||||
<Rule Id="CA1721" Action="None" />
|
|
||||||
<Rule Id="CA1724" Action="None" />
|
|
||||||
<Rule Id="CA1801" Action="None" />
|
|
||||||
<Rule Id="CA1806" Action="None" />
|
|
||||||
<Rule Id="CA1812" Action="None" />
|
|
||||||
<Rule Id="CA1814" Action="None" />
|
|
||||||
<Rule Id="CA1815" Action="None" />
|
|
||||||
<Rule Id="CA1819" Action="None" />
|
|
||||||
<Rule Id="CA1822" Action="None" />
|
|
||||||
<Rule Id="CA1823" Action="None" />
|
|
||||||
<Rule Id="CA2007" Action="Warning" />
|
|
||||||
<Rule Id="CA2214" Action="None" />
|
|
||||||
<Rule Id="CA2227" Action="None" />
|
|
||||||
</Rules>
|
|
||||||
<Rules AnalyzerId="Microsoft.CodeQuality.CSharp.Analyzers" RuleNamespace="Microsoft.CodeQuality.CSharp.Analyzers">
|
|
||||||
<Rule Id="CA1001" Action="None" />
|
|
||||||
<Rule Id="CA1032" Action="None" />
|
|
||||||
</Rules>
|
|
||||||
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
|
|
||||||
<Rule Id="CA1303" Action="None" />
|
|
||||||
<Rule Id="CA1304" Action="None" />
|
|
||||||
<Rule Id="CA1305" Action="None" />
|
|
||||||
<Rule Id="CA1307" Action="None" />
|
|
||||||
<Rule Id="CA1308" Action="None" />
|
|
||||||
<Rule Id="CA1816" Action="None" />
|
|
||||||
<Rule Id="CA1826" Action="None" />
|
|
||||||
<Rule Id="CA2000" Action="None" />
|
|
||||||
<Rule Id="CA2008" Action="None" />
|
|
||||||
<Rule Id="CA2213" Action="None" />
|
|
||||||
<Rule Id="CA2235" Action="None" />
|
|
||||||
</Rules>
|
|
||||||
<Rules AnalyzerId="Microsoft.NetCore.CSharp.Analyzers" RuleNamespace="Microsoft.NetCore.CSharp.Analyzers">
|
|
||||||
<Rule Id="CA1309" Action="Warning" />
|
|
||||||
<Rule Id="CA2201" Action="Warning" />
|
|
||||||
</Rules>
|
|
||||||
</RuleSet>
|
|
@ -18,9 +18,21 @@
|
|||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
|
<!-- Rider compatibility: .globalconfig needs to be explicitly referenced instead of using the global file name. -->
|
||||||
|
<GlobalAnalyzerConfigFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\osu.globalconfig" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Code Analysis">
|
<PropertyGroup Label="Code Analysis">
|
||||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
<AnalysisMode>Default</AnalysisMode>
|
||||||
|
<AnalysisModeDesign>Default</AnalysisModeDesign>
|
||||||
|
<AnalysisModeDocumentation>Recommended</AnalysisModeDocumentation>
|
||||||
|
<AnalysisModeGlobalization>Recommended</AnalysisModeGlobalization>
|
||||||
|
<AnalysisModeInteroperability>Recommended</AnalysisModeInteroperability>
|
||||||
|
<AnalysisModeMaintainability>Recommended</AnalysisModeMaintainability>
|
||||||
|
<AnalysisModeNaming>Default</AnalysisModeNaming>
|
||||||
|
<AnalysisModePerformance>Minimum</AnalysisModePerformance>
|
||||||
|
<AnalysisModeReliability>Recommended</AnalysisModeReliability>
|
||||||
|
<AnalysisModeSecurity>Default</AnalysisModeSecurity>
|
||||||
|
<AnalysisModeUsage>Default</AnalysisModeUsage>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Documentation">
|
<PropertyGroup Label="Documentation">
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1118.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1224.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
@ -175,7 +175,7 @@ namespace osu.Desktop
|
|||||||
presence.State = clampLength(activity.Value.GetStatus(hideIdentifiableInformation));
|
presence.State = clampLength(activity.Value.GetStatus(hideIdentifiableInformation));
|
||||||
presence.Details = clampLength(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty);
|
presence.Details = clampLength(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty);
|
||||||
|
|
||||||
if (getBeatmapID(activity.Value) is int beatmapId && beatmapId > 0)
|
if (activity.Value.GetBeatmapID(hideIdentifiableInformation) is int beatmapId && beatmapId > 0)
|
||||||
{
|
{
|
||||||
presence.Buttons = new[]
|
presence.Buttons = new[]
|
||||||
{
|
{
|
||||||
@ -333,20 +333,6 @@ namespace osu.Desktop
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int? getBeatmapID(UserActivity activity)
|
|
||||||
{
|
|
||||||
switch (activity)
|
|
||||||
{
|
|
||||||
case UserActivity.InGame game:
|
|
||||||
return game.BeatmapID;
|
|
||||||
|
|
||||||
case UserActivity.EditingBeatmap edit:
|
|
||||||
return edit.BeatmapID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
if (multiplayerClient.IsNotNull())
|
if (multiplayerClient.IsNotNull())
|
||||||
|
@ -134,7 +134,6 @@ namespace osu.Desktop
|
|||||||
if (iconStream != null)
|
if (iconStream != null)
|
||||||
host.Window.SetIconFromStream(iconStream);
|
host.Window.SetIconFromStream(iconStream);
|
||||||
|
|
||||||
host.Window.CursorState |= CursorState.Hidden;
|
|
||||||
host.Window.Title = Name;
|
host.Window.Title = Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
var hostOptions = new HostOptions
|
var hostOptions = new HostOptions
|
||||||
{
|
{
|
||||||
IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null,
|
IPCPipeName = !tournamentClient ? OsuGame.IPC_PIPE_NAME : null,
|
||||||
FriendlyGameName = OsuGameBase.GAME_NAME,
|
FriendlyGameName = OsuGameBase.GAME_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,15 +148,7 @@ namespace osu.Desktop.Windows
|
|||||||
foreach (var association in uri_associations)
|
foreach (var association in uri_associations)
|
||||||
association.UpdateDescription(getLocalisedString(association.Description));
|
association.UpdateDescription(getLocalisedString(association.Description));
|
||||||
|
|
||||||
string getLocalisedString(LocalisableString s)
|
string getLocalisedString(LocalisableString s) => localisation?.GetLocalisedString(s) ?? s.ToString();
|
||||||
{
|
|
||||||
if (localisation == null)
|
|
||||||
return s.ToString();
|
|
||||||
|
|
||||||
var b = localisation.GetLocalisedBindableString(s);
|
|
||||||
b.UnbindAll();
|
|
||||||
return b.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Native interop
|
#region Native interop
|
||||||
|
15
osu.Game.Rulesets.Catch.Tests.iOS/AppDelegate.cs
Normal file
15
osu.Game.Rulesets.Catch.Tests.iOS/AppDelegate.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using Foundation;
|
||||||
|
using osu.Framework.iOS;
|
||||||
|
using osu.Game.Tests;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests.iOS
|
||||||
|
{
|
||||||
|
[Register("AppDelegate")]
|
||||||
|
public class AppDelegate : GameApplicationDelegate
|
||||||
|
{
|
||||||
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,15 @@
|
|||||||
// 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 osu.Framework.iOS;
|
using UIKit;
|
||||||
using osu.Game.Tests;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests.iOS
|
namespace osu.Game.Rulesets.Catch.Tests.iOS
|
||||||
{
|
{
|
||||||
public static class Application
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
GameApplication.Main(new OsuTestBrowser());
|
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,10 +10,12 @@ using osu.Framework.Graphics.Primitives;
|
|||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -54,6 +56,12 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorBeatmap? editorBeatmap { get; set; }
|
private EditorBeatmap? editorBeatmap { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IEditorChangeHandler? changeHandler { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private BindableBeatDivisor? beatDivisor { get; set; }
|
||||||
|
|
||||||
public JuiceStreamSelectionBlueprint(JuiceStream hitObject)
|
public JuiceStreamSelectionBlueprint(JuiceStream hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
@ -119,6 +127,20 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
|
{
|
||||||
|
if (!IsSelected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (e.Key == Key.F && e.ControlPressed && e.ShiftPressed)
|
||||||
|
{
|
||||||
|
convertToStream();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void onDefaultsApplied(HitObject _)
|
private void onDefaultsApplied(HitObject _)
|
||||||
{
|
{
|
||||||
computeObjectBounds();
|
computeObjectBounds();
|
||||||
@ -168,6 +190,50 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
lastSliderPathVersion = HitObject.Path.Version.Value;
|
lastSliderPathVersion = HitObject.Path.Version.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// duplicated in `SliderSelectionBlueprint.convertToStream()`
|
||||||
|
// consider extracting common helper when applying changes here
|
||||||
|
private void convertToStream()
|
||||||
|
{
|
||||||
|
if (editorBeatmap == null || beatDivisor == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var timingPoint = editorBeatmap.ControlPointInfo.TimingPointAt(HitObject.StartTime);
|
||||||
|
double streamSpacing = timingPoint.BeatLength / beatDivisor.Value;
|
||||||
|
|
||||||
|
changeHandler?.BeginChange();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
double time = HitObject.StartTime;
|
||||||
|
|
||||||
|
while (!Precision.DefinitelyBigger(time, HitObject.GetEndTime(), 1))
|
||||||
|
{
|
||||||
|
// positionWithRepeats is a fractional number in the range of [0, HitObject.SpanCount()]
|
||||||
|
// and indicates how many fractional spans of a slider have passed up to time.
|
||||||
|
double positionWithRepeats = (time - HitObject.StartTime) / HitObject.Duration * HitObject.SpanCount();
|
||||||
|
double pathPosition = positionWithRepeats - (int)positionWithRepeats;
|
||||||
|
// every second span is in the reverse direction - need to reverse the path position.
|
||||||
|
if (positionWithRepeats % 2 >= 1)
|
||||||
|
pathPosition = 1 - pathPosition;
|
||||||
|
|
||||||
|
float fruitXValue = HitObject.OriginalX + HitObject.Path.PositionAt(pathPosition).X;
|
||||||
|
|
||||||
|
editorBeatmap.Add(new Fruit
|
||||||
|
{
|
||||||
|
StartTime = time,
|
||||||
|
OriginalX = fruitXValue,
|
||||||
|
NewCombo = i == 0 && HitObject.NewCombo,
|
||||||
|
Samples = HitObject.Samples.Select(s => s.With()).ToList()
|
||||||
|
});
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
time = HitObject.StartTime + i * streamSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
editorBeatmap.Remove(HitObject);
|
||||||
|
|
||||||
|
changeHandler?.EndChange();
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<MenuItem> getContextMenuItems()
|
private IEnumerable<MenuItem> getContextMenuItems()
|
||||||
{
|
{
|
||||||
yield return new OsuMenuItem("Add vertex", MenuItemType.Standard, () =>
|
yield return new OsuMenuItem("Add vertex", MenuItemType.Standard, () =>
|
||||||
@ -177,6 +243,11 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
{
|
{
|
||||||
Hotkey = new Hotkey(new KeyCombination(InputKey.Control, InputKey.MouseLeft))
|
Hotkey = new Hotkey(new KeyCombination(InputKey.Control, InputKey.MouseLeft))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
yield return new OsuMenuItem("Convert to stream", MenuItemType.Destructive, convertToStream)
|
||||||
|
{
|
||||||
|
Hotkey = new Hotkey(new KeyCombination(InputKey.Control, InputKey.Shift, InputKey.F))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
15
osu.Game.Rulesets.Mania.Tests.iOS/AppDelegate.cs
Normal file
15
osu.Game.Rulesets.Mania.Tests.iOS/AppDelegate.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using Foundation;
|
||||||
|
using osu.Framework.iOS;
|
||||||
|
using osu.Game.Tests;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.iOS
|
||||||
|
{
|
||||||
|
[Register("AppDelegate")]
|
||||||
|
public class AppDelegate : GameApplicationDelegate
|
||||||
|
{
|
||||||
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,15 @@
|
|||||||
// 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 osu.Framework.iOS;
|
using UIKit;
|
||||||
using osu.Game.Tests;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.iOS
|
namespace osu.Game.Rulesets.Mania.Tests.iOS
|
||||||
{
|
{
|
||||||
public static class Application
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
GameApplication.Main(new OsuTestBrowser());
|
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,9 +22,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
[TestCase("basic")]
|
[TestCase("basic")]
|
||||||
[TestCase("zero-length-slider")]
|
[TestCase("zero-length-slider")]
|
||||||
|
[TestCase("mania-specific-spinner")]
|
||||||
[TestCase("20544")]
|
[TestCase("20544")]
|
||||||
[TestCase("100374")]
|
[TestCase("100374")]
|
||||||
[TestCase("1450162")]
|
[TestCase("1450162")]
|
||||||
|
[TestCase("4869637")]
|
||||||
public void Test(string name) => base.Test(name);
|
public void Test(string name) => base.Test(name);
|
||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
|
File diff suppressed because one or more lines are too long
1442
osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/4869637.osu
Normal file
1442
osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/4869637.osu
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"Mappings": [
|
||||||
|
{
|
||||||
|
"RandomW": 273071671,
|
||||||
|
"RandomX": 842502087,
|
||||||
|
"RandomY": 3579807591,
|
||||||
|
"RandomZ": 273326509,
|
||||||
|
"StartTime": 11783.0,
|
||||||
|
"Objects": [
|
||||||
|
{
|
||||||
|
"StartTime": 11783.0,
|
||||||
|
"EndTime": 15116.0,
|
||||||
|
"Column": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RandomW": 2659271247,
|
||||||
|
"RandomX": 3579807591,
|
||||||
|
"RandomY": 273326509,
|
||||||
|
"RandomZ": 273071671,
|
||||||
|
"StartTime": 91545.0,
|
||||||
|
"Objects": [
|
||||||
|
{
|
||||||
|
"StartTime": 91545.0,
|
||||||
|
"EndTime": 92735.0,
|
||||||
|
"Column": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RandomW": 3083635271,
|
||||||
|
"RandomX": 273326509,
|
||||||
|
"RandomY": 273071671,
|
||||||
|
"RandomZ": 2659271247,
|
||||||
|
"StartTime": 152497.0,
|
||||||
|
"Objects": [
|
||||||
|
{
|
||||||
|
"StartTime": 152497.0,
|
||||||
|
"EndTime": 153687.0,
|
||||||
|
"Column": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RandomW": 4073591514,
|
||||||
|
"RandomX": 273071671,
|
||||||
|
"RandomY": 2659271247,
|
||||||
|
"RandomZ": 3083635271,
|
||||||
|
"StartTime": 231545.0,
|
||||||
|
"Objects": [
|
||||||
|
{
|
||||||
|
"StartTime": 231545.0,
|
||||||
|
"EndTime": 232974.0,
|
||||||
|
"Column": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Mode: 3
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:5
|
||||||
|
CircleSize:4
|
||||||
|
OverallDifficulty:5
|
||||||
|
ApproachRate:0
|
||||||
|
SliderMultiplier:2.6
|
||||||
|
SliderTickRate:1
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
355,476.190476190476,4,2,1,60,1,0
|
||||||
|
60652,-100,4,2,1,60,0,1
|
||||||
|
92735,-100,4,2,1,60,0,0
|
||||||
|
121485,-100,4,2,1,60,0,1
|
||||||
|
153688,-100,4,2,1,60,0,0
|
||||||
|
182497,-100,4,2,1,60,0,1
|
||||||
|
213688,-100,4,2,1,60,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
256,192,11783,12,0,15116,0:0:0:0:
|
||||||
|
256,192,91545,12,0,92735,0:0:0:0:
|
||||||
|
256,192,152497,12,0,153687,0:0:0:0:
|
||||||
|
256,192,231545,12,0,232974,0:0:0:0:
|
@ -7,11 +7,13 @@ using System.Linq;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
using osu.Game.Rulesets.Scoring.Legacy;
|
using osu.Game.Rulesets.Scoring.Legacy;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -124,16 +126,109 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
|
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (original is ManiaHitObject maniaOriginal)
|
LegacyHitObjectType legacyType;
|
||||||
{
|
|
||||||
yield return maniaOriginal;
|
|
||||||
|
|
||||||
yield break;
|
switch (original)
|
||||||
|
{
|
||||||
|
case ManiaHitObject maniaObj:
|
||||||
|
{
|
||||||
|
yield return maniaObj;
|
||||||
|
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IHasLegacyHitObjectType legacy:
|
||||||
|
legacyType = legacy.LegacyType & LegacyHitObjectType.ObjectTypes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IHasPath:
|
||||||
|
legacyType = LegacyHitObjectType.Slider;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IHasDuration:
|
||||||
|
legacyType = LegacyHitObjectType.Hold;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
legacyType = LegacyHitObjectType.Circle;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
|
double startTime = original.StartTime;
|
||||||
foreach (ManiaHitObject obj in objects)
|
double endTime = (original as IHasDuration)?.EndTime ?? startTime;
|
||||||
yield return obj;
|
Vector2 position = (original as IHasPosition)?.Position ?? Vector2.Zero;
|
||||||
|
|
||||||
|
PatternGenerator conversion;
|
||||||
|
|
||||||
|
switch (legacyType)
|
||||||
|
{
|
||||||
|
case LegacyHitObjectType.Circle:
|
||||||
|
if (IsForCurrentRuleset)
|
||||||
|
{
|
||||||
|
conversion = new PassThroughPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
|
||||||
|
recordNote(startTime, position);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Note: The density is used during the pattern generator constructor, and intentionally computed first.
|
||||||
|
computeDensity(startTime);
|
||||||
|
conversion = new HitCirclePatternGenerator(Random, original, beatmap, TotalColumns, lastPattern, lastTime, lastPosition, density, lastStair);
|
||||||
|
recordNote(startTime, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LegacyHitObjectType.Slider:
|
||||||
|
if (IsForCurrentRuleset)
|
||||||
|
{
|
||||||
|
conversion = new PassThroughPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
|
||||||
|
recordNote(original.StartTime, position);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var generator = new SliderPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
|
||||||
|
conversion = generator;
|
||||||
|
|
||||||
|
for (int i = 0; i <= generator.SpanCount; i++)
|
||||||
|
{
|
||||||
|
double time = original.StartTime + generator.SegmentDuration * i;
|
||||||
|
|
||||||
|
recordNote(time, position);
|
||||||
|
computeDensity(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LegacyHitObjectType.Spinner:
|
||||||
|
// Note: Some older mania-specific beatmaps can have spinners that are converted rather than passed through.
|
||||||
|
// Newer beatmaps will usually use the "hold" hitobject type below.
|
||||||
|
conversion = new SpinnerPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
|
||||||
|
recordNote(endTime, new Vector2(256, 192));
|
||||||
|
computeDensity(endTime);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LegacyHitObjectType.Hold:
|
||||||
|
conversion = new PassThroughPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
|
||||||
|
recordNote(endTime, position);
|
||||||
|
computeDensity(endTime);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentException($"Invalid legacy object type: {legacyType}", nameof(original));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var newPattern in conversion.Generate())
|
||||||
|
{
|
||||||
|
if (conversion is HitCirclePatternGenerator circleGenerator)
|
||||||
|
lastStair = circleGenerator.StairType;
|
||||||
|
|
||||||
|
if (conversion is HitCirclePatternGenerator || conversion is SliderPatternGenerator)
|
||||||
|
lastPattern = newPattern;
|
||||||
|
|
||||||
|
foreach (var obj in newPattern.HitObjects)
|
||||||
|
yield return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly LimitedCapacityQueue<double> prevNoteTimes = new LimitedCapacityQueue<double>(max_notes_for_density);
|
private readonly LimitedCapacityQueue<double> prevNoteTimes = new LimitedCapacityQueue<double>(max_notes_for_density);
|
||||||
@ -156,135 +251,5 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
lastTime = time;
|
lastTime = time;
|
||||||
lastPosition = position;
|
lastPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Method that generates hit objects for osu!mania specific beatmaps.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="original">The original hit object.</param>
|
|
||||||
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
|
|
||||||
/// <returns>The hit objects generated.</returns>
|
|
||||||
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, IBeatmap originalBeatmap)
|
|
||||||
{
|
|
||||||
var generator = new SpecificBeatmapPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern);
|
|
||||||
|
|
||||||
foreach (var newPattern in generator.Generate())
|
|
||||||
{
|
|
||||||
lastPattern = newPattern;
|
|
||||||
|
|
||||||
foreach (var obj in newPattern.HitObjects)
|
|
||||||
yield return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Method that generates hit objects for non-osu!mania beatmaps.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="original">The original hit object.</param>
|
|
||||||
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
|
|
||||||
/// <returns>The hit objects generated.</returns>
|
|
||||||
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, IBeatmap originalBeatmap)
|
|
||||||
{
|
|
||||||
Patterns.PatternGenerator? conversion = null;
|
|
||||||
|
|
||||||
switch (original)
|
|
||||||
{
|
|
||||||
case IHasPath:
|
|
||||||
{
|
|
||||||
var generator = new PathObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern);
|
|
||||||
conversion = generator;
|
|
||||||
|
|
||||||
var positionData = original as IHasPosition;
|
|
||||||
|
|
||||||
for (int i = 0; i <= generator.SpanCount; i++)
|
|
||||||
{
|
|
||||||
double time = original.StartTime + generator.SegmentDuration * i;
|
|
||||||
|
|
||||||
recordNote(time, positionData?.Position ?? Vector2.Zero);
|
|
||||||
computeDensity(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IHasDuration endTimeData:
|
|
||||||
{
|
|
||||||
conversion = new EndTimeObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern);
|
|
||||||
|
|
||||||
recordNote(endTimeData.EndTime, new Vector2(256, 192));
|
|
||||||
computeDensity(endTimeData.EndTime);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IHasPosition positionData:
|
|
||||||
{
|
|
||||||
computeDensity(original.StartTime);
|
|
||||||
|
|
||||||
conversion = new HitObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern, lastTime, lastPosition, density, lastStair);
|
|
||||||
|
|
||||||
recordNote(original.StartTime, positionData.Position);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conversion == null)
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
foreach (var newPattern in conversion.Generate())
|
|
||||||
{
|
|
||||||
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
|
|
||||||
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
|
|
||||||
|
|
||||||
foreach (var obj in newPattern.HitObjects)
|
|
||||||
yield return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A pattern generator for osu!mania-specific beatmaps.
|
|
||||||
/// </summary>
|
|
||||||
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
|
|
||||||
{
|
|
||||||
public SpecificBeatmapPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
|
|
||||||
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IEnumerable<Pattern> Generate()
|
|
||||||
{
|
|
||||||
yield return generate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Pattern generate()
|
|
||||||
{
|
|
||||||
var positionData = HitObject as IHasXPosition;
|
|
||||||
|
|
||||||
int column = GetColumn(positionData?.X ?? 0);
|
|
||||||
|
|
||||||
var pattern = new Pattern();
|
|
||||||
|
|
||||||
if (HitObject is IHasDuration endTimeData)
|
|
||||||
{
|
|
||||||
pattern.Add(new HoldNote
|
|
||||||
{
|
|
||||||
StartTime = HitObject.StartTime,
|
|
||||||
Duration = endTimeData.Duration,
|
|
||||||
Column = column,
|
|
||||||
Samples = HitObject.Samples,
|
|
||||||
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples ?? HoldNote.CreateDefaultNodeSamples(HitObject)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (HitObject is IHasXPosition)
|
|
||||||
{
|
|
||||||
pattern.Add(new Note
|
|
||||||
{
|
|
||||||
StartTime = HitObject.StartTime,
|
|
||||||
Samples = HitObject.Samples,
|
|
||||||
Column = column
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,16 @@ using osu.Game.Utils;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
internal class HitObjectPatternGenerator : PatternGenerator
|
/// <summary>
|
||||||
|
/// Converter for legacy "HitCircle" hit objects.
|
||||||
|
/// </summary>
|
||||||
|
internal class HitCirclePatternGenerator : LegacyPatternGenerator
|
||||||
{
|
{
|
||||||
public PatternType StairType { get; private set; }
|
public PatternType StairType { get; private set; }
|
||||||
|
|
||||||
private readonly PatternType convertType;
|
private readonly PatternType convertType;
|
||||||
|
|
||||||
public HitObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition,
|
public HitCirclePatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition,
|
||||||
double density, PatternType lastStair)
|
double density, PatternType lastStair)
|
||||||
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
||||||
{
|
{
|
||||||
@ -114,10 +117,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
|
if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
|
||||||
// If we convert to 7K + 1, let's not overload the special key
|
// If we convert to 7K + 1, let's not overload the special key
|
||||||
&& (TotalColumns != 8 || lastColumn != 0)
|
&& (TotalColumns != 8 || lastColumn != 0)
|
||||||
// Make sure the last column was not the centre column
|
// Make sure the last column was not the centre column
|
||||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||||
{
|
{
|
||||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
int column = RandomStart + TotalColumns - lastColumn - 1;
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
@ -15,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A pattern generator for legacy hit objects.
|
/// A pattern generator for legacy hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal abstract class PatternGenerator : Patterns.PatternGenerator
|
internal abstract class LegacyPatternGenerator : PatternGenerator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The column index at which to start generating random notes.
|
/// The column index at which to start generating random notes.
|
||||||
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly LegacyRandom Random;
|
protected readonly LegacyRandom Random;
|
||||||
|
|
||||||
protected PatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, Pattern previousPattern, int totalColumns)
|
protected LegacyPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, Pattern previousPattern, int totalColumns)
|
||||||
: base(hitObject, beatmap, totalColumns, previousPattern)
|
: base(hitObject, beatmap, totalColumns, previousPattern)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(random);
|
ArgumentNullException.ThrowIfNull(random);
|
||||||
@ -96,8 +94,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
if (conversionDifficulty != null)
|
if (conversionDifficulty != null)
|
||||||
return conversionDifficulty.Value;
|
return conversionDifficulty.Value;
|
||||||
|
|
||||||
HitObject lastObject = Beatmap.HitObjects.LastOrDefault();
|
HitObject? lastObject = Beatmap.HitObjects.LastOrDefault();
|
||||||
HitObject firstObject = Beatmap.HitObjects.FirstOrDefault();
|
HitObject? firstObject = Beatmap.HitObjects.FirstOrDefault();
|
||||||
|
|
||||||
// Drain time in seconds
|
// Drain time in seconds
|
||||||
int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - Beatmap.TotalBreakTime) / 1000);
|
int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - Beatmap.TotalBreakTime) / 1000);
|
||||||
@ -132,13 +130,13 @@ 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="Patterns.PatternGenerator.TotalColumns">TotalColumns</see> is used.</param>
|
/// <param name="upperBound">The maximum column index. If null, <see cref="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
|
||||||
/// <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
|
/// <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
|
||||||
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
|
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
|
||||||
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 ??= RandomStart;
|
lowerBound ??= RandomStart;
|
||||||
@ -189,7 +187,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="Patterns.PatternGenerator.TotalColumns"/> is used.</param>
|
/// <param name="upperBound">The maximum column index. If null, <see cref="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>
|
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A simple generator which, for any object, if the hitobject has an end time
|
||||||
|
/// it becomes a <see cref="HoldNote"/> or otherwise a <see cref="Note"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal class PassThroughPatternGenerator : LegacyPatternGenerator
|
||||||
|
{
|
||||||
|
public PassThroughPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
|
||||||
|
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<Pattern> Generate()
|
||||||
|
{
|
||||||
|
var positionData = HitObject as IHasXPosition;
|
||||||
|
int column = GetColumn(positionData?.X ?? 0);
|
||||||
|
|
||||||
|
var pattern = new Pattern();
|
||||||
|
|
||||||
|
if (HitObject is IHasDuration endTimeData)
|
||||||
|
{
|
||||||
|
pattern.Add(new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = HitObject.StartTime,
|
||||||
|
Duration = endTimeData.Duration,
|
||||||
|
Column = column,
|
||||||
|
Samples = HitObject.Samples,
|
||||||
|
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples ?? HoldNote.CreateDefaultNodeSamples(HitObject)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pattern.Add(new Note
|
||||||
|
{
|
||||||
|
StartTime = HitObject.StartTime,
|
||||||
|
Samples = HitObject.Samples,
|
||||||
|
Column = column
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -19,9 +17,9 @@ using osu.Game.Utils;
|
|||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A pattern generator for IHasDistance hit objects.
|
/// Converter for legacy "Slider" hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class PathObjectPatternGenerator : PatternGenerator
|
internal class SliderPatternGenerator : LegacyPatternGenerator
|
||||||
{
|
{
|
||||||
public readonly int StartTime;
|
public readonly int StartTime;
|
||||||
public readonly int EndTime;
|
public readonly int EndTime;
|
||||||
@ -30,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
private PatternType convertType;
|
private PatternType convertType;
|
||||||
|
|
||||||
public PathObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
|
public SliderPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
|
||||||
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
||||||
{
|
{
|
||||||
convertType = PatternType.None;
|
convertType = PatternType.None;
|
||||||
@ -484,9 +482,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// Retrieves the list of node samples that occur at time greater than or equal to <paramref name="time"/>.
|
/// Retrieves the list of node samples that occur at time greater than or equal to <paramref name="time"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to retrieve node samples at.</param>
|
/// <param name="time">The time to retrieve node samples at.</param>
|
||||||
private IList<IList<HitSampleInfo>> nodeSamplesAt(int time)
|
private IList<IList<HitSampleInfo>>? nodeSamplesAt(int time)
|
||||||
{
|
{
|
||||||
if (!(HitObject is IHasPathWithRepeats curveData))
|
if (HitObject is not IHasPathWithRepeats curveData)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
int index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration;
|
int index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration;
|
@ -12,12 +12,15 @@ using osu.Game.Utils;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
internal class EndTimeObjectPatternGenerator : PatternGenerator
|
/// <summary>
|
||||||
|
/// Converter for legacy "Spinner" hit objects.
|
||||||
|
/// </summary>
|
||||||
|
internal class SpinnerPatternGenerator : LegacyPatternGenerator
|
||||||
{
|
{
|
||||||
private readonly int endTime;
|
private readonly int endTime;
|
||||||
private readonly PatternType convertType;
|
private readonly PatternType convertType;
|
||||||
|
|
||||||
public EndTimeObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
|
public SpinnerPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
|
||||||
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
||||||
{
|
{
|
||||||
endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0);
|
endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0);
|
@ -1,9 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
|
||||||
@ -14,8 +13,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class Pattern
|
internal class Pattern
|
||||||
{
|
{
|
||||||
private List<ManiaHitObject> hitObjects;
|
private List<ManiaHitObject>? hitObjects;
|
||||||
private HashSet<int> containedColumns;
|
private HashSet<int>? containedColumns;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All the hit objects contained in this pattern.
|
/// All the hit objects contained in this pattern.
|
||||||
@ -72,6 +71,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
|||||||
containedColumns?.Clear();
|
containedColumns?.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(hitObjects), nameof(containedColumns))]
|
||||||
private void prepareStorage()
|
private void prepareStorage()
|
||||||
{
|
{
|
||||||
hitObjects ??= new List<ManiaHitObject>();
|
hitObjects ??= new List<ManiaHitObject>();
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
|
|||||||
{
|
{
|
||||||
Caption = "Use special (N+1) style",
|
Caption = "Use special (N+1) style",
|
||||||
HintText = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6K (5+1) or 8K (7+1) configurations.",
|
HintText = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6K (5+1) or 8K (7+1) configurations.",
|
||||||
Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
|
Current = { Value = Beatmap.SpecialStyle }
|
||||||
},
|
},
|
||||||
healthDrainSlider = new FormSliderBar<float>
|
healthDrainSlider = new FormSliderBar<float>
|
||||||
{
|
{
|
||||||
@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
|
|||||||
// for now, update these on commit rather than making BeatmapMetadata bindables.
|
// for now, update these on commit rather than making BeatmapMetadata bindables.
|
||||||
// after switching database engines we can reconsider if switching to bindables is a good direction.
|
// after switching database engines we can reconsider if switching to bindables is a good direction.
|
||||||
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value;
|
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value;
|
||||||
Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value;
|
Beatmap.SpecialStyle = specialStyle.Current.Value;
|
||||||
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
|
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
|
||||||
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
||||||
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
||||||
|
@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 1: return colour_cyan;
|
case 1: return colour_cyan;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 2: return colour_cyan;
|
case 2: return colour_cyan;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 3: return colour_purple;
|
case 3: return colour_purple;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 4: return colour_cyan;
|
case 4: return colour_cyan;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
@ -224,7 +224,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 5: return colour_pink;
|
case 5: return colour_pink;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
@ -244,7 +244,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 6: return colour_pink;
|
case 6: return colour_pink;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 8:
|
case 8:
|
||||||
@ -266,7 +266,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 7: return colour_purple;
|
case 7: return colour_purple;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 9:
|
case 9:
|
||||||
@ -290,7 +290,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 8: return colour_purple;
|
case 8: return colour_purple;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 10:
|
case 10:
|
||||||
@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 9: return colour_purple;
|
case 9: return colour_purple;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,7 +339,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case 5: return colour_green;
|
case 5: return colour_green;
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,10 +164,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
|
|
||||||
private Drawable getResult(HitResult result)
|
private Drawable getResult(HitResult result)
|
||||||
{
|
{
|
||||||
if (!hit_result_mapping.ContainsKey(result))
|
if (!hit_result_mapping.TryGetValue(result, out var value))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
string filename = this.GetManiaSkinConfig<string>(hit_result_mapping[result])?.Value
|
string filename = this.GetManiaSkinConfig<string>(value)?.Value
|
||||||
?? default_hit_result_skin_filenames[result];
|
?? default_hit_result_skin_filenames[result];
|
||||||
|
|
||||||
var animation = this.GetAnimation(filename, true, true, frameLength: 1000 / 20d);
|
var animation = this.GetAnimation(filename, true, true, frameLength: 1000 / 20d);
|
||||||
|
15
osu.Game.Rulesets.Osu.Tests.iOS/AppDelegate.cs
Normal file
15
osu.Game.Rulesets.Osu.Tests.iOS/AppDelegate.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using Foundation;
|
||||||
|
using osu.Framework.iOS;
|
||||||
|
using osu.Game.Tests;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.iOS
|
||||||
|
{
|
||||||
|
[Register("AppDelegate")]
|
||||||
|
public class AppDelegate : GameApplicationDelegate
|
||||||
|
{
|
||||||
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,15 @@
|
|||||||
// 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 osu.Framework.iOS;
|
using UIKit;
|
||||||
using osu.Game.Tests;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests.iOS
|
namespace osu.Game.Rulesets.Osu.Tests.iOS
|
||||||
{
|
{
|
||||||
public static class Application
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
GameApplication.Main(new OsuTestBrowser());
|
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,7 +10,9 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
@ -231,6 +233,193 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddAssert("slider still has 2 anchors", () => secondSlider.Path.ControlPoints.Count, () => Is.EqualTo(2));
|
AddAssert("slider still has 2 anchors", () => secondSlider.Path.ControlPoints.Count, () => Is.EqualTo(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestControlClickDoesNotDiscardExistingSelectionEvenIfNothingHit()
|
||||||
|
{
|
||||||
|
var firstSlider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Position = new Vector2(0, 0),
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint(),
|
||||||
|
new PathControlPoint(new Vector2(100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("add object", () => EditorBeatmap.AddRange([firstSlider]));
|
||||||
|
AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.AddRange([firstSlider]));
|
||||||
|
|
||||||
|
AddStep("move mouse to middle of playfield", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddStep("control-click left mouse", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
AddAssert("selection preserved", () => EditorBeatmap.SelectedHitObjects.Count, () => Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestQuickDeleteOnUnselectedControlPointOnlyRemovesThatControlPoint()
|
||||||
|
{
|
||||||
|
var slider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Position = new Vector2(100, 100),
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint { Type = PathType.LINEAR },
|
||||||
|
new PathControlPoint(new Vector2(100, 0)),
|
||||||
|
new PathControlPoint(new Vector2(100)),
|
||||||
|
new PathControlPoint(new Vector2(0, 100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AddStep("add slider", () => EditorBeatmap.Add(slider));
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
|
||||||
|
AddStep("select second node", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(1));
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddStep("also select third node", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(2));
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
AddStep("quick-delete fourth node", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(3));
|
||||||
|
InputManager.Click(MouseButton.Middle);
|
||||||
|
});
|
||||||
|
AddUntilStep("slider not deleted", () => EditorBeatmap.HitObjects.OfType<Slider>().Count(), () => Is.EqualTo(1));
|
||||||
|
AddUntilStep("slider path has 3 nodes", () => EditorBeatmap.HitObjects.OfType<Slider>().Single().Path.ControlPoints.Count, () => Is.EqualTo(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestQuickDeleteOnSelectedControlPointRemovesEntireSelection()
|
||||||
|
{
|
||||||
|
var slider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Position = new Vector2(100, 100),
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint { Type = PathType.LINEAR },
|
||||||
|
new PathControlPoint(new Vector2(100, 0)),
|
||||||
|
new PathControlPoint(new Vector2(100)),
|
||||||
|
new PathControlPoint(new Vector2(0, 100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AddStep("add slider", () => EditorBeatmap.Add(slider));
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
|
||||||
|
AddStep("select second node", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(1));
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddStep("also select third node", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(2));
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
AddStep("quick-delete second node", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(1));
|
||||||
|
InputManager.Click(MouseButton.Middle);
|
||||||
|
});
|
||||||
|
AddUntilStep("slider not deleted", () => EditorBeatmap.HitObjects.OfType<Slider>().Count(), () => Is.EqualTo(1));
|
||||||
|
AddUntilStep("slider path has 2 nodes", () => EditorBeatmap.HitObjects.OfType<Slider>().Single().Path.ControlPoints.Count, () => Is.EqualTo(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSliderDragMarkerDoesNotBlockControlPointContextMenu()
|
||||||
|
{
|
||||||
|
var slider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Position = new Vector2(100, 100),
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint { Type = PathType.LINEAR },
|
||||||
|
new PathControlPoint(new Vector2(50, 100)),
|
||||||
|
new PathControlPoint(new Vector2(145, 100)),
|
||||||
|
},
|
||||||
|
ExpectedDistance = { Value = 162.62 }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
AddStep("add slider", () => EditorBeatmap.Add(slider));
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
|
||||||
|
AddStep("select last node", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<PathControlPointPiece<Slider>>().Last());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddStep("right click node", () => InputManager.Click(MouseButton.Right));
|
||||||
|
AddUntilStep("context menu open", () => this.ChildrenOfType<ContextMenuContainer>().Single().ChildrenOfType<Menu>().All(m => m.State == MenuState.Open));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSliderDragMarkerBlocksSelectionOfObjectsUnderneath()
|
||||||
|
{
|
||||||
|
var firstSlider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Position = new Vector2(10, 50),
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint(),
|
||||||
|
new PathControlPoint(new Vector2(100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var secondSlider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 500,
|
||||||
|
Position = new Vector2(200, 0),
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint(),
|
||||||
|
new PathControlPoint(new Vector2(-100, 100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("add objects", () => EditorBeatmap.AddRange(new HitObject[] { firstSlider, secondSlider }));
|
||||||
|
AddStep("select second slider", () => EditorBeatmap.SelectedHitObjects.Add(secondSlider));
|
||||||
|
|
||||||
|
AddStep("move to marker", () =>
|
||||||
|
{
|
||||||
|
var marker = this.ChildrenOfType<SliderEndDragMarker>().First();
|
||||||
|
var position = (marker.ScreenSpaceDrawQuad.TopRight + marker.ScreenSpaceDrawQuad.BottomRight) / 2;
|
||||||
|
InputManager.MoveMouseTo(position);
|
||||||
|
});
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second slider still selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondSlider));
|
||||||
|
}
|
||||||
|
|
||||||
private ComposeBlueprintContainer blueprintContainer
|
private ComposeBlueprintContainer blueprintContainer
|
||||||
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
private void gridSizeIs(int size)
|
private void gridSizeIs(int size)
|
||||||
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType<RectangularPositionSnapGrid>().Single().Spacing.Value == new Vector2(size)
|
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType<RectangularPositionSnapGrid>().Single().Spacing.Value == new Vector2(size)
|
||||||
&& EditorBeatmap.BeatmapInfo.GridSize == size);
|
&& EditorBeatmap.GridSize == size);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGridTypeToggling()
|
public void TestGridTypeToggling()
|
||||||
|
@ -83,10 +83,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BeatmapInfo =
|
StackLeniency = 0,
|
||||||
{
|
|
||||||
StackLeniency = 0,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
ReplayFrames = new List<ReplayFrame>
|
ReplayFrames = new List<ReplayFrame>
|
||||||
{
|
{
|
||||||
|
@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
StackLeniency = 0,
|
|
||||||
Difficulty = new BeatmapDifficulty
|
Difficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
ApproachRate = 8.5f
|
ApproachRate = 8.5f
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
StackLeniency = 0,
|
||||||
ControlPointInfo = controlPointInfo
|
ControlPointInfo = controlPointInfo
|
||||||
};
|
};
|
||||||
|
|
||||||
|
100
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRelax.cs
Normal file
100
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRelax.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Replays;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||||
|
{
|
||||||
|
public partial class TestSceneOsuModRelax : OsuModTestScene
|
||||||
|
{
|
||||||
|
private readonly HitCircle hitObject;
|
||||||
|
private readonly HitWindows hitWindows = new OsuHitWindows();
|
||||||
|
|
||||||
|
public TestSceneOsuModRelax()
|
||||||
|
{
|
||||||
|
hitWindows.SetDifficulty(9);
|
||||||
|
|
||||||
|
hitObject = new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Position = new Vector2(100, 100),
|
||||||
|
HitWindows = hitWindows
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TestPlayer CreateModPlayer(Ruleset ruleset) => new ModRelaxTestPlayer(CurrentTestData, AllowFail);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRelax() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModRelax(),
|
||||||
|
Autoplay = false,
|
||||||
|
CreateBeatmap = () => new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject> { hitObject }
|
||||||
|
},
|
||||||
|
ReplayFrames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new OsuReplayFrame(0, new Vector2()),
|
||||||
|
new OsuReplayFrame(hitObject.StartTime, hitObject.Position),
|
||||||
|
},
|
||||||
|
PassCondition = () => Player.ScoreProcessor.Combo.Value == 1
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRelaxLeniency() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModRelax(),
|
||||||
|
Autoplay = false,
|
||||||
|
CreateBeatmap = () => new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject> { hitObject }
|
||||||
|
},
|
||||||
|
ReplayFrames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new OsuReplayFrame(0, new Vector2(hitObject.X - 22, hitObject.Y - 22)), // must be an edge hit for the cursor to not stay on the object for too long
|
||||||
|
new OsuReplayFrame(hitObject.StartTime - OsuModRelax.RELAX_LENIENCY, new Vector2(hitObject.X - 22, hitObject.Y - 22)),
|
||||||
|
new OsuReplayFrame(hitObject.StartTime, new Vector2(0)),
|
||||||
|
},
|
||||||
|
PassCondition = () => Player.ScoreProcessor.Combo.Value == 1
|
||||||
|
});
|
||||||
|
|
||||||
|
protected partial class ModRelaxTestPlayer : ModTestPlayer
|
||||||
|
{
|
||||||
|
private readonly ModTestData currentTestData;
|
||||||
|
|
||||||
|
public ModRelaxTestPlayer(ModTestData data, bool allowFail)
|
||||||
|
: base(data, allowFail)
|
||||||
|
{
|
||||||
|
currentTestData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PrepareReplay()
|
||||||
|
{
|
||||||
|
// We need to set IsLegacyScore to true otherwise the mod assumes that presses are already embedded into the replay
|
||||||
|
DrawableRuleset?.SetReplayScore(new Score
|
||||||
|
{
|
||||||
|
Replay = new Replay { Frames = currentTestData.ReplayFrames! },
|
||||||
|
ScoreInfo = new ScoreInfo { User = new APIUser { Username = @"Test" }, IsLegacyScore = true, Mods = new Mod[] { new OsuModRelax() } },
|
||||||
|
});
|
||||||
|
|
||||||
|
DrawableRuleset?.SetRecordTarget(Score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -465,7 +465,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private void performTest(List<ReplayFrame> frames, Beatmap<OsuHitObject> beatmap)
|
private void performTest(List<ReplayFrame> frames, Beatmap<OsuHitObject> beatmap)
|
||||||
{
|
{
|
||||||
beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo;
|
beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo;
|
||||||
beatmap.BeatmapInfo.StackLeniency = 0;
|
beatmap.StackLeniency = 0;
|
||||||
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
|
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
SliderMultiplier = 4,
|
SliderMultiplier = 4,
|
||||||
|
@ -56,13 +56,13 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
h.StackHeight = 0;
|
h.StackHeight = 0;
|
||||||
|
|
||||||
if (beatmap.BeatmapInfo.BeatmapVersion >= 6)
|
if (beatmap.BeatmapInfo.BeatmapVersion >= 6)
|
||||||
applyStacking(beatmap.BeatmapInfo, hitObjects, 0, hitObjects.Count - 1);
|
applyStacking(beatmap, hitObjects, 0, hitObjects.Count - 1);
|
||||||
else
|
else
|
||||||
applyStackingOld(beatmap.BeatmapInfo, hitObjects);
|
applyStackingOld(beatmap, hitObjects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyStacking(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects, int startIndex, int endIndex)
|
private static void applyStacking(IBeatmap beatmap, List<OsuHitObject> hitObjects, int startIndex, int endIndex)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, endIndex);
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, endIndex);
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
|
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
|
||||||
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
double endTime = stackBaseObject.GetEndTime();
|
double endTime = stackBaseObject.GetEndTime();
|
||||||
double stackThreshold = objectN.TimePreempt * beatmapInfo.StackLeniency;
|
double stackThreshold = objectN.TimePreempt * beatmap.StackLeniency;
|
||||||
|
|
||||||
if (objectN.StartTime - endTime > stackThreshold)
|
if (objectN.StartTime - endTime > stackThreshold)
|
||||||
// We are no longer within stacking range of the next object.
|
// We are no longer within stacking range of the next object.
|
||||||
@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
OsuHitObject objectI = hitObjects[i];
|
OsuHitObject objectI = hitObjects[i];
|
||||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||||
|
|
||||||
double stackThreshold = objectI.TimePreempt * beatmapInfo.StackLeniency;
|
double stackThreshold = objectI.TimePreempt * beatmap.StackLeniency;
|
||||||
|
|
||||||
/* If this object is a hitcircle, then we enter this "special" case.
|
/* If this object is a hitcircle, then we enter this "special" case.
|
||||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||||
@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyStackingOld(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects)
|
private static void applyStackingOld(IBeatmap beatmap, List<OsuHitObject> hitObjects)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < hitObjects.Count; i++)
|
for (int i = 0; i < hitObjects.Count; i++)
|
||||||
{
|
{
|
||||||
@ -228,7 +228,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
|
|
||||||
for (int j = i + 1; j < hitObjects.Count; j++)
|
for (int j = i + 1; j < hitObjects.Count; j++)
|
||||||
{
|
{
|
||||||
double stackThreshold = hitObjects[i].TimePreempt * beatmapInfo.StackLeniency;
|
double stackThreshold = hitObjects[i].TimePreempt * beatmap.StackLeniency;
|
||||||
|
|
||||||
if (hitObjects[j].StartTime - stackThreshold > startTime)
|
if (hitObjects[j].StartTime - stackThreshold > startTime)
|
||||||
break;
|
break;
|
||||||
|
@ -137,11 +137,27 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete all visually selected <see cref="PathControlPoint"/>s.
|
/// Delete all visually selected <see cref="PathControlPoint"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns>Whether any change actually took place.</returns>
|
||||||
public bool DeleteSelected()
|
public bool DeleteSelected()
|
||||||
{
|
{
|
||||||
List<PathControlPoint> toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.ControlPoint).ToList();
|
List<PathControlPoint> toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.ControlPoint).ToList();
|
||||||
|
|
||||||
|
if (!Delete(toRemove))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
|
||||||
|
foreach (var piece in Pieces)
|
||||||
|
piece.IsSelected.Value = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete the specified <see cref="PathControlPoint"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Whether any change actually took place.</returns>
|
||||||
|
public bool Delete(List<PathControlPoint> toRemove)
|
||||||
|
{
|
||||||
// Ensure that there are any points to be deleted
|
// Ensure that there are any points to be deleted
|
||||||
if (toRemove.Count == 0)
|
if (toRemove.Count == 0)
|
||||||
return false;
|
return false;
|
||||||
@ -149,11 +165,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
changeHandler?.BeginChange();
|
changeHandler?.BeginChange();
|
||||||
RemoveControlPointsRequested?.Invoke(toRemove);
|
RemoveControlPointsRequested?.Invoke(toRemove);
|
||||||
changeHandler?.EndChange();
|
changeHandler?.EndChange();
|
||||||
|
|
||||||
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
|
|
||||||
foreach (var piece in Pieces)
|
|
||||||
piece.IsSelected.Value = false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Utils;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||||
{
|
{
|
||||||
@ -76,6 +77,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
base.OnDragEnd(e);
|
base.OnDragEnd(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e) => e.Button == MouseButton.Left;
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e) => e.Button == MouseButton.Left;
|
||||||
|
|
||||||
private void updateState()
|
private void updateState()
|
||||||
{
|
{
|
||||||
Colour = IsHovered || IsDragged ? colours.Red : colours.Yellow;
|
Colour = IsHovered || IsDragged ? colours.Red : colours.Yellow;
|
||||||
|
@ -140,8 +140,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
if (hoveredControlPoint == null)
|
if (hoveredControlPoint == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
hoveredControlPoint.IsSelected.Value = true;
|
if (hoveredControlPoint.IsSelected.Value)
|
||||||
ControlPointVisualiser?.DeleteSelected();
|
ControlPointVisualiser?.DeleteSelected();
|
||||||
|
else
|
||||||
|
ControlPointVisualiser?.Delete([hoveredControlPoint.ControlPoint]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,6 +554,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
HitObject.Position += first;
|
HitObject.Position += first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// duplicated in `JuiceStreamSelectionBlueprint.convertToStream()`
|
||||||
|
// consider extracting common helper when applying changes here
|
||||||
private void convertToStream()
|
private void convertToStream()
|
||||||
{
|
{
|
||||||
if (editorBeatmap == null || beatDivisor == null)
|
if (editorBeatmap == null || beatDivisor == null)
|
||||||
|
@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Spacing.Value = editorBeatmap.BeatmapInfo.GridSize;
|
Spacing.Value = editorBeatmap.GridSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:#,0.##}";
|
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:#,0.##}";
|
||||||
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:#,0.##}";
|
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:#,0.##}";
|
||||||
SpacingVector.Value = new Vector2(spacing.NewValue);
|
SpacingVector.Value = new Vector2(spacing.NewValue);
|
||||||
editorBeatmap.BeatmapInfo.GridSize = (int)spacing.NewValue;
|
editorBeatmap.GridSize = (int)spacing.NewValue;
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
GridLinesRotation.BindValueChanged(rotation =>
|
GridLinesRotation.BindValueChanged(rotation =>
|
||||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Setup
|
|||||||
{
|
{
|
||||||
Caption = "Stack Leniency",
|
Caption = "Stack Leniency",
|
||||||
HintText = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.",
|
HintText = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.",
|
||||||
Current = new BindableFloat(Beatmap.BeatmapInfo.StackLeniency)
|
Current = new BindableFloat(Beatmap.StackLeniency)
|
||||||
{
|
{
|
||||||
Default = 0.7f,
|
Default = 0.7f,
|
||||||
MinValue = 0,
|
MinValue = 0,
|
||||||
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Setup
|
|||||||
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
||||||
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
||||||
Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value;
|
Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value;
|
||||||
Beatmap.BeatmapInfo.StackLeniency = stackLeniency.Current.Value;
|
Beatmap.StackLeniency = stackLeniency.Current.Value;
|
||||||
|
|
||||||
Beatmap.UpdateAllHitObjects();
|
Beatmap.UpdateAllHitObjects();
|
||||||
Beatmap.SaveState();
|
Beatmap.SaveState();
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// How early before a hitobject's start time to trigger a hit.
|
/// How early before a hitobject's start time to trigger a hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float relax_leniency = 3;
|
public const float RELAX_LENIENCY = 12;
|
||||||
|
|
||||||
private bool isDownState;
|
private bool isDownState;
|
||||||
private bool wasLeft;
|
private bool wasLeft;
|
||||||
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
foreach (var h in playfield.HitObjectContainer.AliveObjects.OfType<DrawableOsuHitObject>())
|
foreach (var h in playfield.HitObjectContainer.AliveObjects.OfType<DrawableOsuHitObject>())
|
||||||
{
|
{
|
||||||
// we are not yet close enough to the object.
|
// we are not yet close enough to the object.
|
||||||
if (time < h.HitObject.StartTime - relax_leniency)
|
if (time < h.HitObject.StartTime - RELAX_LENIENCY)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// already hit or beyond the hittable end time.
|
// already hit or beyond the hittable end time.
|
||||||
|
@ -57,11 +57,25 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
const string base_lookup = @"hitcircle";
|
||||||
|
|
||||||
var drawableOsuObject = (DrawableOsuHitObject?)drawableObject;
|
var drawableOsuObject = (DrawableOsuHitObject?)drawableObject;
|
||||||
|
|
||||||
|
// As a precondition, prefer that any *prefix* lookups are run against the skin which is providing "hitcircle".
|
||||||
|
// This is to correctly handle a case such as:
|
||||||
|
//
|
||||||
|
// - Beatmap provides `hitcircle`
|
||||||
|
// - User skin provides `sliderstartcircle`
|
||||||
|
//
|
||||||
|
// In such a case, the `hitcircle` should be used for slider start circles rather than the user's skin override.
|
||||||
|
//
|
||||||
|
// Of note, this consideration should only be used to decide whether to continue looking up the prefixed name or not.
|
||||||
|
// The final lookups must still run on the full skin hierarchy as per usual in order to correctly handle fallback cases.
|
||||||
|
var provider = skin.FindProvider(s => s.GetTexture(base_lookup) != null) ?? skin;
|
||||||
|
|
||||||
// if a base texture for the specified prefix exists, continue using it for subsequent lookups.
|
// if a base texture for the specified prefix exists, continue using it for subsequent lookups.
|
||||||
// otherwise fall back to the default prefix "hitcircle".
|
// otherwise fall back to the default prefix "hitcircle".
|
||||||
string circleName = (priorityLookupPrefix != null && skin.GetTexture(priorityLookupPrefix) != null) ? priorityLookupPrefix : @"hitcircle";
|
string circleName = (priorityLookupPrefix != null && provider.GetTexture(priorityLookupPrefix) != null) ? priorityLookupPrefix : base_lookup;
|
||||||
|
|
||||||
Vector2 maxSize = OsuHitObject.OBJECT_DIMENSIONS * 2;
|
Vector2 maxSize = OsuHitObject.OBJECT_DIMENSIONS * 2;
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
protected override ResumeOverlay CreateResumeOverlay()
|
protected override ResumeOverlay CreateResumeOverlay()
|
||||||
{
|
{
|
||||||
if (Mods.Any(m => m is OsuModAutopilot))
|
if (Mods.Any(m => m is OsuModAutopilot or OsuModTouchDevice))
|
||||||
return new DelayedResumeOverlay { Scale = new Vector2(0.65f) };
|
return new DelayedResumeOverlay { Scale = new Vector2(0.65f) };
|
||||||
|
|
||||||
return new OsuResumeOverlay();
|
return new OsuResumeOverlay();
|
||||||
|
15
osu.Game.Rulesets.Taiko.Tests.iOS/AppDelegate.cs
Normal file
15
osu.Game.Rulesets.Taiko.Tests.iOS/AppDelegate.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using Foundation;
|
||||||
|
using osu.Framework.iOS;
|
||||||
|
using osu.Game.Tests;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests.iOS
|
||||||
|
{
|
||||||
|
[Register("AppDelegate")]
|
||||||
|
public class AppDelegate : GameApplicationDelegate
|
||||||
|
{
|
||||||
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,15 @@
|
|||||||
// 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 osu.Framework.iOS;
|
using UIKit;
|
||||||
using osu.Game.Tests;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests.iOS
|
namespace osu.Game.Rulesets.Taiko.Tests.iOS
|
||||||
{
|
{
|
||||||
public static class Application
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
GameApplication.Main(new OsuTestBrowser());
|
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -144,6 +144,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
foreach (var nested in hitObject.NestedHitObjects)
|
foreach (var nested in hitObject.NestedHitObjects)
|
||||||
simulateHit(nested, ref attributes);
|
simulateHit(nested, ref attributes);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case StrongNestedHitObject:
|
||||||
|
// we never need to deal with these directly.
|
||||||
|
// the only thing strong hits do in terms of scoring is double their object's score increase,
|
||||||
|
// which is already handled at the parent object level via the `strongable.IsStrong` check lower down in this method.
|
||||||
|
// not handling these here can lead to them falsely being counted as combo-increasing when handling strong drum rolls!
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hitObject is DrumRollTick tick)
|
if (hitObject is DrumRollTick tick)
|
||||||
|
14
osu.Game.Tests.iOS/AppDelegate.cs
Normal file
14
osu.Game.Tests.iOS/AppDelegate.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// 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 Foundation;
|
||||||
|
using osu.Framework.iOS;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.iOS
|
||||||
|
{
|
||||||
|
[Register("AppDelegate")]
|
||||||
|
public class AppDelegate : GameApplicationDelegate
|
||||||
|
{
|
||||||
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,15 @@
|
|||||||
// 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 osu.Framework.iOS;
|
using UIKit;
|
||||||
|
|
||||||
namespace osu.Game.Tests.iOS
|
namespace osu.Game.Tests.iOS
|
||||||
{
|
{
|
||||||
public static class Application
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
GameApplication.Main(new OsuTestBrowser());
|
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -80,16 +80,16 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
var metadata = beatmap.Metadata;
|
var metadata = beatmap.Metadata;
|
||||||
|
|
||||||
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
|
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
|
||||||
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
|
Assert.AreEqual(0, beatmap.AudioLeadIn);
|
||||||
Assert.AreEqual(164471, metadata.PreviewTime);
|
Assert.AreEqual(164471, metadata.PreviewTime);
|
||||||
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
|
Assert.AreEqual(0.7f, beatmap.StackLeniency);
|
||||||
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
|
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
|
||||||
Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
|
Assert.IsFalse(beatmap.LetterboxInBreaks);
|
||||||
Assert.IsFalse(beatmapInfo.SpecialStyle);
|
Assert.IsFalse(beatmap.SpecialStyle);
|
||||||
Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
|
Assert.IsFalse(beatmap.WidescreenStoryboard);
|
||||||
Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate);
|
Assert.IsFalse(beatmap.SamplesMatchPlaybackRate);
|
||||||
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
|
Assert.AreEqual(CountdownType.None, beatmap.Countdown);
|
||||||
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
|
Assert.AreEqual(0, beatmap.CountdownOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
{
|
{
|
||||||
var beatmapInfo = decoder.Decode(stream).BeatmapInfo;
|
var beatmap = decoder.Decode(stream);
|
||||||
|
|
||||||
int[] expectedBookmarks =
|
int[] expectedBookmarks =
|
||||||
{
|
{
|
||||||
@ -109,13 +109,13 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
||||||
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
||||||
};
|
};
|
||||||
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
|
Assert.AreEqual(expectedBookmarks.Length, beatmap.Bookmarks.Length);
|
||||||
for (int i = 0; i < expectedBookmarks.Length; i++)
|
for (int i = 0; i < expectedBookmarks.Length; i++)
|
||||||
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
|
Assert.AreEqual(expectedBookmarks[i], beatmap.Bookmarks[i]);
|
||||||
Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
|
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
|
||||||
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
|
Assert.AreEqual(4, beatmap.BeatmapInfo.BeatDivisor);
|
||||||
Assert.AreEqual(4, beatmapInfo.GridSize);
|
Assert.AreEqual(4, beatmap.GridSize);
|
||||||
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
|
Assert.AreEqual(2, beatmap.TimelineZoom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -993,15 +993,15 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(decoded.BeatmapInfo.AudioLeadIn, Is.EqualTo(0));
|
Assert.That(decoded.AudioLeadIn, Is.EqualTo(0));
|
||||||
Assert.That(decoded.BeatmapInfo.StackLeniency, Is.EqualTo(0.7f));
|
Assert.That(decoded.StackLeniency, Is.EqualTo(0.7f));
|
||||||
Assert.That(decoded.BeatmapInfo.SpecialStyle, Is.False);
|
Assert.That(decoded.SpecialStyle, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.LetterboxInBreaks, Is.False);
|
Assert.That(decoded.LetterboxInBreaks, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.WidescreenStoryboard, Is.False);
|
Assert.That(decoded.WidescreenStoryboard, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.EpilepsyWarning, Is.False);
|
Assert.That(decoded.EpilepsyWarning, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.SamplesMatchPlaybackRate, Is.False);
|
Assert.That(decoded.SamplesMatchPlaybackRate, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.Countdown, Is.EqualTo(CountdownType.None));
|
Assert.That(decoded.Countdown, Is.EqualTo(CountdownType.None));
|
||||||
Assert.That(decoded.BeatmapInfo.CountdownOffset, Is.EqualTo(0));
|
Assert.That(decoded.CountdownOffset, Is.EqualTo(0));
|
||||||
Assert.That(decoded.BeatmapInfo.Metadata.PreviewTime, Is.EqualTo(-1));
|
Assert.That(decoded.BeatmapInfo.Metadata.PreviewTime, Is.EqualTo(-1));
|
||||||
Assert.That(decoded.BeatmapInfo.Ruleset.OnlineID, Is.EqualTo(0));
|
Assert.That(decoded.BeatmapInfo.Ruleset.OnlineID, Is.EqualTo(0));
|
||||||
});
|
});
|
||||||
|
@ -51,14 +51,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var beatmap = decodeAsJson(normal);
|
var beatmap = decodeAsJson(normal);
|
||||||
var beatmapInfo = beatmap.BeatmapInfo;
|
var beatmapInfo = beatmap.BeatmapInfo;
|
||||||
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
|
Assert.AreEqual(0, beatmap.AudioLeadIn);
|
||||||
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
|
Assert.AreEqual(0.7f, beatmap.StackLeniency);
|
||||||
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
|
Assert.AreEqual(false, beatmap.SpecialStyle);
|
||||||
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
|
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
|
||||||
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
|
Assert.AreEqual(false, beatmap.LetterboxInBreaks);
|
||||||
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
|
Assert.AreEqual(false, beatmap.WidescreenStoryboard);
|
||||||
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
|
Assert.AreEqual(CountdownType.None, beatmap.Countdown);
|
||||||
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
|
Assert.AreEqual(0, beatmap.CountdownOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -73,13 +73,13 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
||||||
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
||||||
};
|
};
|
||||||
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
|
Assert.AreEqual(expectedBookmarks.Length, beatmap.Bookmarks.Length);
|
||||||
for (int i = 0; i < expectedBookmarks.Length; i++)
|
for (int i = 0; i < expectedBookmarks.Length; i++)
|
||||||
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
|
Assert.AreEqual(expectedBookmarks[i], beatmap.Bookmarks[i]);
|
||||||
Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
|
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
|
||||||
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
|
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
|
||||||
Assert.AreEqual(4, beatmapInfo.GridSize);
|
Assert.AreEqual(4, beatmap.GridSize);
|
||||||
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
|
Assert.AreEqual(2, beatmap.TimelineZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
7
osu.Game.Tests/CodeAnalysis.tests.globalconfig
Normal file
7
osu.Game.Tests/CodeAnalysis.tests.globalconfig
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Higher global_level has higher priority, the default global_level
|
||||||
|
# is 100 for root .globalconfig and 0 for others
|
||||||
|
# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/configuration-files#precedence
|
||||||
|
is_global = true
|
||||||
|
global_level = 101
|
||||||
|
|
||||||
|
dotnet_diagnostic.CA2007.severity = none
|
@ -41,7 +41,7 @@ namespace osu.Game.Tests.Database
|
|||||||
Assert.That(lastChanges?.ModifiedIndices, Is.Empty);
|
Assert.That(lastChanges?.ModifiedIndices, Is.Empty);
|
||||||
Assert.That(lastChanges?.NewModifiedIndices, Is.Empty);
|
Assert.That(lastChanges?.NewModifiedIndices, Is.Empty);
|
||||||
|
|
||||||
realm.Write(r => r.All<BeatmapSetInfo>().First().Beatmaps.First().CountdownOffset = 5);
|
realm.Write(r => r.All<BeatmapSetInfo>().First().Beatmaps.First().EditorTimestamp = 5);
|
||||||
realm.Run(r => r.Refresh());
|
realm.Run(r => r.Refresh());
|
||||||
|
|
||||||
Assert.That(collectionChanges, Is.EqualTo(1));
|
Assert.That(collectionChanges, Is.EqualTo(1));
|
||||||
|
BIN
osu.Game.Tests/Resources/Archives/modified-default-20241207.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-default-20241207.osk
Normal file
Binary file not shown.
@ -68,7 +68,9 @@ namespace osu.Game.Tests.Skins
|
|||||||
// Covers legacy rank display
|
// Covers legacy rank display
|
||||||
"Archives/modified-classic-20230809.osk",
|
"Archives/modified-classic-20230809.osk",
|
||||||
// Covers legacy key counter
|
// Covers legacy key counter
|
||||||
"Archives/modified-classic-20240724.osk"
|
"Archives/modified-classic-20240724.osk",
|
||||||
|
// Covers skinnable mod display
|
||||||
|
"Archives/modified-default-20241207.osk",
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -131,21 +131,6 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
assertNoBackgrounds();
|
assertNoBackgrounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestDelayedConnectivity()
|
|
||||||
{
|
|
||||||
registerBackgroundsResponse(DateTimeOffset.Now.AddDays(30));
|
|
||||||
setSeasonalBackgroundMode(SeasonalBackgroundMode.Always);
|
|
||||||
AddStep("go offline", () => dummyAPI.SetState(APIState.Offline));
|
|
||||||
|
|
||||||
createLoader();
|
|
||||||
assertNoBackgrounds();
|
|
||||||
|
|
||||||
AddStep("go online", () => dummyAPI.SetState(APIState.Online));
|
|
||||||
|
|
||||||
assertAnyBackground();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerBackgroundsResponse(DateTimeOffset endDate)
|
private void registerBackgroundsResponse(DateTimeOffset endDate)
|
||||||
=> AddStep("setup request handler", () =>
|
=> AddStep("setup request handler", () =>
|
||||||
{
|
{
|
||||||
@ -185,7 +170,8 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
{
|
{
|
||||||
previousBackground = (SeasonalBackground)backgroundContainer.SingleOrDefault();
|
previousBackground = (SeasonalBackground)backgroundContainer.SingleOrDefault();
|
||||||
background = backgroundLoader.LoadNextBackground();
|
background = backgroundLoader.LoadNextBackground();
|
||||||
LoadComponentAsync(background, bg => backgroundContainer.Child = bg);
|
if (background != null)
|
||||||
|
LoadComponentAsync(background, bg => backgroundContainer.Child = bg);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("background loaded", () => background.IsLoaded);
|
AddUntilStep("background loaded", () => background.IsLoaded);
|
||||||
|
@ -49,17 +49,17 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
DetachedBeatmapStore detachedBeatmapStore;
|
BeatmapStore beatmapStore;
|
||||||
|
|
||||||
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||||
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
|
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
manager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
|
manager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
|
||||||
|
|
||||||
Add(detachedBeatmapStore);
|
Add(beatmapStore);
|
||||||
|
|
||||||
Beatmap.SetDefault();
|
Beatmap.SetDefault();
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false);
|
AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false);
|
||||||
|
|
||||||
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.None);
|
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.None);
|
||||||
AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent);
|
AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,12 +65,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
|
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
|
||||||
|
|
||||||
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
|
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.Normal);
|
||||||
AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent);
|
AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent);
|
||||||
|
|
||||||
AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed);
|
AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed);
|
||||||
|
|
||||||
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.DoubleSpeed);
|
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.DoubleSpeed);
|
||||||
AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent);
|
AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
|
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
|
||||||
|
|
||||||
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
|
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.Normal);
|
||||||
|
|
||||||
checkOffsetAfter("1", 1);
|
checkOffsetAfter("1", 1);
|
||||||
checkOffsetAfter(string.Empty, 0);
|
checkOffsetAfter(string.Empty, 0);
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||||
|
|
||||||
AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture));
|
AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture));
|
||||||
AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.BeatmapInfo.CountdownOffset == expectedFinalValue);
|
AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.CountdownOffset == expectedFinalValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class TestDesignSection : DesignSection
|
private partial class TestDesignSection : DesignSection
|
||||||
|
@ -4,11 +4,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -19,6 +21,7 @@ using osu.Game.Overlays.Dialog;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Catch;
|
using osu.Game.Rulesets.Catch;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
@ -27,6 +30,7 @@ using osu.Game.Rulesets.Taiko.Objects;
|
|||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
using osu.Game.Screens.Edit.Setup;
|
using osu.Game.Screens.Edit.Setup;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -98,44 +102,15 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("enter compose mode", () => InputManager.Key(Key.F1));
|
AddStep("enter compose mode", () => InputManager.Key(Key.F1));
|
||||||
AddUntilStep("wait for timeline load", () => Editor.ChildrenOfType<Timeline>().FirstOrDefault()?.IsLoaded == true);
|
AddUntilStep("wait for timeline load", () => Editor.ChildrenOfType<Timeline>().FirstOrDefault()?.IsLoaded == true);
|
||||||
|
|
||||||
AddStep("enter setup mode", () => InputManager.Key(Key.F4));
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual);
|
AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual);
|
||||||
AddAssert("switch track to real track", () =>
|
AddAssert("switch track to real track", () => setAudio(applyToAllDifficulties: true, expected: "audio.mp3"));
|
||||||
{
|
|
||||||
var setup = Editor.ChildrenOfType<SetupScreen>().First();
|
|
||||||
|
|
||||||
string temp = TestResources.GetTestBeatmapForImport();
|
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
|
||||||
Directory.CreateDirectory(extractedFolder);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var zip = ZipArchive.Open(temp))
|
|
||||||
zip.WriteToDirectory(extractedFolder);
|
|
||||||
|
|
||||||
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(new FileInfo(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")));
|
|
||||||
|
|
||||||
// ensure audio file is copied to beatmap as "audio.mp3" rather than original filename.
|
|
||||||
Assert.That(Beatmap.Value.Metadata.AudioFile == "audio.mp3");
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
File.Delete(temp);
|
|
||||||
Directory.Delete(extractedFolder, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
AddAssert("track is not virtual", () => Beatmap.Value.Track is not TrackVirtual);
|
AddAssert("track is not virtual", () => Beatmap.Value.Track is not TrackVirtual);
|
||||||
AddUntilStep("track length changed", () => Beatmap.Value.Track.Length > 60000);
|
AddUntilStep("track length changed", () => Beatmap.Value.Track.Length > 60000);
|
||||||
|
|
||||||
AddStep("test play", () => Editor.TestGameplay());
|
AddStep("test play", () => Editor.TestGameplay());
|
||||||
|
|
||||||
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog != null);
|
|
||||||
AddStep("confirm save", () => InputManager.Key(Key.Number1));
|
|
||||||
|
|
||||||
AddUntilStep("wait for return to editor", () => Editor.IsCurrentScreen());
|
AddUntilStep("wait for return to editor", () => Editor.IsCurrentScreen());
|
||||||
|
|
||||||
AddAssert("track is still not virtual", () => Beatmap.Value.Track is not TrackVirtual);
|
AddAssert("track is still not virtual", () => Beatmap.Value.Track is not TrackVirtual);
|
||||||
@ -154,6 +129,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = firstDifficultyName);
|
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = firstDifficultyName);
|
||||||
AddStep("add timing point", () => EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }));
|
AddStep("add timing point", () => EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }));
|
||||||
|
AddStep("add effect point", () => EditorBeatmap.ControlPointInfo.Add(500, new EffectControlPoint { KiaiMode = true }));
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[]
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[]
|
||||||
{
|
{
|
||||||
new HitCircle
|
new HitCircle
|
||||||
@ -200,6 +176,11 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
|
var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
|
||||||
return timingPoint.Time == 0 && timingPoint.BeatLength == 1000;
|
return timingPoint.Time == 0 && timingPoint.BeatLength == 1000;
|
||||||
});
|
});
|
||||||
|
AddAssert("created difficulty has effect points", () =>
|
||||||
|
{
|
||||||
|
var effectPoint = EditorBeatmap.ControlPointInfo.EffectPoints.Single();
|
||||||
|
return effectPoint.Time == 500 && effectPoint.KiaiMode && effectPoint.ScrollSpeedBindable.IsDefault;
|
||||||
|
});
|
||||||
AddAssert("created difficulty has no objects", () => EditorBeatmap.HitObjects.Count == 0);
|
AddAssert("created difficulty has no objects", () => EditorBeatmap.HitObjects.Count == 0);
|
||||||
|
|
||||||
AddAssert("status is modified", () => EditorBeatmap.BeatmapInfo.Status == BeatmapOnlineStatus.LocallyModified);
|
AddAssert("status is modified", () => EditorBeatmap.BeatmapInfo.Status == BeatmapOnlineStatus.LocallyModified);
|
||||||
@ -219,6 +200,111 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCreateNewDifficultyWithScrollSpeed_SameRuleset()
|
||||||
|
{
|
||||||
|
string previousDifficultyName = null!;
|
||||||
|
|
||||||
|
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = previousDifficultyName = Guid.NewGuid().ToString());
|
||||||
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
|
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new ManiaRuleset().RulesetInfo));
|
||||||
|
|
||||||
|
AddUntilStep("wait for created", () =>
|
||||||
|
{
|
||||||
|
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||||
|
return difficultyName != null && difficultyName != previousDifficultyName;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = previousDifficultyName = Guid.NewGuid().ToString());
|
||||||
|
AddStep("add timing point", () => EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }));
|
||||||
|
AddStep("add effect points", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(250, new EffectControlPoint { KiaiMode = false, ScrollSpeed = 0.05 });
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(500, new EffectControlPoint { KiaiMode = true, ScrollSpeed = 0.1 });
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(750, new EffectControlPoint { KiaiMode = true, ScrollSpeed = 0.15 });
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(1000, new EffectControlPoint { KiaiMode = false, ScrollSpeed = 0.2 });
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(1500, new EffectControlPoint { KiaiMode = false, ScrollSpeed = 0.3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
|
|
||||||
|
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new ManiaRuleset().RulesetInfo));
|
||||||
|
|
||||||
|
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
|
||||||
|
AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog!.PerformOkAction());
|
||||||
|
|
||||||
|
AddUntilStep("wait for created", () =>
|
||||||
|
{
|
||||||
|
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||||
|
return difficultyName != null && difficultyName != previousDifficultyName;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("created difficulty has timing point", () =>
|
||||||
|
{
|
||||||
|
var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
|
||||||
|
return timingPoint.Time == 0 && timingPoint.BeatLength == 1000;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("created difficulty has effect points", () =>
|
||||||
|
{
|
||||||
|
return EditorBeatmap.ControlPointInfo.EffectPoints.SequenceEqual(new[]
|
||||||
|
{
|
||||||
|
new EffectControlPoint { Time = 250, KiaiMode = false, ScrollSpeed = 0.05 },
|
||||||
|
new EffectControlPoint { Time = 500, KiaiMode = true, ScrollSpeed = 0.1 },
|
||||||
|
new EffectControlPoint { Time = 750, KiaiMode = true, ScrollSpeed = 0.15 },
|
||||||
|
new EffectControlPoint { Time = 1000, KiaiMode = false, ScrollSpeed = 0.2 },
|
||||||
|
new EffectControlPoint { Time = 1500, KiaiMode = false, ScrollSpeed = 0.3 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCreateNewDifficultyWithScrollSpeed_DifferentRuleset()
|
||||||
|
{
|
||||||
|
string firstDifficultyName = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
|
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new ManiaRuleset().RulesetInfo));
|
||||||
|
|
||||||
|
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = firstDifficultyName);
|
||||||
|
AddStep("add timing point", () => EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }));
|
||||||
|
AddStep("add effect points", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(250, new EffectControlPoint { KiaiMode = false, ScrollSpeed = 0.05 });
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(500, new EffectControlPoint { KiaiMode = true, ScrollSpeed = 0.1 });
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(750, new EffectControlPoint { KiaiMode = true, ScrollSpeed = 0.15 });
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(1000, new EffectControlPoint { KiaiMode = false, ScrollSpeed = 0.2 });
|
||||||
|
EditorBeatmap.ControlPointInfo.Add(1500, new EffectControlPoint { KiaiMode = false, ScrollSpeed = 0.3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
|
|
||||||
|
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new TaikoRuleset().RulesetInfo));
|
||||||
|
|
||||||
|
AddUntilStep("wait for created", () =>
|
||||||
|
{
|
||||||
|
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||||
|
return difficultyName != null && difficultyName != firstDifficultyName;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("created difficulty has timing point", () =>
|
||||||
|
{
|
||||||
|
var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
|
||||||
|
return timingPoint.Time == 0 && timingPoint.BeatLength == 1000;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("created difficulty has effect points", () =>
|
||||||
|
{
|
||||||
|
// since this difficulty is on another ruleset, scroll speed specifications are completely reset,
|
||||||
|
// therefore discarding some effect points in the process due to being redundant.
|
||||||
|
return EditorBeatmap.ControlPointInfo.EffectPoints.SequenceEqual(new[]
|
||||||
|
{
|
||||||
|
new EffectControlPoint { Time = 500, KiaiMode = true },
|
||||||
|
new EffectControlPoint { Time = 1000, KiaiMode = false },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCopyDifficulty()
|
public void TestCopyDifficulty()
|
||||||
{
|
{
|
||||||
@ -530,5 +616,228 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
return set != null && set.PerformRead(s => s.Beatmaps.Count == 3 && s.Files.Count == 3);
|
return set != null && set.PerformRead(s => s.Beatmaps.Count == 3 && s.Files.Count == 3);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSingleBackgroundFile()
|
||||||
|
{
|
||||||
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
|
AddAssert("set background", () => setBackground(applyToAllDifficulties: true, expected: "bg.jpg"));
|
||||||
|
|
||||||
|
createNewDifficulty();
|
||||||
|
createNewDifficulty();
|
||||||
|
|
||||||
|
switchToDifficulty(1);
|
||||||
|
|
||||||
|
AddAssert("set background on second diff only", () => setBackground(applyToAllDifficulties: false, expected: "bg (1).jpg"));
|
||||||
|
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (1).jpg"));
|
||||||
|
|
||||||
|
switchToDifficulty(0);
|
||||||
|
|
||||||
|
AddAssert("set background on first diff only", () => setBackground(applyToAllDifficulties: false, expected: "bg (2).jpg"));
|
||||||
|
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (2).jpg"));
|
||||||
|
|
||||||
|
AddAssert("set background on all diff", () => setBackground(applyToAllDifficulties: true, expected: "bg.jpg"));
|
||||||
|
AddAssert("all diff uses one background", () => Beatmap.Value.BeatmapSetInfo.Beatmaps.All(b => b.Metadata.BackgroundFile == "bg.jpg"));
|
||||||
|
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg.jpg"));
|
||||||
|
AddAssert("other files removed", () => !Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (1).jpg" || f.Filename == "bg (2).jpg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBackgroundFileChangesPreserveOnEncode()
|
||||||
|
{
|
||||||
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
|
AddAssert("set background", () => setBackground(applyToAllDifficulties: true, expected: "bg.jpg"));
|
||||||
|
|
||||||
|
createNewDifficulty();
|
||||||
|
createNewDifficulty();
|
||||||
|
|
||||||
|
switchToDifficulty(0);
|
||||||
|
|
||||||
|
AddAssert("set different background on all diff", () => setBackgroundDifferentExtension(applyToAllDifficulties: true, expected: "bg.jpeg"));
|
||||||
|
AddAssert("all diff uses one background", () => Beatmap.Value.BeatmapSetInfo.Beatmaps.All(b => b.Metadata.BackgroundFile == "bg.jpeg"));
|
||||||
|
AddAssert("all diff encode same background", () =>
|
||||||
|
{
|
||||||
|
return Beatmap.Value.BeatmapSetInfo.Beatmaps.All(b =>
|
||||||
|
{
|
||||||
|
var files = new RealmFileStore(Realm, Dependencies.Get<GameHost>().Storage);
|
||||||
|
using var store = new RealmBackedResourceStore<BeatmapSetInfo>(b.BeatmapSet!.ToLive(Realm), files.Store, Realm);
|
||||||
|
string[] osu = Encoding.UTF8.GetString(store.Get(b.File!.Filename)).Split(Environment.NewLine);
|
||||||
|
Assert.That(osu, Does.Contain("0,0,\"bg.jpeg\",0,0"));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSingleAudioFile()
|
||||||
|
{
|
||||||
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
|
AddAssert("set audio", () => setAudio(applyToAllDifficulties: true, expected: "audio.mp3"));
|
||||||
|
|
||||||
|
createNewDifficulty();
|
||||||
|
createNewDifficulty();
|
||||||
|
|
||||||
|
switchToDifficulty(1);
|
||||||
|
|
||||||
|
AddAssert("set audio on second diff only", () => setAudio(applyToAllDifficulties: false, expected: "audio (1).mp3"));
|
||||||
|
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (1).mp3"));
|
||||||
|
|
||||||
|
switchToDifficulty(0);
|
||||||
|
|
||||||
|
AddAssert("set audio on first diff only", () => setAudio(applyToAllDifficulties: false, expected: "audio (2).mp3"));
|
||||||
|
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (2).mp3"));
|
||||||
|
|
||||||
|
AddAssert("set audio on all diff", () => setAudio(applyToAllDifficulties: true, expected: "audio.mp3"));
|
||||||
|
AddAssert("all diff uses one audio", () => Beatmap.Value.BeatmapSetInfo.Beatmaps.All(b => b.Metadata.AudioFile == "audio.mp3"));
|
||||||
|
AddAssert("file added", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio.mp3"));
|
||||||
|
AddAssert("other files removed", () => !Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (1).mp3" || f.Filename == "audio (2).mp3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMultipleBackgroundFiles()
|
||||||
|
{
|
||||||
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
|
AddAssert("set background", () => setBackground(applyToAllDifficulties: false, expected: "bg.jpg"));
|
||||||
|
|
||||||
|
createNewDifficulty();
|
||||||
|
|
||||||
|
AddAssert("new difficulty uses same background", () => Beatmap.Value.Metadata.BackgroundFile == "bg.jpg");
|
||||||
|
AddAssert("set background", () => setBackground(applyToAllDifficulties: false, expected: "bg (1).jpg"));
|
||||||
|
AddAssert("new difficulty uses new background", () => Beatmap.Value.Metadata.BackgroundFile == "bg (1).jpg");
|
||||||
|
|
||||||
|
switchToDifficulty(0);
|
||||||
|
|
||||||
|
AddAssert("old difficulty uses old background", () => Beatmap.Value.Metadata.BackgroundFile == "bg.jpg");
|
||||||
|
AddAssert("old background not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg.jpg"));
|
||||||
|
AddStep("set background", () => setBackground(applyToAllDifficulties: false, expected: "bg.jpg"));
|
||||||
|
AddAssert("other background not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "bg (1).jpg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMultipleAudioFiles()
|
||||||
|
{
|
||||||
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
|
AddAssert("set audio", () => setAudio(applyToAllDifficulties: false, expected: "audio.mp3"));
|
||||||
|
|
||||||
|
createNewDifficulty();
|
||||||
|
|
||||||
|
AddAssert("new difficulty uses same audio", () => Beatmap.Value.Metadata.AudioFile == "audio.mp3");
|
||||||
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
|
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
|
||||||
|
AddAssert("set audio", () => setAudio(applyToAllDifficulties: false, expected: "audio (1).mp3"));
|
||||||
|
AddAssert("new difficulty uses new audio", () => Beatmap.Value.Metadata.AudioFile == "audio (1).mp3");
|
||||||
|
|
||||||
|
switchToDifficulty(0);
|
||||||
|
|
||||||
|
AddAssert("old difficulty uses old audio", () => Beatmap.Value.Metadata.AudioFile == "audio.mp3");
|
||||||
|
AddAssert("old audio not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio.mp3"));
|
||||||
|
AddStep("set audio", () => setAudio(applyToAllDifficulties: false, expected: "audio.mp3"));
|
||||||
|
AddAssert("other audio not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (1).mp3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNewDifficulty()
|
||||||
|
{
|
||||||
|
string? currentDifficulty = null;
|
||||||
|
|
||||||
|
AddStep("save", () => Editor.Save());
|
||||||
|
AddStep("create new difficulty", () =>
|
||||||
|
{
|
||||||
|
currentDifficulty = EditorBeatmap.BeatmapInfo.DifficultyName;
|
||||||
|
Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
|
||||||
|
AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog!.PerformOkAction());
|
||||||
|
AddUntilStep("wait for created", () =>
|
||||||
|
{
|
||||||
|
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||||
|
return difficultyName != null && difficultyName != currentDifficulty;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for editor load", () => Editor.IsLoaded);
|
||||||
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
|
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchToDifficulty(int index)
|
||||||
|
{
|
||||||
|
AddStep("save", () => Editor.Save());
|
||||||
|
AddStep($"switch to difficulty #{index + 1}", () =>
|
||||||
|
Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.ElementAt(index)));
|
||||||
|
|
||||||
|
AddUntilStep("wait for editor load", () => Editor.IsLoaded);
|
||||||
|
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||||
|
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool setBackground(bool applyToAllDifficulties, string expected)
|
||||||
|
{
|
||||||
|
var setup = Editor.ChildrenOfType<SetupScreen>().First();
|
||||||
|
|
||||||
|
return setFile(TestResources.GetQuickTestBeatmapForImport(), extractedFolder =>
|
||||||
|
{
|
||||||
|
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeBackgroundImage(
|
||||||
|
new FileInfo(Path.Combine(extractedFolder, @"machinetop_background.jpg")),
|
||||||
|
applyToAllDifficulties);
|
||||||
|
|
||||||
|
Assert.That(Beatmap.Value.Metadata.BackgroundFile, Is.EqualTo(expected));
|
||||||
|
return success;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool setBackgroundDifferentExtension(bool applyToAllDifficulties, string expected)
|
||||||
|
{
|
||||||
|
var setup = Editor.ChildrenOfType<SetupScreen>().First();
|
||||||
|
|
||||||
|
return setFile(TestResources.GetQuickTestBeatmapForImport(), extractedFolder =>
|
||||||
|
{
|
||||||
|
File.Move(
|
||||||
|
Path.Combine(extractedFolder, @"machinetop_background.jpg"),
|
||||||
|
Path.Combine(extractedFolder, @"machinetop_background.jpeg"));
|
||||||
|
|
||||||
|
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeBackgroundImage(
|
||||||
|
new FileInfo(Path.Combine(extractedFolder, @"machinetop_background.jpeg")),
|
||||||
|
applyToAllDifficulties);
|
||||||
|
|
||||||
|
Assert.That(Beatmap.Value.Metadata.BackgroundFile, Is.EqualTo(expected));
|
||||||
|
return success;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool setAudio(bool applyToAllDifficulties, string expected)
|
||||||
|
{
|
||||||
|
var setup = Editor.ChildrenOfType<SetupScreen>().First();
|
||||||
|
|
||||||
|
return setFile(TestResources.GetTestBeatmapForImport(), extractedFolder =>
|
||||||
|
{
|
||||||
|
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(
|
||||||
|
new FileInfo(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")),
|
||||||
|
applyToAllDifficulties);
|
||||||
|
|
||||||
|
Assert.That(Beatmap.Value.Metadata.AudioFile, Is.EqualTo(expected));
|
||||||
|
return success;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool setFile(string archivePath, Func<string, bool> func)
|
||||||
|
{
|
||||||
|
string temp = archivePath;
|
||||||
|
|
||||||
|
string extractedFolder = $"{temp}_extracted";
|
||||||
|
Directory.CreateDirectory(extractedFolder);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var zip = ZipArchive.Open(temp))
|
||||||
|
zip.WriteToDirectory(extractedFolder);
|
||||||
|
|
||||||
|
return func(extractedFolder);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
File.Delete(temp);
|
||||||
|
Directory.Delete(extractedFolder, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("Set beat divisor", () => Editor.Dependencies.Get<BindableBeatDivisor>().Value = 16);
|
AddStep("Set beat divisor", () => Editor.Dependencies.Get<BindableBeatDivisor>().Value = 16);
|
||||||
AddStep("Set timeline zoom", () =>
|
AddStep("Set timeline zoom", () =>
|
||||||
{
|
{
|
||||||
originalTimelineZoom = EditorBeatmap.BeatmapInfo.TimelineZoom;
|
originalTimelineZoom = EditorBeatmap.TimelineZoom;
|
||||||
|
|
||||||
var timeline = Editor.ChildrenOfType<Timeline>().Single();
|
var timeline = Editor.ChildrenOfType<Timeline>().Single();
|
||||||
InputManager.MoveMouseTo(timeline);
|
InputManager.MoveMouseTo(timeline);
|
||||||
@ -81,19 +81,19 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddAssert("Ensure timeline zoom changed", () =>
|
AddAssert("Ensure timeline zoom changed", () =>
|
||||||
{
|
{
|
||||||
changedTimelineZoom = EditorBeatmap.BeatmapInfo.TimelineZoom;
|
changedTimelineZoom = EditorBeatmap.TimelineZoom;
|
||||||
return !Precision.AlmostEquals(changedTimelineZoom, originalTimelineZoom);
|
return !Precision.AlmostEquals(changedTimelineZoom, originalTimelineZoom);
|
||||||
});
|
});
|
||||||
|
|
||||||
SaveEditor();
|
SaveEditor();
|
||||||
|
|
||||||
AddAssert("Beatmap has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
|
AddAssert("Beatmap has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
|
||||||
AddAssert("Beatmap has correct timeline zoom", () => EditorBeatmap.BeatmapInfo.TimelineZoom == changedTimelineZoom);
|
AddAssert("Beatmap has correct timeline zoom", () => EditorBeatmap.TimelineZoom == changedTimelineZoom);
|
||||||
|
|
||||||
ReloadEditorToSameBeatmap();
|
ReloadEditorToSameBeatmap();
|
||||||
|
|
||||||
AddAssert("Beatmap still has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
|
AddAssert("Beatmap still has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
|
||||||
AddAssert("Beatmap still has correct timeline zoom", () => EditorBeatmap.BeatmapInfo.TimelineZoom == changedTimelineZoom);
|
AddAssert("Beatmap still has correct timeline zoom", () => EditorBeatmap.TimelineZoom == changedTimelineZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
beatmap.ControlPointInfo.Add(50000, new DifficultyControlPoint { SliderVelocity = 2 });
|
beatmap.ControlPointInfo.Add(50000, new DifficultyControlPoint { SliderVelocity = 2 });
|
||||||
beatmap.ControlPointInfo.Add(80000, new EffectControlPoint { KiaiMode = true });
|
beatmap.ControlPointInfo.Add(80000, new EffectControlPoint { KiaiMode = true });
|
||||||
beatmap.ControlPointInfo.Add(110000, new EffectControlPoint { KiaiMode = false });
|
beatmap.ControlPointInfo.Add(110000, new EffectControlPoint { KiaiMode = false });
|
||||||
beatmap.BeatmapInfo.Bookmarks = new[] { 75000, 125000 };
|
beatmap.Bookmarks = new[] { 75000, 125000 };
|
||||||
beatmap.Breaks.Add(new ManualBreakPeriod(90000, 120000));
|
beatmap.Breaks.Add(new ManualBreakPeriod(90000, 120000));
|
||||||
|
|
||||||
editorBeatmap = new EditorBeatmap(beatmap);
|
editorBeatmap = new EditorBeatmap(beatmap);
|
||||||
|
@ -177,6 +177,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
// bit of a hack to ensure this test can be ran multiple times without running into UNIQUE constraint failures
|
// bit of a hack to ensure this test can be ran multiple times without running into UNIQUE constraint failures
|
||||||
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = Guid.NewGuid().ToString());
|
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = Guid.NewGuid().ToString());
|
||||||
|
|
||||||
|
AddStep("start playing track", () => InputManager.Key(Key.Space));
|
||||||
AddStep("click test gameplay button", () =>
|
AddStep("click test gameplay button", () =>
|
||||||
{
|
{
|
||||||
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();
|
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();
|
||||||
@ -185,11 +186,13 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
AddUntilStep("save prompt shown", () => DialogOverlay.CurrentDialog is SaveRequiredPopupDialog);
|
AddUntilStep("save prompt shown", () => DialogOverlay.CurrentDialog is SaveRequiredPopupDialog);
|
||||||
|
AddAssert("track stopped", () => !Beatmap.Value.Track.IsRunning);
|
||||||
|
|
||||||
AddStep("save changes", () => DialogOverlay.CurrentDialog!.PerformOkAction());
|
AddStep("save changes", () => DialogOverlay.CurrentDialog!.PerformOkAction());
|
||||||
|
|
||||||
EditorPlayer editorPlayer = null;
|
EditorPlayer editorPlayer = null;
|
||||||
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
|
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
|
||||||
|
AddUntilStep("track playing", () => Beatmap.Value.Track.IsRunning);
|
||||||
AddAssert("beatmap has 1 object", () => editorPlayer.Beatmap.Value.Beatmap.HitObjects.Count == 1);
|
AddAssert("beatmap has 1 object", () => editorPlayer.Beatmap.Value.Beatmap.HitObjects.Count == 1);
|
||||||
|
|
||||||
AddUntilStep("wait for return to editor", () => Stack.CurrentScreen is Editor);
|
AddUntilStep("wait for return to editor", () => Stack.CurrentScreen is Editor);
|
||||||
|
@ -205,7 +205,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
double originalSpacing = 0;
|
double originalSpacing = 0;
|
||||||
|
|
||||||
AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.BeatmapInfo.DistanceSpacing);
|
AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.DistanceSpacing);
|
||||||
|
|
||||||
AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl));
|
AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl));
|
||||||
AddStep("hold alt", () => InputManager.PressKey(Key.LAlt));
|
AddStep("hold alt", () => InputManager.PressKey(Key.LAlt));
|
||||||
@ -215,7 +215,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt));
|
AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt));
|
||||||
AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl));
|
AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl));
|
||||||
|
|
||||||
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
|
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.DistanceSpacing == originalSpacing + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class EditorBeatmapContainer : PopoverContainer
|
public partial class EditorBeatmapContainer : PopoverContainer
|
||||||
|
@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
private TimingScreen timingScreen;
|
private TimingScreen timingScreen;
|
||||||
private EditorBeatmap editorBeatmap;
|
private EditorBeatmap editorBeatmap;
|
||||||
|
private BeatmapEditorChangeHandler changeHandler;
|
||||||
|
|
||||||
protected override bool ScrollUsingMouseWheel => false;
|
protected override bool ScrollUsingMouseWheel => false;
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
private void reloadEditorBeatmap()
|
private void reloadEditorBeatmap()
|
||||||
{
|
{
|
||||||
editorBeatmap = new EditorBeatmap(Beatmap.Value.GetPlayableBeatmap(Ruleset.Value));
|
editorBeatmap = new EditorBeatmap(Beatmap.Value.GetPlayableBeatmap(Ruleset.Value));
|
||||||
|
changeHandler = new BeatmapEditorChangeHandler(editorBeatmap);
|
||||||
|
|
||||||
Child = new DependencyProvidingContainer
|
Child = new DependencyProvidingContainer
|
||||||
{
|
{
|
||||||
@ -53,6 +55,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
CachedDependencies = new (Type, object)[]
|
CachedDependencies = new (Type, object)[]
|
||||||
{
|
{
|
||||||
(typeof(EditorBeatmap), editorBeatmap),
|
(typeof(EditorBeatmap), editorBeatmap),
|
||||||
|
(typeof(IEditorChangeHandler), changeHandler),
|
||||||
(typeof(IBeatSnapProvider), editorBeatmap)
|
(typeof(IBeatSnapProvider), editorBeatmap)
|
||||||
},
|
},
|
||||||
Child = timingScreen = new TimingScreen
|
Child = timingScreen = new TimingScreen
|
||||||
@ -72,8 +75,10 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddUntilStep("Wait for rows to load", () => Child.ChildrenOfType<EffectRowAttribute>().Any());
|
AddUntilStep("Wait for rows to load", () => Child.ChildrenOfType<EffectRowAttribute>().Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: this is best-effort for now, but the comment out test below should probably be how things should work.
|
||||||
|
// Was originally working as of https://github.com/ppy/osu/pull/26141; Regressed at some point.
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSelectedRetainedOverUndo()
|
public void TestSelectionDismissedOnUndo()
|
||||||
{
|
{
|
||||||
AddStep("Select first timing point", () =>
|
AddStep("Select first timing point", () =>
|
||||||
{
|
{
|
||||||
@ -95,25 +100,52 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
return timingScreen.SelectedGroup.Value.ControlPoints.Any(c => c is TimingControlPoint) && timingScreen.SelectedGroup.Value.Time > 2170;
|
return timingScreen.SelectedGroup.Value.ControlPoints.Any(c => c is TimingControlPoint) && timingScreen.SelectedGroup.Value.Time > 2170;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("simulate undo", () =>
|
AddStep("undo", () => changeHandler?.RestoreState(-1));
|
||||||
{
|
|
||||||
var clone = editorBeatmap.ControlPointInfo.DeepClone();
|
|
||||||
|
|
||||||
editorBeatmap.ControlPointInfo.Clear();
|
AddUntilStep("selection dismissed", () => timingScreen.SelectedGroup.Value, () => Is.Null);
|
||||||
|
|
||||||
foreach (var group in clone.Groups)
|
|
||||||
{
|
|
||||||
foreach (var cp in group.ControlPoints)
|
|
||||||
editorBeatmap.ControlPointInfo.Add(group.Time, cp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
AddUntilStep("selection retained", () =>
|
|
||||||
{
|
|
||||||
return timingScreen.SelectedGroup.Value.ControlPoints.Any(c => c is TimingControlPoint) && timingScreen.SelectedGroup.Value.Time > 2170;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [Test]
|
||||||
|
// public void TestSelectedRetainedOverUndo()
|
||||||
|
// {
|
||||||
|
// AddStep("Select first timing point", () =>
|
||||||
|
// {
|
||||||
|
// InputManager.MoveMouseTo(Child.ChildrenOfType<TimingRowAttribute>().First());
|
||||||
|
// InputManager.Click(MouseButton.Left);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 2170);
|
||||||
|
// AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 2170);
|
||||||
|
//
|
||||||
|
// AddStep("Adjust offset", () =>
|
||||||
|
// {
|
||||||
|
// InputManager.MoveMouseTo(timingScreen.ChildrenOfType<TimingAdjustButton>().First().ScreenSpaceDrawQuad.Centre + new Vector2(20, 0));
|
||||||
|
// InputManager.Click(MouseButton.Left);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// AddUntilStep("wait for offset changed", () =>
|
||||||
|
// {
|
||||||
|
// return timingScreen.SelectedGroup.Value.ControlPoints.Any(c => c is TimingControlPoint) && timingScreen.SelectedGroup.Value.Time > 2170;
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// AddStep("undo", () => changeHandler?.RestoreState(-1));
|
||||||
|
//
|
||||||
|
// AddUntilStep("selection retained", () =>
|
||||||
|
// {
|
||||||
|
// return timingScreen.SelectedGroup.Value.ControlPoints.Any(c => c is TimingControlPoint) && timingScreen.SelectedGroup.Value.Time > 2170;
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// AddAssert("check group count", () => editorBeatmap.ControlPointInfo.Groups.Count, () => Is.EqualTo(10));
|
||||||
|
//
|
||||||
|
// AddStep("Adjust offset", () =>
|
||||||
|
// {
|
||||||
|
// InputManager.MoveMouseTo(timingScreen.ChildrenOfType<TimingAdjustButton>().First().ScreenSpaceDrawQuad.Centre + new Vector2(20, 0));
|
||||||
|
// InputManager.Click(MouseButton.Left);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// AddAssert("check group count", () => editorBeatmap.ControlPointInfo.Groups.Count, () => Is.EqualTo(10));
|
||||||
|
// }
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestScrollControlGroupIntoView()
|
public void TestScrollControlGroupIntoView()
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||||
{
|
{
|
||||||
BeatmapInfo = { AudioLeadIn = leadIn }
|
AudioLeadIn = leadIn
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFirstFrameTime(expectedStartTime);
|
checkFirstFrameTime(expectedStartTime);
|
||||||
@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
Text = $"GameplayStartTime: {DrawableRuleset.GameplayStartTime} "
|
Text = $"GameplayStartTime: {DrawableRuleset.GameplayStartTime} "
|
||||||
+ $"FirstHitObjectTime: {FirstHitObjectTime} "
|
+ $"FirstHitObjectTime: {FirstHitObjectTime} "
|
||||||
+ $"LeadInTime: {Beatmap.Value.BeatmapInfo.AudioLeadIn} "
|
+ $"LeadInTime: {Beatmap.Value.Beatmap.AudioLeadIn} "
|
||||||
+ $"FirstFrameClockTime: {FirstFrameClockTime}"
|
+ $"FirstFrameClockTime: {FirstFrameClockTime}"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
@ -19,6 +20,7 @@ using osu.Game.Rulesets;
|
|||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
@ -28,6 +30,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
protected new PausePlayer Player => (PausePlayer)base.Player;
|
protected new PausePlayer Player => (PausePlayer)base.Player;
|
||||||
|
|
||||||
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
|
{
|
||||||
|
beatmap.AudioLeadIn = 4000;
|
||||||
|
return base.CreateWorkingBeatmap(beatmap, storyboard);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Container content;
|
private readonly Container content;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
@ -200,8 +208,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
[Ignore("Fails on github runners if they happen to skip too far forward in time.")]
|
||||||
public void TestUserPauseDuringCooldownTooSoon()
|
public void TestUserPauseDuringCooldownTooSoon()
|
||||||
{
|
{
|
||||||
|
AddStep("seek to gameplay", () => Player.GameplayClockContainer.Seek(0));
|
||||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
@ -213,9 +223,23 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
confirmNotExited();
|
confirmNotExited();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUserPauseDuringIntroSkipsCooldown()
|
||||||
|
{
|
||||||
|
AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000));
|
||||||
|
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||||
|
|
||||||
|
pauseAndConfirm();
|
||||||
|
|
||||||
|
resume();
|
||||||
|
pauseViaBackAction();
|
||||||
|
confirmPaused();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestQuickExitDuringCooldownTooSoon()
|
public void TestQuickExitDuringCooldownTooSoon()
|
||||||
{
|
{
|
||||||
|
AddStep("seek to gameplay", () => Player.GameplayClockContainer.Seek(0));
|
||||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
|
@ -136,10 +136,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
var workingBeatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
var workingBeatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
// Add intro time to test quick retry skipping (TestQuickRetry).
|
// Add intro time to test quick retry skipping (TestQuickRetry).
|
||||||
workingBeatmap.BeatmapInfo.AudioLeadIn = 60000;
|
workingBeatmap.Beatmap.AudioLeadIn = 60000;
|
||||||
|
|
||||||
// Set up data for testing disclaimer display.
|
// Set up data for testing disclaimer display.
|
||||||
workingBeatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning ?? false;
|
workingBeatmap.Beatmap.EpilepsyWarning = epilepsyWarning ?? false;
|
||||||
workingBeatmap.BeatmapInfo.Status = onlineStatus ?? BeatmapOnlineStatus.Ranked;
|
workingBeatmap.BeatmapInfo.Status = onlineStatus ?? BeatmapOnlineStatus.Ranked;
|
||||||
|
|
||||||
Beatmap.Value = workingBeatmap;
|
Beatmap.Value = workingBeatmap;
|
||||||
|
@ -221,7 +221,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
string? filePath = null;
|
string? filePath = null;
|
||||||
|
|
||||||
// Files starting with _ are temporary, created by CreateFileSafely call.
|
// Files starting with _ are temporary, created by CreateFileSafely call.
|
||||||
AddUntilStep("wait for export file", () => filePath = LocalStorage.GetFiles("exports").SingleOrDefault(f => !Path.GetFileName(f).StartsWith("_", StringComparison.Ordinal)), () => Is.Not.Null);
|
AddUntilStep("wait for export file", () => filePath = LocalStorage.GetFiles("exports").SingleOrDefault(f => !Path.GetFileName(f).StartsWith('_')), () => Is.Not.Null);
|
||||||
AddUntilStep("filesize is non-zero", () =>
|
AddUntilStep("filesize is non-zero", () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||||
{
|
{
|
||||||
BeatmapInfo = { AudioLeadIn = 60000 }
|
AudioLeadIn = 60000
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType<SkipOverlay>().First().IsButtonVisible);
|
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType<SkipOverlay>().First().IsButtonVisible);
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("load storyboard with only video", () =>
|
AddStep("load storyboard with only video", () =>
|
||||||
{
|
{
|
||||||
// LegacyStoryboardDecoder doesn't parse WidescreenStoryboard, so it is set manually
|
// LegacyStoryboardDecoder doesn't parse WidescreenStoryboard, so it is set manually
|
||||||
loadStoryboard("storyboard_only_video.osu", s => s.BeatmapInfo.WidescreenStoryboard = false);
|
loadStoryboard("storyboard_only_video.osu", s => s.Beatmap.WidescreenStoryboard = false);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("storyboard is correct width", () => Precision.AlmostEquals(storyboard?.Width ?? 0f, 480 * 16 / 9f));
|
AddAssert("storyboard is correct width", () => Precision.AlmostEquals(storyboard?.Width ?? 0f, 480 * 16 / 9f));
|
||||||
|
16
osu.Game.Tests/Visual/Menus/TestSceneIntroChristmas.cs
Normal file
16
osu.Game.Tests/Visual/Menus/TestSceneIntroChristmas.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
using osu.Game.Seasonal;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Menus
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public partial class TestSceneIntroChristmas : IntroTestScene
|
||||||
|
{
|
||||||
|
protected override bool IntroReliesOnTrack => true;
|
||||||
|
protected override IntroScreen CreateScreen() => new IntroChristmas();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Seasonal;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Menus
|
||||||
|
{
|
||||||
|
public partial class TestSceneMainMenuSeasonalLighting : OsuTestScene
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private BeatmapManager beatmaps { get; set; } = null!;
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("prepare beatmap", () =>
|
||||||
|
{
|
||||||
|
var setInfo = beatmaps.QueryBeatmapSet(b => b.Protected && b.Hash == IntroChristmas.CHRISTMAS_BEATMAP_SET_HASH);
|
||||||
|
|
||||||
|
if (setInfo != null)
|
||||||
|
Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Value.Beatmaps.First());
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("create lighting", () => Child = new MainMenuSeasonalLighting());
|
||||||
|
|
||||||
|
AddStep("restart beatmap", () =>
|
||||||
|
{
|
||||||
|
Beatmap.Value.Track.Start();
|
||||||
|
Beatmap.Value.Track.Seek(4000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasic()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
@ -73,5 +75,57 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
((StarFountain)Children[1]).Shoot(-1);
|
((StarFountain)Children[1]).Shoot(-1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestGameplayStarFountainsSetting()
|
||||||
|
{
|
||||||
|
Bindable<bool> starFountainsEnabled = null!;
|
||||||
|
|
||||||
|
AddStep("load configuration", () =>
|
||||||
|
{
|
||||||
|
var config = new OsuConfigManager(LocalStorage);
|
||||||
|
starFountainsEnabled = config.GetBindable<bool>(OsuSetting.StarFountains);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("make fountains", () =>
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new KiaiGameplayFountains.GameplayStarFountain
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
X = 75,
|
||||||
|
},
|
||||||
|
new KiaiGameplayFountains.GameplayStarFountain
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
X = -75,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("enable KiaiStarEffects", () => starFountainsEnabled.Value = true);
|
||||||
|
AddRepeatStep("activate fountains (enabled)", () =>
|
||||||
|
{
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
AddStep("disable KiaiStarEffects", () => starFountainsEnabled.Value = false);
|
||||||
|
AddRepeatStep("attempt to activate fountains (disabled)", () =>
|
||||||
|
{
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
AddStep("re-enable KiaiStarEffects", () => starFountainsEnabled.Value = true);
|
||||||
|
AddRepeatStep("activate fountains (re-enabled)", () =>
|
||||||
|
{
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Mods
|
|||||||
protected override TestPlayer CreateModPlayer(Ruleset ruleset)
|
protected override TestPlayer CreateModPlayer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
var player = base.CreateModPlayer(ruleset);
|
var player = base.CreateModPlayer(ruleset);
|
||||||
player.RestartRequested = _ => restartRequested = true;
|
player.PrepareLoaderForRestart = _ => restartRequested = true;
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,14 +44,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
DetachedBeatmapStore detachedBeatmapStore;
|
BeatmapStore beatmapStore;
|
||||||
|
|
||||||
Dependencies.Cache(new RealmRulesetStore(Realm));
|
Dependencies.Cache(new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
|
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
Add(detachedBeatmapStore);
|
Add(beatmapStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
|
@ -14,7 +14,6 @@ using osu.Game.Graphics.Sprites;
|
|||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Online.Rooms.RoomStatuses;
|
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||||
@ -76,7 +75,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
createLoungeRoom(new Room
|
createLoungeRoom(new Room
|
||||||
{
|
{
|
||||||
Name = "Multiplayer room",
|
Name = "Multiplayer room",
|
||||||
Status = new RoomStatusOpen(),
|
|
||||||
EndDate = DateTimeOffset.Now.AddDays(1),
|
EndDate = DateTimeOffset.Now.AddDays(1),
|
||||||
Type = MatchType.HeadToHead,
|
Type = MatchType.HeadToHead,
|
||||||
Playlist = [item1],
|
Playlist = [item1],
|
||||||
@ -85,7 +83,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
createLoungeRoom(new Room
|
createLoungeRoom(new Room
|
||||||
{
|
{
|
||||||
Name = "Private room",
|
Name = "Private room",
|
||||||
Status = new RoomStatusOpenPrivate(),
|
|
||||||
Password = "*",
|
Password = "*",
|
||||||
EndDate = DateTimeOffset.Now.AddDays(1),
|
EndDate = DateTimeOffset.Now.AddDays(1),
|
||||||
Type = MatchType.HeadToHead,
|
Type = MatchType.HeadToHead,
|
||||||
@ -95,36 +92,38 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
createLoungeRoom(new Room
|
createLoungeRoom(new Room
|
||||||
{
|
{
|
||||||
Name = "Playlist room with multiple beatmaps",
|
Name = "Playlist room with multiple beatmaps",
|
||||||
Status = new RoomStatusPlaying(),
|
Status = RoomStatus.Playing,
|
||||||
EndDate = DateTimeOffset.Now.AddDays(1),
|
EndDate = DateTimeOffset.Now.AddDays(1),
|
||||||
Playlist = [item1, item2],
|
Playlist = [item1, item2],
|
||||||
CurrentPlaylistItem = item1
|
CurrentPlaylistItem = item1
|
||||||
}),
|
}),
|
||||||
createLoungeRoom(new Room
|
createLoungeRoom(new Room
|
||||||
{
|
{
|
||||||
Name = "Finished room",
|
Name = "Closing soon",
|
||||||
Status = new RoomStatusEnded(),
|
EndDate = DateTimeOffset.Now.AddSeconds(5),
|
||||||
|
}),
|
||||||
|
createLoungeRoom(new Room
|
||||||
|
{
|
||||||
|
Name = "Closed room",
|
||||||
EndDate = DateTimeOffset.Now,
|
EndDate = DateTimeOffset.Now,
|
||||||
}),
|
}),
|
||||||
createLoungeRoom(new Room
|
createLoungeRoom(new Room
|
||||||
{
|
{
|
||||||
Name = "Spotlight room",
|
Name = "Spotlight room",
|
||||||
Status = new RoomStatusOpen(),
|
|
||||||
Category = RoomCategory.Spotlight,
|
Category = RoomCategory.Spotlight,
|
||||||
}),
|
}),
|
||||||
createLoungeRoom(new Room
|
createLoungeRoom(new Room
|
||||||
{
|
{
|
||||||
Name = "Featured artist room",
|
Name = "Featured artist room",
|
||||||
Status = new RoomStatusOpen(),
|
|
||||||
Category = RoomCategory.FeaturedArtist,
|
Category = RoomCategory.FeaturedArtist,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for panel load", () => rooms.Count == 6);
|
AddUntilStep("wait for panel load", () => rooms.Count == 7);
|
||||||
AddUntilStep("correct status text", () => rooms.ChildrenOfType<OsuSpriteText>().Count(s => s.Text.ToString().StartsWith("Currently playing", StringComparison.Ordinal)) == 2);
|
AddUntilStep("correct status text", () => rooms.ChildrenOfType<OsuSpriteText>().Count(s => s.Text.ToString().StartsWith("Currently playing", StringComparison.Ordinal)) == 2);
|
||||||
AddUntilStep("correct status text", () => rooms.ChildrenOfType<OsuSpriteText>().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)) == 4);
|
AddUntilStep("correct status text", () => rooms.ChildrenOfType<OsuSpriteText>().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)) == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -136,7 +135,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddStep("create room", () => Child = drawableRoom = createLoungeRoom(room = new Room
|
AddStep("create room", () => Child = drawableRoom = createLoungeRoom(room = new Room
|
||||||
{
|
{
|
||||||
Name = "Room with password",
|
Name = "Room with password",
|
||||||
Status = new RoomStatusOpen(),
|
|
||||||
Type = MatchType.HeadToHead,
|
Type = MatchType.HeadToHead,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -406,13 +406,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests spectating with a beatmap that has a high <see cref="BeatmapInfo.AudioLeadIn"/> value.
|
/// Tests spectating with a beatmap that has a high <see cref="IBeatmap.AudioLeadIn"/> value.
|
||||||
///
|
///
|
||||||
/// This test is not intended not to check the correct initial time value, but only to guard against
|
/// This test is not intended not to check the correct initial time value, but only to guard against
|
||||||
/// gameplay potentially getting stuck in a stopped state due to lead in time being present.
|
/// gameplay potentially getting stuck in a stopped state due to lead in time being present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAudioLeadIn() => testLeadIn(b => b.BeatmapInfo.AudioLeadIn = 2000);
|
public void TestAudioLeadIn() => testLeadIn(b => b.Beatmap.AudioLeadIn = 2000);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests spectating with a beatmap that has a storyboard element with a negative start time (i.e. intro storyboard element).
|
/// Tests spectating with a beatmap that has a storyboard element with a negative start time (i.e. intro storyboard element).
|
||||||
|
@ -66,14 +66,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
DetachedBeatmapStore detachedBeatmapStore;
|
BeatmapStore beatmapStore;
|
||||||
|
|
||||||
Dependencies.Cache(new RealmRulesetStore(Realm));
|
Dependencies.Cache(new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
|
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
Add(detachedBeatmapStore);
|
Add(beatmapStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
|
@ -46,16 +46,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
DetachedBeatmapStore detachedBeatmapStore;
|
BeatmapStore beatmapStore;
|
||||||
|
|
||||||
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
|
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
importedBeatmapSet = manager.Import(TestResources.CreateTestBeatmapSetInfo(8, rulesets.AvailableRulesets.ToArray()))!;
|
importedBeatmapSet = manager.Import(TestResources.CreateTestBeatmapSetInfo(8, rulesets.AvailableRulesets.ToArray()))!;
|
||||||
|
|
||||||
Add(detachedBeatmapStore);
|
Add(beatmapStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUp()
|
private void setUp()
|
||||||
|
@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
AddStep("make second user host", () => MultiplayerClient.TransferHost(3));
|
AddStep("make second user host", () => MultiplayerClient.TransferHost(3));
|
||||||
|
|
||||||
AddUntilStep("kick buttons not visible", () => this.ChildrenOfType<ParticipantPanel.KickButton>().Count(d => d.IsPresent) == 0);
|
AddUntilStep("kick buttons not visible", () => !this.ChildrenOfType<ParticipantPanel.KickButton>().Any(d => d.IsPresent));
|
||||||
|
|
||||||
AddStep("make local user host again", () => MultiplayerClient.TransferHost(API.LocalUser.Value.Id));
|
AddStep("make local user host again", () => MultiplayerClient.TransferHost(API.LocalUser.Value.Id));
|
||||||
|
|
||||||
|
@ -31,18 +31,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
DetachedBeatmapStore detachedBeatmapStore;
|
BeatmapStore beatmapStore;
|
||||||
|
|
||||||
Dependencies.Cache(new RealmRulesetStore(Realm));
|
Dependencies.Cache(new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
|
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
var beatmapSet = TestResources.CreateTestBeatmapSetInfo();
|
var beatmapSet = TestResources.CreateTestBeatmapSetInfo();
|
||||||
|
|
||||||
manager.Import(beatmapSet);
|
manager.Import(beatmapSet);
|
||||||
|
|
||||||
Add(detachedBeatmapStore);
|
Add(beatmapStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
});
|
});
|
||||||
AddStep("create IPC sender channels", () =>
|
AddStep("create IPC sender channels", () =>
|
||||||
{
|
{
|
||||||
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { IPCPort = OsuGame.IPC_PORT });
|
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { IPCPipeName = OsuGame.IPC_PIPE_NAME });
|
||||||
osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost);
|
osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost);
|
||||||
archiveImportIPCSender = new ArchiveImportIPCChannel(ipcSenderHost);
|
archiveImportIPCSender = new ArchiveImportIPCChannel(ipcSenderHost);
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -58,7 +56,11 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
// First scroll makes volume controls appear, second adjusts volume.
|
// First scroll makes volume controls appear, second adjusts volume.
|
||||||
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 10);
|
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 10);
|
||||||
AddAssert("Volume is still zero", () => Game.Audio.Volume.Value == 0);
|
AddAssert("Volume is still zero", () => Game.Audio.Volume.Value, () => Is.Zero);
|
||||||
|
|
||||||
|
AddStep("Pause", () => InputManager.PressKey(Key.Escape));
|
||||||
|
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 10);
|
||||||
|
AddAssert("Volume is above zero", () => Game.Audio.Volume.Value > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -80,8 +82,8 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
private void loadToPlayerNonBreakTime()
|
private void loadToPlayerNonBreakTime()
|
||||||
{
|
{
|
||||||
Player player = null;
|
Player? player = null;
|
||||||
Screens.Select.SongSelect songSelect = null;
|
Screens.Select.SongSelect songSelect = null!;
|
||||||
PushAndConfirm(() => songSelect = new TestSceneScreenNavigation.TestPlaySongSelect());
|
PushAndConfirm(() => songSelect = new TestSceneScreenNavigation.TestPlaySongSelect());
|
||||||
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
|
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
|
||||||
|
|
||||||
@ -95,7 +97,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
|
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for play time active", () => !player.IsBreakTime.Value);
|
AddUntilStep("wait for play time active", () => player!.IsBreakTime.Value, () => Is.False);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,6 +354,23 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddAssert("retry count is 1", () => player.RestartCount == 1);
|
AddAssert("retry count is 1", () => player.RestartCount == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLastScoreNullAfterExitingPlayer()
|
||||||
|
{
|
||||||
|
AddUntilStep("wait for last play null", getLastPlay, () => Is.Null);
|
||||||
|
|
||||||
|
var getOriginalPlayer = playToCompletion();
|
||||||
|
|
||||||
|
AddStep("attempt to retry", () => getOriginalPlayer().ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
||||||
|
AddUntilStep("wait for last play matches player", getLastPlay, () => Is.EqualTo(getOriginalPlayer().Score.ScoreInfo));
|
||||||
|
|
||||||
|
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != getOriginalPlayer() && Game.ScreenStack.CurrentScreen is Player);
|
||||||
|
AddStep("exit player", () => (Game.ScreenStack.CurrentScreen as Player)?.Exit());
|
||||||
|
AddUntilStep("wait for last play null", getLastPlay, () => Is.Null);
|
||||||
|
|
||||||
|
ScoreInfo getLastPlay() => Game.Dependencies.Get<SessionStatics>().Get<ScoreInfo>(Static.LastLocalUserScore);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRetryImmediatelyAfterCompletion()
|
public void TestRetryImmediatelyAfterCompletion()
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,7 @@ using osu.Game.Rulesets.Osu;
|
|||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.Edit.Components;
|
using osu.Game.Screens.Edit.Components;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Tests.Beatmaps.IO;
|
using osu.Game.Tests.Beatmaps.IO;
|
||||||
@ -212,6 +213,33 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any());
|
AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestGameplaySettingsDoesNotExpandWhenSkinOverlayPresent()
|
||||||
|
{
|
||||||
|
advanceToSongSelect();
|
||||||
|
openSkinEditor();
|
||||||
|
AddStep("select autoplay", () => Game.SelectedMods.Value = new Mod[] { new OsuModAutoplay() });
|
||||||
|
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
|
||||||
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
switchToGameplayScene();
|
||||||
|
|
||||||
|
AddUntilStep("wait for settings", () => getPlayerSettingsOverlay() != null);
|
||||||
|
AddAssert("settings not visible", () => getPlayerSettingsOverlay().DrawWidth, () => Is.EqualTo(0));
|
||||||
|
|
||||||
|
AddStep("move cursor to right of screen", () => InputManager.MoveMouseTo(InputManager.ScreenSpaceDrawQuad.TopRight));
|
||||||
|
AddAssert("settings not visible", () => getPlayerSettingsOverlay().DrawWidth, () => Is.EqualTo(0));
|
||||||
|
|
||||||
|
toggleSkinEditor();
|
||||||
|
|
||||||
|
AddStep("move cursor slightly", () => InputManager.MoveMouseTo(InputManager.ScreenSpaceDrawQuad.TopRight + new Vector2(1)));
|
||||||
|
AddUntilStep("settings visible", () => getPlayerSettingsOverlay().DrawWidth, () => Is.GreaterThan(0));
|
||||||
|
|
||||||
|
AddStep("move cursor to right of screen too far", () => InputManager.MoveMouseTo(InputManager.ScreenSpaceDrawQuad.TopRight + new Vector2(10240, 0)));
|
||||||
|
AddUntilStep("settings not visible", () => getPlayerSettingsOverlay().DrawWidth, () => Is.EqualTo(0));
|
||||||
|
|
||||||
|
PlayerSettingsOverlay getPlayerSettingsOverlay() => ((Player)Game.ScreenStack.CurrentScreen).ChildrenOfType<PlayerSettingsOverlay>().SingleOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCinemaModRemovedOnEnteringGameplay()
|
public void TestCinemaModRemovedOnEnteringGameplay()
|
||||||
{
|
{
|
||||||
|
@ -457,6 +457,61 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
waitForChannel1Visible();
|
waitForChannel1Visible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPublicChannelsSortedByName()
|
||||||
|
{
|
||||||
|
// Intentionally join back to front.
|
||||||
|
AddStep("Show overlay with channel 2", () =>
|
||||||
|
{
|
||||||
|
channelManager.CurrentChannel.Value = channelManager.JoinChannel(testChannel2);
|
||||||
|
chatOverlay.Show();
|
||||||
|
});
|
||||||
|
AddUntilStep("second channel is at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel2);
|
||||||
|
|
||||||
|
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
||||||
|
AddUntilStep("first channel is at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel1);
|
||||||
|
|
||||||
|
AddStep("message in channel 2", () =>
|
||||||
|
{
|
||||||
|
testChannel2.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
|
||||||
|
});
|
||||||
|
AddUntilStep("first channel still at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel1);
|
||||||
|
|
||||||
|
ChannelListItem getFirstVisiblePublicChannel() =>
|
||||||
|
chatOverlay.ChildrenOfType<ChannelList>().Single().PublicChannelGroup.ItemFlow.FlowingChildren.OfType<ChannelListItem>().First(item => item.Channel.Type == ChannelType.Public);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPrivateChannelsSortedByRecent()
|
||||||
|
{
|
||||||
|
Channel pmChannel1 = createPrivateChannel();
|
||||||
|
Channel pmChannel2 = createPrivateChannel();
|
||||||
|
|
||||||
|
joinChannel(pmChannel1);
|
||||||
|
joinChannel(pmChannel2);
|
||||||
|
|
||||||
|
AddStep("Show overlay", () => chatOverlay.Show());
|
||||||
|
|
||||||
|
AddUntilStep("first channel is at top of list", () => getFirstVisiblePMChannel().Channel == pmChannel1);
|
||||||
|
|
||||||
|
AddStep("message in channel 2", () =>
|
||||||
|
{
|
||||||
|
pmChannel2.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for first channel raised to top of list", () => getFirstVisiblePMChannel().Channel == pmChannel2);
|
||||||
|
|
||||||
|
AddStep("message in channel 1", () =>
|
||||||
|
{
|
||||||
|
pmChannel1.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for first channel raised to top of list", () => getFirstVisiblePMChannel().Channel == pmChannel1);
|
||||||
|
|
||||||
|
ChannelListItem getFirstVisiblePMChannel() =>
|
||||||
|
chatOverlay.ChildrenOfType<ChannelList>().Single().PrivateChannelGroup.ItemFlow.FlowingChildren.OfType<ChannelListItem>().First(item => item.Channel.Type == ChannelType.PM);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestKeyboardNewChannel()
|
public void TestKeyboardNewChannel()
|
||||||
{
|
{
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddSliderStep("weekly best", 0, 250, 1, v => update(s => s.WeeklyStreakBest = v));
|
AddSliderStep("weekly best", 0, 250, 1, v => update(s => s.WeeklyStreakBest = v));
|
||||||
AddSliderStep("top 10%", 0, 999, 0, v => update(s => s.Top10PercentPlacements = v));
|
AddSliderStep("top 10%", 0, 999, 0, v => update(s => s.Top10PercentPlacements = v));
|
||||||
AddSliderStep("top 50%", 0, 999, 0, v => update(s => s.Top50PercentPlacements = v));
|
AddSliderStep("top 50%", 0, 999, 0, v => update(s => s.Top50PercentPlacements = v));
|
||||||
AddSliderStep("playcount", 0, 999, 0, v => update(s => s.PlayCount = v));
|
AddSliderStep("playcount", 0, 1500, 1, v => update(s => s.PlayCount = v));
|
||||||
AddStep("create", () =>
|
AddStep("create", () =>
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
@ -66,8 +66,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPlayCountRankingTier()
|
public void TestPlayCountRankingTier()
|
||||||
{
|
{
|
||||||
AddAssert("1 before silver", () => DailyChallengeStatsTooltip.TierForPlayCount(30) == RankingTier.Bronze);
|
AddAssert("1 before silver", () => DailyChallengeStatsTooltip.TierForPlayCount(29) == RankingTier.Bronze);
|
||||||
AddAssert("first silver", () => DailyChallengeStatsTooltip.TierForPlayCount(31) == RankingTier.Silver);
|
AddAssert("first silver", () => DailyChallengeStatsTooltip.TierForPlayCount(30) == RankingTier.Silver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -31,7 +32,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
private const int scores_per_result = 10;
|
private const int scores_per_result = 10;
|
||||||
private const int real_user_position = 200;
|
private const int real_user_position = 200;
|
||||||
|
|
||||||
private TestResultsScreen resultsScreen = null!;
|
private ResultsScreen resultsScreen = null!;
|
||||||
|
|
||||||
private int lowestScoreId; // Score ID of the lowest score in the list.
|
private int lowestScoreId; // Score ID of the lowest score in the list.
|
||||||
private int highestScoreId; // Score ID of the highest score in the list.
|
private int highestScoreId; // Score ID of the highest score in the list.
|
||||||
@ -68,11 +69,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowWithUserScore()
|
public void TestShowUserScore()
|
||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
||||||
|
|
||||||
createResults(() => userScore);
|
createResultsWithScore(() => userScore);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.OnlineID == userScore.OnlineID).State == PanelState.Expanded);
|
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.OnlineID == userScore.OnlineID).State == PanelState.Expanded);
|
||||||
@ -81,11 +82,24 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowNullUserScore()
|
public void TestShowUserBest()
|
||||||
|
{
|
||||||
|
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
||||||
|
|
||||||
|
createUserBestResults();
|
||||||
|
waitForDisplay();
|
||||||
|
|
||||||
|
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.UserID == userScore.UserID).State == PanelState.Expanded);
|
||||||
|
AddAssert($"score panel position is {real_user_position}",
|
||||||
|
() => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.UserID == userScore.UserID).ScorePosition.Value == real_user_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShowNonUserScores()
|
||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler());
|
AddStep("bind user score info handler", () => bindHandler());
|
||||||
|
|
||||||
createResults();
|
createUserBestResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
|
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
|
||||||
@ -96,7 +110,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler(true, userScore));
|
AddStep("bind user score info handler", () => bindHandler(true, userScore));
|
||||||
|
|
||||||
createResults(() => userScore);
|
createResultsWithScore(() => userScore);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("more than 1 panel displayed", () => this.ChildrenOfType<ScorePanel>().Count() > 1);
|
AddAssert("more than 1 panel displayed", () => this.ChildrenOfType<ScorePanel>().Count() > 1);
|
||||||
@ -104,11 +118,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowNullUserScoreWithDelay()
|
public void TestShowNonUserScoresWithDelay()
|
||||||
{
|
{
|
||||||
AddStep("bind delayed handler", () => bindHandler(true));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
|
|
||||||
createResults();
|
createUserBestResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
|
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
|
||||||
@ -119,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddStep("bind delayed handler", () => bindHandler(true));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
|
|
||||||
createResults();
|
createUserBestResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
@ -127,13 +141,16 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
int beforePanelCount = 0;
|
int beforePanelCount = 0;
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||||
|
|
||||||
|
AddAssert("right loading spinner shown", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
AddAssert("right loading spinner hidden", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,29 +159,36 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddStep("bind delayed handler with scores", () => bindHandler(delayed: true));
|
AddStep("bind delayed handler with scores", () => bindHandler(delayed: true));
|
||||||
|
|
||||||
createResults();
|
createUserBestResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
int beforePanelCount = 0;
|
int beforePanelCount = 0;
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||||
|
|
||||||
|
AddAssert("right loading spinner shown", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
AddAssert("right loading spinner hidden", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
AddStep("bind delayed handler with no scores", () => bindHandler(delayed: true, noScores: true));
|
AddStep("bind delayed handler with no scores", () => bindHandler(delayed: true, noScores: true));
|
||||||
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||||
|
|
||||||
|
AddAssert("right loading spinner shown", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("count not increased", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount);
|
AddAssert("count not increased", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount);
|
||||||
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
AddAssert("right loading spinner hidden", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||||
|
|
||||||
AddAssert("no placeholders shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.Zero);
|
AddAssert("no placeholders shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +197,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
||||||
|
|
||||||
createResults(() => userScore);
|
createResultsWithScore(() => userScore);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddStep("bind delayed handler", () => bindHandler(true));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
@ -183,30 +207,36 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
int beforePanelCount = 0;
|
int beforePanelCount = 0;
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
AddStep("scroll to left", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
|
AddStep("scroll to left", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
|
||||||
|
|
||||||
|
AddAssert("left loading spinner shown", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddAssert("left loading spinner shown", () => resultsScreen.LeftSpinner.State.Value == Visibility.Visible);
|
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("left loading spinner hidden", () => resultsScreen.LeftSpinner.State.Value == Visibility.Hidden);
|
AddAssert("left loading spinner hidden", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the <see cref="TestUserBestResultsScreen"/> with no scores provided by the API.
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowWithNoScores()
|
public void TestShowUserBestWithNoScoresPresent()
|
||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler(noScores: true));
|
AddStep("bind user score info handler", () => bindHandler(noScores: true));
|
||||||
createResults();
|
createUserBestResults();
|
||||||
AddAssert("no scores visible", () => !resultsScreen.ScorePanelList.GetScorePanels().Any());
|
AddAssert("no scores visible", () => !resultsScreen.ChildrenOfType<ScorePanelList>().Single().GetScorePanels().Any());
|
||||||
AddAssert("placeholder shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.EqualTo(1));
|
AddAssert("placeholder shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.EqualTo(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createResults(Func<ScoreInfo>? getScore = null)
|
private void createResultsWithScore(Func<ScoreInfo> getScore)
|
||||||
{
|
{
|
||||||
AddStep("load results", () =>
|
AddStep("load results", () =>
|
||||||
{
|
{
|
||||||
LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
LoadScreen(resultsScreen = new TestScoreResultsScreen(getScore(), 1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||||
{
|
{
|
||||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||||
}));
|
}));
|
||||||
@ -215,14 +245,27 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded);
|
AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createUserBestResults()
|
||||||
|
{
|
||||||
|
AddStep("load results", () =>
|
||||||
|
{
|
||||||
|
LoadScreen(resultsScreen = new TestUserBestResultsScreen(1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||||
|
{
|
||||||
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||||
|
}, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForDisplay()
|
private void waitForDisplay()
|
||||||
{
|
{
|
||||||
AddUntilStep("wait for scores loaded", () =>
|
AddUntilStep("wait for scores loaded", () =>
|
||||||
requestComplete
|
requestComplete
|
||||||
// request handler may need to fire more than once to get scores.
|
// request handler may need to fire more than once to get scores.
|
||||||
&& totalCount > 0
|
&& totalCount > 0
|
||||||
&& resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount
|
&& resultsScreen.ChildrenOfType<ScorePanelList>().Single().GetScorePanels().Count() == totalCount
|
||||||
&& resultsScreen.ScorePanelList.AllPanelsVisible);
|
&& resultsScreen.ChildrenOfType<ScorePanelList>().Single().AllPanelsVisible);
|
||||||
AddWaitStep("wait for display", 5);
|
AddWaitStep("wait for display", 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,6 +274,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
// pre-check for requests we should be handling (as they are scheduled below).
|
// pre-check for requests we should be handling (as they are scheduled below).
|
||||||
switch (request)
|
switch (request)
|
||||||
{
|
{
|
||||||
|
case ShowPlaylistScoreRequest:
|
||||||
case ShowPlaylistUserScoreRequest:
|
case ShowPlaylistUserScoreRequest:
|
||||||
case IndexPlaylistScoresRequest:
|
case IndexPlaylistScoresRequest:
|
||||||
break;
|
break;
|
||||||
@ -253,7 +297,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
|
|
||||||
switch (request)
|
switch (request)
|
||||||
{
|
{
|
||||||
case ShowPlaylistUserScoreRequest s:
|
case ShowPlaylistScoreRequest s:
|
||||||
if (userScore == null)
|
if (userScore == null)
|
||||||
triggerFail(s);
|
triggerFail(s);
|
||||||
else
|
else
|
||||||
@ -261,6 +305,14 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ShowPlaylistUserScoreRequest u:
|
||||||
|
if (userScore == null)
|
||||||
|
triggerFail(u);
|
||||||
|
else
|
||||||
|
triggerSuccess(u, createUserResponse(userScore));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case IndexPlaylistScoresRequest i:
|
case IndexPlaylistScoresRequest i:
|
||||||
triggerSuccess(i, createIndexResponse(i, noScores));
|
triggerSuccess(i, createIndexResponse(i, noScores));
|
||||||
break;
|
break;
|
||||||
@ -314,7 +366,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
MaxCombo = userScore.MaxCombo,
|
MaxCombo = userScore.MaxCombo,
|
||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2 + i,
|
||||||
Username = $"peppy{i}",
|
Username = $"peppy{i}",
|
||||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
},
|
},
|
||||||
@ -329,7 +381,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
MaxCombo = userScore.MaxCombo,
|
MaxCombo = userScore.MaxCombo,
|
||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2 + i,
|
||||||
Username = $"peppy{i}",
|
Username = $"peppy{i}",
|
||||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
},
|
},
|
||||||
@ -363,7 +415,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
MaxCombo = 1000,
|
MaxCombo = 1000,
|
||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2 + i,
|
||||||
Username = $"peppy{i}",
|
Username = $"peppy{i}",
|
||||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
},
|
},
|
||||||
@ -410,18 +462,22 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class TestResultsScreen : PlaylistItemUserResultsScreen
|
private partial class TestScoreResultsScreen : PlaylistItemScoreResultsScreen
|
||||||
{
|
{
|
||||||
public new LoadingSpinner LeftSpinner => base.LeftSpinner;
|
public TestScoreResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem)
|
||||||
public new LoadingSpinner CentreSpinner => base.CentreSpinner;
|
|
||||||
public new LoadingSpinner RightSpinner => base.RightSpinner;
|
|
||||||
public new ScorePanelList ScorePanelList => base.ScorePanelList;
|
|
||||||
|
|
||||||
public TestResultsScreen(ScoreInfo? score, int roomId, PlaylistItem playlistItem)
|
|
||||||
: base(score, roomId, playlistItem)
|
: base(score, roomId, playlistItem)
|
||||||
{
|
{
|
||||||
AllowRetry = true;
|
AllowRetry = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private partial class TestUserBestResultsScreen : PlaylistItemUserBestResultsScreen
|
||||||
|
{
|
||||||
|
public TestUserBestResultsScreen(int roomId, PlaylistItem playlistItem, int userId)
|
||||||
|
: base(roomId, playlistItem, userId)
|
||||||
|
{
|
||||||
|
AllowRetry = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Screens;
|
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Online.Rooms;
|
|
||||||
using osu.Game.Online.Rooms.RoomStatuses;
|
|
||||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
|
||||||
using osu.Game.Tests.Visual.OnlinePlay;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Playlists
|
|
||||||
{
|
|
||||||
public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
|
|
||||||
{
|
|
||||||
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestStatusUpdateOnEnter()
|
|
||||||
{
|
|
||||||
Room room = null!;
|
|
||||||
PlaylistsRoomSubScreen roomScreen = null!;
|
|
||||||
|
|
||||||
AddStep("create room", () =>
|
|
||||||
{
|
|
||||||
RoomManager.AddRoom(room = new Room
|
|
||||||
{
|
|
||||||
Name = @"Test Room",
|
|
||||||
Host = new APIUser { Username = @"Host" },
|
|
||||||
Category = RoomCategory.Normal,
|
|
||||||
EndDate = DateTimeOffset.Now.AddMinutes(-1)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
AddStep("push screen", () => LoadScreen(roomScreen = new PlaylistsRoomSubScreen(room)));
|
|
||||||
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
|
|
||||||
AddAssert("status is still ended", () => roomScreen.Room.Status, Is.TypeOf<RoomStatusEnded>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,6 +16,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Database;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Catch;
|
using osu.Game.Rulesets.Catch;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
@ -23,6 +24,7 @@ using osu.Game.Rulesets.Taiko;
|
|||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osu.Game.Screens.Select.Carousel;
|
using osu.Game.Screens.Select.Carousel;
|
||||||
using osu.Game.Screens.Select.Filter;
|
using osu.Game.Screens.Select.Filter;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
@ -42,6 +44,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
private const int set_count = 5;
|
private const int set_count = 5;
|
||||||
private const int diff_count = 3;
|
private const int diff_count = 3;
|
||||||
|
|
||||||
|
[Cached(typeof(BeatmapStore))]
|
||||||
|
private TestBeatmapStore beatmaps = new TestBeatmapStore();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetStore rulesets)
|
private void load(RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
@ -1329,7 +1334,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
carouselAdjust?.Invoke(carousel);
|
carouselAdjust?.Invoke(carousel);
|
||||||
|
|
||||||
carousel.BeatmapSets = beatmapSets;
|
beatmaps.BeatmapSets.Clear();
|
||||||
|
beatmaps.BeatmapSets.AddRange(beatmapSets);
|
||||||
|
|
||||||
(target ?? this).Child = carousel;
|
(target ?? this).Child = carousel;
|
||||||
});
|
});
|
||||||
|
@ -13,9 +13,12 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.BeatmapListing;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Catch;
|
using osu.Game.Rulesets.Catch;
|
||||||
using osu.Game.Rulesets.Mania;
|
using osu.Game.Rulesets.Mania;
|
||||||
@ -23,6 +26,8 @@ using osu.Game.Rulesets.Osu;
|
|||||||
using osu.Game.Rulesets.Taiko;
|
using osu.Game.Rulesets.Taiko;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.SongSelect
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
{
|
{
|
||||||
@ -63,7 +68,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
return 336; // recommended star rating of 2
|
return 336; // recommended star rating of 2
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return 928; // SR 3
|
return 973; // SR 3
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
return 1905; // SR 4
|
return 1905; // SR 4
|
||||||
@ -170,6 +175,45 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
presentAndConfirm(() => maniaSet, 5);
|
presentAndConfirm(() => maniaSet, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapListingFilter()
|
||||||
|
{
|
||||||
|
AddStep("set playmode to taiko", () => ((DummyAPIAccess)API).LocalUser.Value.PlayMode = "taiko");
|
||||||
|
|
||||||
|
AddStep("open beatmap listing", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.PressKey(Key.B);
|
||||||
|
InputManager.ReleaseKey(Key.B);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for load", () => Game.ChildrenOfType<BeatmapListingOverlay>().SingleOrDefault()?.IsLoaded, () => Is.True);
|
||||||
|
|
||||||
|
checkRecommendedDifficulty(3);
|
||||||
|
|
||||||
|
AddStep("change mode filter to osu!", () => Game.ChildrenOfType<BeatmapSearchRulesetFilterRow>().Single().ChildrenOfType<FilterTabItem<RulesetInfo>>().ElementAt(1).TriggerClick());
|
||||||
|
|
||||||
|
checkRecommendedDifficulty(2);
|
||||||
|
|
||||||
|
AddStep("change mode filter to osu!taiko", () => Game.ChildrenOfType<BeatmapSearchRulesetFilterRow>().Single().ChildrenOfType<FilterTabItem<RulesetInfo>>().ElementAt(2).TriggerClick());
|
||||||
|
|
||||||
|
checkRecommendedDifficulty(3);
|
||||||
|
|
||||||
|
AddStep("change mode filter to osu!catch", () => Game.ChildrenOfType<BeatmapSearchRulesetFilterRow>().Single().ChildrenOfType<FilterTabItem<RulesetInfo>>().ElementAt(3).TriggerClick());
|
||||||
|
|
||||||
|
checkRecommendedDifficulty(4);
|
||||||
|
|
||||||
|
AddStep("change mode filter to osu!mania", () => Game.ChildrenOfType<BeatmapSearchRulesetFilterRow>().Single().ChildrenOfType<FilterTabItem<RulesetInfo>>().ElementAt(4).TriggerClick());
|
||||||
|
|
||||||
|
checkRecommendedDifficulty(5);
|
||||||
|
|
||||||
|
void checkRecommendedDifficulty(double starRating)
|
||||||
|
=> AddAssert($"recommended difficulty is {starRating}",
|
||||||
|
() => Game.ChildrenOfType<BeatmapSearchGeneralFilterRow>().Single().ChildrenOfType<OsuSpriteText>().ElementAt(1).Text.ToString(),
|
||||||
|
() => Is.EqualTo($"Recommended difficulty ({starRating.FormatStarRating()})"));
|
||||||
|
}
|
||||||
|
|
||||||
private BeatmapSetInfo importBeatmapSet(IEnumerable<RulesetInfo> difficultyRulesets)
|
private BeatmapSetInfo importBeatmapSet(IEnumerable<RulesetInfo> difficultyRulesets)
|
||||||
{
|
{
|
||||||
var rulesets = difficultyRulesets.ToArray();
|
var rulesets = difficultyRulesets.ToArray();
|
||||||
|
@ -56,20 +56,20 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
DetachedBeatmapStore detachedBeatmapStore;
|
BeatmapStore beatmapStore;
|
||||||
|
|
||||||
// These DI caches are required to ensure for interactive runs this test scene doesn't nuke all user beatmaps in the local install.
|
// These DI caches are required to ensure for interactive runs this test scene doesn't nuke all user beatmaps in the local install.
|
||||||
// At a point we have isolated interactive test runs enough, this can likely be removed.
|
// At a point we have isolated interactive test runs enough, this can likely be removed.
|
||||||
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, defaultBeatmap = Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, defaultBeatmap = Beatmap.Default));
|
||||||
Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore());
|
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
|
||||||
|
|
||||||
Dependencies.Cache(music = new MusicController());
|
Dependencies.Cache(music = new MusicController());
|
||||||
|
|
||||||
// required to get bindables attached
|
// required to get bindables attached
|
||||||
Add(music);
|
Add(music);
|
||||||
Add(detachedBeatmapStore);
|
Add(beatmapStore);
|
||||||
|
|
||||||
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
|
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -10,12 +9,14 @@ using osu.Framework.Extensions.ObjectExtensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Database;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Dialog;
|
using osu.Game.Overlays.Dialog;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osu.Game.Screens.Select.Carousel;
|
using osu.Game.Screens.Select.Carousel;
|
||||||
using osu.Game.Screens.Select.Filter;
|
using osu.Game.Screens.Select.Filter;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osu.Game.Tests.Online;
|
using osu.Game.Tests.Online;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -31,6 +32,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
private BeatmapSetInfo testBeatmapSetInfo = null!;
|
private BeatmapSetInfo testBeatmapSetInfo = null!;
|
||||||
|
|
||||||
|
[Cached(typeof(BeatmapStore))]
|
||||||
|
private TestBeatmapStore beatmaps = new TestBeatmapStore();
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
{
|
{
|
||||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
@ -246,13 +250,12 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
private BeatmapCarousel createCarousel()
|
private BeatmapCarousel createCarousel()
|
||||||
{
|
{
|
||||||
|
beatmaps.BeatmapSets.Clear();
|
||||||
|
beatmaps.BeatmapSets.Add(testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo(5));
|
||||||
|
|
||||||
return carousel = new BeatmapCarousel(new FilterCriteria())
|
return carousel = new BeatmapCarousel(new FilterCriteria())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
BeatmapSets = new List<BeatmapSetInfo>
|
|
||||||
{
|
|
||||||
(testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo(5)),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Rulesets;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Legacy;
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Screens.SelectV2;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.SongSelectV2
|
namespace osu.Game.Tests.Visual.SongSelectV2
|
||||||
{
|
{
|
||||||
|
@ -101,7 +101,16 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new OsuMenuItem(@"Another nested option")
|
||||||
|
{
|
||||||
|
Items = new MenuItem[]
|
||||||
|
{
|
||||||
|
new OsuMenuItem(@"Sub-One"),
|
||||||
|
new OsuMenuItem(@"Sub-Two"),
|
||||||
|
new OsuMenuItem(@"Sub-Three"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new OsuMenuItem(@"Choose me please"),
|
new OsuMenuItem(@"Choose me please"),
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user