同步更新,Ez设置大优化,增强兼容官方数据库

This commit is contained in:
LA
2025-07-06 16:56:21 +08:00
parent ed234db58c
commit bfe675ce50
164 changed files with 2610 additions and 3858 deletions

View File

@@ -10,7 +10,7 @@
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2025.625.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2025.704.0" />
</ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.

View File

@@ -1,30 +1,32 @@
{
"solution": {
"path": "osu.sln",
"projects": [
"..\\osu-resources\\osu.Game.Resources\\osu.Game.Resources.csproj",
"osu.Desktop\\osu.Desktop.csproj",
"osu.Game.Benchmarks\\osu.Game.Benchmarks.csproj",
"osu.Game.Rulesets.Catch.Tests\\osu.Game.Rulesets.Catch.Tests.csproj",
"osu.Game.Rulesets.Catch\\osu.Game.Rulesets.Catch.csproj",
"osu.Game.Rulesets.Mania.Tests\\osu.Game.Rulesets.Mania.Tests.csproj",
"osu.Game.Rulesets.Mania\\osu.Game.Rulesets.Mania.csproj",
"osu.Game.Rulesets.Osu.Tests\\osu.Game.Rulesets.Osu.Tests.csproj",
"osu.Game.Rulesets.Osu\\osu.Game.Rulesets.Osu.csproj",
"osu.Game.Rulesets.Taiko.Tests\\osu.Game.Rulesets.Taiko.Tests.csproj",
"osu.Game.Rulesets.Taiko\\osu.Game.Rulesets.Taiko.csproj",
"osu.Game.Tests\\osu.Game.Tests.csproj",
"osu.Game.Tournament.Tests\\osu.Game.Tournament.Tests.csproj",
"osu.Game.Tournament\\osu.Game.Tournament.csproj",
"osu.Game\\osu.Game.csproj",
"Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform.Tests\\osu.Game.Rulesets.EmptyFreeform.Tests.csproj",
"Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform\\osu.Game.Rulesets.EmptyFreeform.csproj",
"Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj",
"Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj",
"Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling.Tests\\osu.Game.Rulesets.EmptyScrolling.Tests.csproj",
"Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling\\osu.Game.Rulesets.EmptyScrolling.csproj",
"Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj",
"Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj"
]
}
}
"solution": {
"path": "osu.sln",
"projects": [
"..\\osu-resources\\osu.Game.Resources\\osu.Game.Resources.csproj",
"osu.Desktop\\osu.Desktop.csproj",
"osu.Game.Benchmarks\\osu.Game.Benchmarks.csproj",
"osu.Game.Rulesets.Catch.Tests\\osu.Game.Rulesets.Catch.Tests.csproj",
"osu.Game.Rulesets.Catch\\osu.Game.Rulesets.Catch.csproj",
"osu.Game.Rulesets.Mania.Tests\\osu.Game.Rulesets.Mania.Tests.csproj",
"osu.Game.Rulesets.Mania\\osu.Game.Rulesets.Mania.csproj",
"osu.Game.Rulesets.Osu.Tests\\osu.Game.Rulesets.Osu.Tests.csproj",
"osu.Game.Rulesets.Osu\\osu.Game.Rulesets.Osu.csproj",
"osu.Game.Rulesets.Taiko.Tests\\osu.Game.Rulesets.Taiko.Tests.csproj",
"osu.Game.Rulesets.Taiko\\osu.Game.Rulesets.Taiko.csproj",
"osu.Game.Tests\\osu.Game.Tests.csproj",
"osu.Game.Tournament.Tests\\osu.Game.Tournament.Tests.csproj",
"osu.Game.Tournament\\osu.Game.Tournament.csproj",
"osu.Game\\osu.Game.csproj",
"Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform.Tests\\osu.Game.Rulesets.EmptyFreeform.Tests.csproj",
"Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform\\osu.Game.Rulesets.EmptyFreeform.csproj",
"Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj",
"Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj",
"Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling.Tests\\osu.Game.Rulesets.EmptyScrolling.Tests.csproj",
"Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling\\osu.Game.Rulesets.EmptyScrolling.csproj",
"Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj",
"Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj",
"../osu-framework/osu.Framework/osu.Framework.csproj",
"../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj"
]
}
}

View File

@@ -4,10 +4,10 @@
<OutputType>WinExe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
<AssemblyName>osu!</AssemblyName>
<AssemblyTitle>osu!(lazer)</AssemblyTitle>
<Title>osu!</Title>
<Product>osu!(lazer)</Product>
<AssemblyName>Ez2osu!</AssemblyName>
<AssemblyTitle>osu!(Ez2lazer)</AssemblyTitle>
<Title>Ez2lazer!</Title>
<Product>osu!(Ez2lazer)</Product>
<ApplicationIcon>lazer.ico</ApplicationIcon>
<Version>0.0.0</Version>
<FileVersion>0.0.0</FileVersion>
@@ -15,6 +15,11 @@
<PropertyGroup>
<StartupObject>osu.Desktop.Program</StartupObject>
</PropertyGroup>
<!-- <PropertyGroup Condition="'$(Configuration)'=='Release' OR '$(Configuration)'=='Debug'">-->
<PropertyGroup>
<PublishDir>F:\MUG OSU\Ez2Lazer_Publish\$(Configuration)</PublishDir>
<OutputPath>bin\$(Configuration)\</OutputPath>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Tournament\osu.Game.Tournament.csproj" />
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />

View File

@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -219,5 +220,40 @@ namespace osu.Game.Rulesets.Catch.Edit
distanceSnapGrid.StartTime = sourceHitObject.GetEndTime();
distanceSnapGrid.StartX = sourceHitObject.EffectiveX;
}
#region Clipboard handling
public override string ConvertSelectionToString()
=> string.Join(',', EditorBeatmap.SelectedHitObjects.Cast<CatchHitObject>().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString()));
// 1,2,3,4 ...
private static readonly Regex selection_regex = new Regex(@"^\d+(,\d+)*$", RegexOptions.Compiled);
public override void SelectFromTimestamp(double timestamp, string objectDescription)
{
if (!selection_regex.IsMatch(objectDescription))
return;
List<CatchHitObject> remainingHitObjects = EditorBeatmap.HitObjects.Cast<CatchHitObject>().Where(h => h.StartTime >= timestamp).ToList();
string[] splitDescription = objectDescription.Split(',');
for (int i = 0; i < splitDescription.Length; i++)
{
if (!int.TryParse(splitDescription[i], out int combo) || combo < 1)
continue;
CatchHitObject? current = remainingHitObjects.FirstOrDefault(h => h.IndexInCurrentCombo + 1 == combo);
if (current == null)
continue;
EditorBeatmap.SelectedHitObjects.Add(current);
if (i < splitDescription.Length - 1)
remainingHitObjects = remainingHitObjects.Where(h => h != current && h.StartTime >= current.StartTime).ToList();
}
}
#endregion
}
}

View File

@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{
get
{
if (UserAdjustedSettingsCount != 1)
if (!IsExactlyOneSettingChanged(CircleSize, ApproachRate, OverallDifficulty, DrainRate))
return string.Empty;
if (!CircleSize.IsDefault) return format("CS", CircleSize);

View File

@@ -19,7 +19,6 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.Tests
{
[Ignore("These tests are expected to fail until an acceptable solution for various replay playback issues concerning rounding of replay frame times & hit windows is found.")]
public partial class TestSceneLegacyReplayPlayback : LegacyReplayPlaybackTestScene
{
protected override Ruleset CreateRuleset() => new ManiaRuleset();
@@ -72,13 +71,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 5f, -137d, HitResult.Miss },
new object[] { 5f, -138d, HitResult.Miss },
new object[] { 5f, 111d, HitResult.Ok },
new object[] { 5f, 112d, HitResult.Miss },
new object[] { 5f, 113d, HitResult.Miss },
new object[] { 5f, 114d, HitResult.Miss },
new object[] { 5f, 135d, HitResult.Miss },
new object[] { 5f, 136d, HitResult.Miss },
new object[] { 5f, 137d, HitResult.Miss },
new object[] { 5f, 138d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 5f, 112d, HitResult.Miss },
// new object[] { 5f, 113d, HitResult.Miss },
// new object[] { 5f, 114d, HitResult.Miss },
// new object[] { 5f, 135d, HitResult.Miss },
// new object[] { 5f, 136d, HitResult.Miss },
// new object[] { 5f, 137d, HitResult.Miss },
// new object[] { 5f, 138d, HitResult.Miss },
// OD = 9.3 test cases.
// PERFECT hit window is [ -14ms, 14ms]
@@ -99,13 +99,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 9.3f, 70d, HitResult.Ok },
new object[] { 9.3f, 71d, HitResult.Ok },
new object[] { 9.3f, 98d, HitResult.Ok },
new object[] { 9.3f, 99d, HitResult.Miss },
new object[] { 9.3f, 100d, HitResult.Miss },
new object[] { 9.3f, 101d, HitResult.Miss },
new object[] { 9.3f, 122d, HitResult.Miss },
new object[] { 9.3f, 123d, HitResult.Miss },
new object[] { 9.3f, 124d, HitResult.Miss },
new object[] { 9.3f, 125d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 9.3f, 99d, HitResult.Miss },
// new object[] { 9.3f, 100d, HitResult.Miss },
// new object[] { 9.3f, 101d, HitResult.Miss },
// new object[] { 9.3f, 122d, HitResult.Miss },
// new object[] { 9.3f, 123d, HitResult.Miss },
// new object[] { 9.3f, 124d, HitResult.Miss },
// new object[] { 9.3f, 125d, HitResult.Miss },
new object[] { 9.3f, -98d, HitResult.Ok },
new object[] { 9.3f, -99d, HitResult.Ok },
new object[] { 9.3f, -100d, HitResult.Meh },
@@ -145,13 +146,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 5f, -137d, HitResult.Miss },
new object[] { 5f, -138d, HitResult.Miss },
new object[] { 5f, 111d, HitResult.Ok },
new object[] { 5f, 112d, HitResult.Miss },
new object[] { 5f, 113d, HitResult.Miss },
new object[] { 5f, 114d, HitResult.Miss },
new object[] { 5f, 135d, HitResult.Miss },
new object[] { 5f, 136d, HitResult.Miss },
new object[] { 5f, 137d, HitResult.Miss },
new object[] { 5f, 138d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 5f, 112d, HitResult.Miss },
// new object[] { 5f, 113d, HitResult.Miss },
// new object[] { 5f, 114d, HitResult.Miss },
// new object[] { 5f, 135d, HitResult.Miss },
// new object[] { 5f, 136d, HitResult.Miss },
// new object[] { 5f, 137d, HitResult.Miss },
// new object[] { 5f, 138d, HitResult.Miss },
// OD = 9.3 test cases.
// PERFECT hit window is [ -16ms, 16ms]
@@ -172,13 +174,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 9.3f, 70d, HitResult.Ok },
new object[] { 9.3f, 71d, HitResult.Ok },
new object[] { 9.3f, 98d, HitResult.Ok },
new object[] { 9.3f, 99d, HitResult.Miss },
new object[] { 9.3f, 100d, HitResult.Miss },
new object[] { 9.3f, 101d, HitResult.Miss },
new object[] { 9.3f, 122d, HitResult.Miss },
new object[] { 9.3f, 123d, HitResult.Miss },
new object[] { 9.3f, 124d, HitResult.Miss },
new object[] { 9.3f, 125d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 9.3f, 99d, HitResult.Miss },
// new object[] { 9.3f, 100d, HitResult.Miss },
// new object[] { 9.3f, 101d, HitResult.Miss },
// new object[] { 9.3f, 122d, HitResult.Miss },
// new object[] { 9.3f, 123d, HitResult.Miss },
// new object[] { 9.3f, 124d, HitResult.Miss },
// new object[] { 9.3f, 125d, HitResult.Miss },
new object[] { 9.3f, -98d, HitResult.Ok },
new object[] { 9.3f, -99d, HitResult.Ok },
new object[] { 9.3f, -100d, HitResult.Meh },
@@ -207,13 +210,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 3.1f, 88d, HitResult.Ok },
new object[] { 3.1f, 89d, HitResult.Ok },
new object[] { 3.1f, 116d, HitResult.Ok },
new object[] { 3.1f, 117d, HitResult.Miss },
new object[] { 3.1f, 118d, HitResult.Miss },
new object[] { 3.1f, 119d, HitResult.Miss },
new object[] { 3.1f, 140d, HitResult.Miss },
new object[] { 3.1f, 141d, HitResult.Miss },
new object[] { 3.1f, 142d, HitResult.Miss },
new object[] { 3.1f, 143d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 3.1f, 117d, HitResult.Miss },
// new object[] { 3.1f, 118d, HitResult.Miss },
// new object[] { 3.1f, 119d, HitResult.Miss },
// new object[] { 3.1f, 140d, HitResult.Miss },
// new object[] { 3.1f, 141d, HitResult.Miss },
// new object[] { 3.1f, 142d, HitResult.Miss },
// new object[] { 3.1f, 143d, HitResult.Miss },
new object[] { 3.1f, -116d, HitResult.Ok },
new object[] { 3.1f, -117d, HitResult.Ok },
new object[] { 3.1f, -118d, HitResult.Meh },
@@ -253,13 +257,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 5f, -122d, HitResult.Miss },
new object[] { 5f, -123d, HitResult.Miss },
new object[] { 5f, 96d, HitResult.Ok },
new object[] { 5f, 97d, HitResult.Miss },
new object[] { 5f, 98d, HitResult.Miss },
new object[] { 5f, 99d, HitResult.Miss },
new object[] { 5f, 120d, HitResult.Miss },
new object[] { 5f, 121d, HitResult.Miss },
new object[] { 5f, 122d, HitResult.Miss },
new object[] { 5f, 123d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 5f, 97d, HitResult.Miss },
// new object[] { 5f, 98d, HitResult.Miss },
// new object[] { 5f, 99d, HitResult.Miss },
// new object[] { 5f, 120d, HitResult.Miss },
// new object[] { 5f, 121d, HitResult.Miss },
// new object[] { 5f, 122d, HitResult.Miss },
// new object[] { 5f, 123d, HitResult.Miss },
// OD = 3.1 test cases.
// PERFECT hit window is [ -16ms, 16ms]
@@ -280,13 +285,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 3.1f, 78d, HitResult.Ok },
new object[] { 3.1f, 79d, HitResult.Ok },
new object[] { 3.1f, 96d, HitResult.Ok },
new object[] { 3.1f, 97d, HitResult.Miss },
new object[] { 3.1f, 98d, HitResult.Miss },
new object[] { 3.1f, 99d, HitResult.Miss },
new object[] { 3.1f, 120d, HitResult.Miss },
new object[] { 3.1f, 121d, HitResult.Miss },
new object[] { 3.1f, 122d, HitResult.Miss },
new object[] { 3.1f, 123d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 3.1f, 97d, HitResult.Miss },
// new object[] { 3.1f, 98d, HitResult.Miss },
// new object[] { 3.1f, 99d, HitResult.Miss },
// new object[] { 3.1f, 120d, HitResult.Miss },
// new object[] { 3.1f, 121d, HitResult.Miss },
// new object[] { 3.1f, 122d, HitResult.Miss },
// new object[] { 3.1f, 123d, HitResult.Miss },
new object[] { 3.1f, -96d, HitResult.Ok },
new object[] { 3.1f, -97d, HitResult.Ok },
new object[] { 3.1f, -98d, HitResult.Meh },
@@ -327,13 +333,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 5f, -98d, HitResult.Miss },
new object[] { 5f, -99d, HitResult.Miss },
new object[] { 5f, 79d, HitResult.Ok },
new object[] { 5f, 80d, HitResult.Miss },
new object[] { 5f, 81d, HitResult.Miss },
new object[] { 5f, 82d, HitResult.Miss },
new object[] { 5f, 96d, HitResult.Miss },
new object[] { 5f, 97d, HitResult.Miss },
new object[] { 5f, 98d, HitResult.Miss },
new object[] { 5f, 99d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 5f, 80d, HitResult.Miss },
// new object[] { 5f, 81d, HitResult.Miss },
// new object[] { 5f, 82d, HitResult.Miss },
// new object[] { 5f, 96d, HitResult.Miss },
// new object[] { 5f, 97d, HitResult.Miss },
// new object[] { 5f, 98d, HitResult.Miss },
// new object[] { 5f, 99d, HitResult.Miss },
// OD = 9.3 test cases.
// This leads to "effective" OD of 13.02.
@@ -356,13 +363,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 9.3f, 50d, HitResult.Ok },
new object[] { 9.3f, 51d, HitResult.Ok },
new object[] { 9.3f, 69d, HitResult.Ok },
new object[] { 9.3f, 70d, HitResult.Miss },
new object[] { 9.3f, 71d, HitResult.Miss },
new object[] { 9.3f, 72d, HitResult.Miss },
new object[] { 9.3f, 86d, HitResult.Miss },
new object[] { 9.3f, 87d, HitResult.Miss },
new object[] { 9.3f, 88d, HitResult.Miss },
new object[] { 9.3f, 89d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 9.3f, 70d, HitResult.Miss },
// new object[] { 9.3f, 71d, HitResult.Miss },
// new object[] { 9.3f, 72d, HitResult.Miss },
// new object[] { 9.3f, 86d, HitResult.Miss },
// new object[] { 9.3f, 87d, HitResult.Miss },
// new object[] { 9.3f, 88d, HitResult.Miss },
// new object[] { 9.3f, 89d, HitResult.Miss },
new object[] { 9.3f, -69d, HitResult.Ok },
new object[] { 9.3f, -70d, HitResult.Ok },
new object[] { 9.3f, -71d, HitResult.Meh },
@@ -402,13 +410,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 5f, -191d, HitResult.Miss },
new object[] { 5f, -192d, HitResult.Miss },
new object[] { 5f, 155d, HitResult.Ok },
new object[] { 5f, 156d, HitResult.Miss },
new object[] { 5f, 157d, HitResult.Miss },
new object[] { 5f, 158d, HitResult.Miss },
new object[] { 5f, 189d, HitResult.Miss },
new object[] { 5f, 190d, HitResult.Miss },
new object[] { 5f, 191d, HitResult.Miss },
new object[] { 5f, 192d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 5f, 156d, HitResult.Miss },
// new object[] { 5f, 157d, HitResult.Miss },
// new object[] { 5f, 158d, HitResult.Miss },
// new object[] { 5f, 189d, HitResult.Miss },
// new object[] { 5f, 190d, HitResult.Miss },
// new object[] { 5f, 191d, HitResult.Miss },
// new object[] { 5f, 192d, HitResult.Miss },
};
private static readonly object[][] score_v1_non_convert_double_time_test_cases =
@@ -440,13 +449,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 5f, -205d, HitResult.Miss },
new object[] { 5f, -206d, HitResult.Miss },
new object[] { 5f, 167d, HitResult.Ok },
new object[] { 5f, 168d, HitResult.Miss },
new object[] { 5f, 169d, HitResult.Miss },
new object[] { 5f, 170d, HitResult.Miss },
new object[] { 5f, 203d, HitResult.Miss },
new object[] { 5f, 204d, HitResult.Miss },
new object[] { 5f, 205d, HitResult.Miss },
new object[] { 5f, 206d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 5f, 168d, HitResult.Miss },
// new object[] { 5f, 169d, HitResult.Miss },
// new object[] { 5f, 170d, HitResult.Miss },
// new object[] { 5f, 203d, HitResult.Miss },
// new object[] { 5f, 204d, HitResult.Miss },
// new object[] { 5f, 205d, HitResult.Miss },
// new object[] { 5f, 206d, HitResult.Miss },
};
private static readonly object[][] score_v1_non_convert_half_time_test_cases =
@@ -478,13 +488,14 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { 5f, -103d, HitResult.Miss },
new object[] { 5f, -104d, HitResult.Miss },
new object[] { 5f, 83d, HitResult.Ok },
new object[] { 5f, 84d, HitResult.Miss },
new object[] { 5f, 85d, HitResult.Miss },
new object[] { 5f, 86d, HitResult.Miss },
new object[] { 5f, 101d, HitResult.Miss },
new object[] { 5f, 102d, HitResult.Miss },
new object[] { 5f, 103d, HitResult.Miss },
new object[] { 5f, 104d, HitResult.Miss },
// coverage of broken "can't hit meh late" behaviour, which is intentionally not being reproduced
// new object[] { 5f, 84d, HitResult.Miss },
// new object[] { 5f, 85d, HitResult.Miss },
// new object[] { 5f, 86d, HitResult.Miss },
// new object[] { 5f, 101d, HitResult.Miss },
// new object[] { 5f, 102d, HitResult.Miss },
// new object[] { 5f, 103d, HitResult.Miss },
// new object[] { 5f, 104d, HitResult.Miss },
};
private const double note_time = 300;
@@ -517,6 +528,7 @@ namespace osu.Game.Rulesets.Mania.Tests
RunTest($@"SV2 single note @ OD{overallDifficulty}", beatmap, $@"SV2 {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
}
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
[TestCaseSource(nameof(score_v1_non_convert_test_cases))]
public void TestHitWindowTreatmentWithScoreV1NonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
{
@@ -544,6 +556,7 @@ namespace osu.Game.Rulesets.Mania.Tests
RunTest($@"SV1 single note @ OD{overallDifficulty}", beatmap, $@"SV1 {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
}
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
[TestCaseSource(nameof(score_v1_convert_test_cases))]
public void TestHitWindowTreatmentWithScoreV1Convert(float overallDifficulty, double hitOffset, HitResult expectedResult)
{
@@ -572,6 +585,7 @@ namespace osu.Game.Rulesets.Mania.Tests
RunTest($@"SV1 convert single note @ OD{overallDifficulty}", beatmap, $@"SV1 convert {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
}
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
[TestCaseSource(nameof(score_v1_non_convert_hard_rock_test_cases))]
public void TestHitWindowTreatmentWithScoreV1AndHardRockNonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
{
@@ -600,6 +614,7 @@ namespace osu.Game.Rulesets.Mania.Tests
RunTest($@"SV1+HR single note @ OD{overallDifficulty}", beatmap, $@"SV1+HR {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
}
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
[TestCaseSource(nameof(score_v1_non_convert_easy_test_cases))]
public void TestHitWindowTreatmentWithScoreV1AndEasyNonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
{
@@ -628,6 +643,7 @@ namespace osu.Game.Rulesets.Mania.Tests
RunTest($@"SV1+EZ single note @ OD{overallDifficulty}", beatmap, $@"SV1+EZ {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
}
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
[TestCaseSource(nameof(score_v1_non_convert_double_time_test_cases))]
public void TestHitWindowTreatmentWithScoreV1AndDoubleTimeNonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
{
@@ -656,6 +672,7 @@ namespace osu.Game.Rulesets.Mania.Tests
RunTest($@"SV1+DT single note @ OD{overallDifficulty}", beatmap, $@"SV1+DT {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
}
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
[TestCaseSource(nameof(score_v1_non_convert_half_time_test_cases))]
public void TestHitWindowTreatmentWithScoreV1AndHalfTimeNonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
{

View File

@@ -12,7 +12,6 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
[Ignore("These tests are expected to fail until an acceptable solution for various replay playback issues concerning rounding of replay frame times & hit windows is found.")]
public partial class TestSceneReplayStability : ReplayStabilityTestScene
{
private static readonly object[][] test_cases =
@@ -22,87 +21,79 @@ namespace osu.Game.Rulesets.Mania.Tests
// while round brackets `()` represent *open* or *exclusive* bounds.
// OD = 5 test cases.
// PERFECT hit window is [ -19.4ms, 19.4ms]
// GREAT hit window is [ -49.0ms, 49.0ms]
// GOOD hit window is [ -82.0ms, 82.0ms]
// OK hit window is [-112.0ms, 112.0ms]
// MEH hit window is [-136.0ms, 136.0ms]
// MISS hit window is [-173.0ms, 173.0ms]
// PERFECT hit window is [ -19.5ms, 19.5ms]
// GREAT hit window is [ -49.5ms, 49.5ms]
// GOOD hit window is [ -82.5ms, 82.5ms]
// OK hit window is [-112.5ms, 112.5ms]
// MEH hit window is [-136.5ms, 136.5ms]
// MISS hit window is [-173.5ms, 173.5ms]
new object[] { 5f, -19d, HitResult.Perfect },
new object[] { 5f, -19.2d, HitResult.Perfect },
new object[] { 5f, -19.38d, HitResult.Perfect },
// new object[] { 5f, -19.4d, HitResult.Perfect }, <- in theory this should work, in practice it does not (fails even before encode & rounding due to floating point precision issues)
new object[] { 5f, -19.44d, HitResult.Great },
new object[] { 5f, -19.7d, HitResult.Great },
new object[] { 5f, -20d, HitResult.Great },
new object[] { 5f, -48d, HitResult.Great },
new object[] { 5f, -48.4d, HitResult.Great },
new object[] { 5f, -48.7d, HitResult.Great },
new object[] { 5f, -49d, HitResult.Great },
new object[] { 5f, -49.2d, HitResult.Good },
new object[] { 5f, -49.2d, HitResult.Great },
new object[] { 5f, -49.7d, HitResult.Good },
new object[] { 5f, -50d, HitResult.Good },
new object[] { 5f, -81d, HitResult.Good },
new object[] { 5f, -81.2d, HitResult.Good },
new object[] { 5f, -81.7d, HitResult.Good },
new object[] { 5f, -82d, HitResult.Good },
new object[] { 5f, -82.2d, HitResult.Ok },
new object[] { 5f, -82.2d, HitResult.Good },
new object[] { 5f, -82.7d, HitResult.Ok },
new object[] { 5f, -83d, HitResult.Ok },
new object[] { 5f, -111d, HitResult.Ok },
new object[] { 5f, -111.2d, HitResult.Ok },
new object[] { 5f, -111.7d, HitResult.Ok },
new object[] { 5f, -112d, HitResult.Ok },
new object[] { 5f, -112.2d, HitResult.Meh },
new object[] { 5f, -112.2d, HitResult.Ok },
new object[] { 5f, -112.7d, HitResult.Meh },
new object[] { 5f, -113d, HitResult.Meh },
new object[] { 5f, -135d, HitResult.Meh },
new object[] { 5f, -135.2d, HitResult.Meh },
new object[] { 5f, -135.8d, HitResult.Meh },
new object[] { 5f, -136d, HitResult.Meh },
new object[] { 5f, -136.2d, HitResult.Miss },
new object[] { 5f, -136.2d, HitResult.Meh },
new object[] { 5f, -136.7d, HitResult.Miss },
new object[] { 5f, -137d, HitResult.Miss },
// OD = 9.3 test cases.
// PERFECT hit window is [ -14.67ms, 14.67ms]
// GREAT hit window is [ -36.10ms, 36.10ms]
// GOOD hit window is [ -69.10ms, 69.10ms]
// OK hit window is [ -99.10ms, 99.10ms]
// MEH hit window is [-123.10ms, 123.10ms]
// MISS hit window is [-160.10ms, 160.10ms]
// PERFECT hit window is [ -14.5ms, 14.5ms]
// GREAT hit window is [ -36.5ms, 36.5ms]
// GOOD hit window is [ -69.5ms, 69.5ms]
// OK hit window is [ -99.5ms, 99.5ms]
// MEH hit window is [-123.5ms, 123.5ms]
// MISS hit window is [-160.5ms, 160.5ms]
new object[] { 9.3f, 14d, HitResult.Perfect },
new object[] { 9.3f, 14.2d, HitResult.Perfect },
new object[] { 9.3f, 14.6d, HitResult.Perfect },
// new object[] { 9.3f, 14.67d, HitResult.Perfect }, <- in theory this should work, in practice it does not (fails even before encode & rounding due to floating point precision issues)
new object[] { 9.3f, 14.7d, HitResult.Great },
new object[] { 9.3f, 15d, HitResult.Great },
new object[] { 9.3f, 35d, HitResult.Great },
new object[] { 9.3f, 35.3d, HitResult.Great },
new object[] { 9.3f, 35.8d, HitResult.Great },
new object[] { 9.3f, 36.05d, HitResult.Great },
new object[] { 9.3f, 36.3d, HitResult.Good },
new object[] { 9.3f, 36.3d, HitResult.Great },
new object[] { 9.3f, 36.7d, HitResult.Good },
new object[] { 9.3f, 37d, HitResult.Good },
new object[] { 9.3f, 68d, HitResult.Good },
new object[] { 9.3f, 68.4d, HitResult.Good },
new object[] { 9.3f, 68.9d, HitResult.Good },
new object[] { 9.3f, 69.07d, HitResult.Good },
new object[] { 9.3f, 69.25d, HitResult.Ok },
new object[] { 9.3f, 69.25d, HitResult.Good },
new object[] { 9.3f, 69.85d, HitResult.Ok },
new object[] { 9.3f, 70d, HitResult.Ok },
new object[] { 9.3f, 98d, HitResult.Ok },
new object[] { 9.3f, 98.3d, HitResult.Ok },
new object[] { 9.3f, 98.6d, HitResult.Ok },
new object[] { 9.3f, 99d, HitResult.Ok },
new object[] { 9.3f, 99.3d, HitResult.Meh },
new object[] { 9.3f, 99.3d, HitResult.Ok },
new object[] { 9.3f, 99.7d, HitResult.Meh },
new object[] { 9.3f, 100d, HitResult.Meh },
new object[] { 9.3f, 122d, HitResult.Meh },
new object[] { 9.3f, 122.34d, HitResult.Meh },
new object[] { 9.3f, 122.57d, HitResult.Meh },
new object[] { 9.3f, 123.04d, HitResult.Meh },
new object[] { 9.3f, 123.45d, HitResult.Miss },
new object[] { 9.3f, 123.45d, HitResult.Meh },
new object[] { 9.3f, 123.95d, HitResult.Miss },
new object[] { 9.3f, 124d, HitResult.Miss },
};
@@ -110,7 +101,7 @@ namespace osu.Game.Rulesets.Mania.Tests
[TestCaseSource(nameof(test_cases))]
public void TestHitWindowStability(float overallDifficulty, double hitOffset, HitResult expectedResult)
{
const double note_time = 100;
const double note_time = 300;
var beatmap = new ManiaBeatmap(new StageDefinition(1))
{

View File

@@ -1,12 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.ComponentModel;
using osu.Framework.Configuration.Tracking;
using osu.Game.Configuration;
using osu.Game.Localisation;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Configuration
@@ -24,16 +23,14 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
SetDefault(ManiaRulesetSetting.ColumnWidth, 46, 9, 90, 1.0);
SetDefault(ManiaRulesetSetting.SpecialFactor, 1, 0.1, 4, 0.1);
// SetDefault(ManiaRulesetSetting.HitMode, MUGHitMode.EZ2AC);
SetDefault(ManiaRulesetSetting.ScrollSpeed, 200, 1.0, 401.0, current_scroll_speed_precision);
SetDefault(ManiaRulesetSetting.ScrollBaseSpeed, 500, 100, 1000, 1.0);
SetDefault(ManiaRulesetSetting.ScrollTimePerSpeed, 5, 1.0, 40, 1.0);
SetDefault(ManiaRulesetSetting.ScrollStyle, EzManiaScrollingStyle.ScrollTimeStyleFixed);
SetDefault(ManiaRulesetSetting.ScrollPerKeyMode, false);
SetDefault(ManiaRulesetSetting.PerspectiveAngle, 90.0f, 30.0f, 90.0f);
SetDefault(ManiaRulesetSetting.ScrollSpeed, 200, 1.0, 401.0, current_scroll_speed_precision);
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false);
SetDefault(ManiaRulesetSetting.MobileLayout, ManiaMobileLayout.Portrait);
@@ -56,34 +53,20 @@ namespace osu.Game.Rulesets.Mania.Configuration
public enum ManiaRulesetSetting
{
[Obsolete("Use ScrollSpeed instead.")] // Can be removed 2023-11-30
ScrollStyle,
ScrollTime,
ScrollBaseSpeed,
ScrollTimePerSpeed,
ScrollStyle,
// HitMode,
// HitMode,
//暂时无用
PerspectiveAngle,
ColumnWidth,
SpecialFactor,
ScrollPerKeyMode,
//官方设置
ScrollSpeed,
ScrollDirection,
TimingBasedNoteColouring,
MobileLayout,
}
public enum EzManiaScrollingStyle
{
// [LocalisableDescription(typeof(RulesetSettingsStrings), nameof(RulesetSettingsStrings.ScrollingDirectionUp))]
[Description("40速 通配速度风格(不可用)")]
ScrollSpeedStyle,
// [LocalisableDescription(typeof(RulesetSettingsStrings), nameof(RulesetSettingsStrings.ScrollingDirectionDown))]
[Description("ms值 恒定速度")]
ScrollTimeStyle,
[Description("ms值 恒定时间")]
ScrollTimeStyleFixed,
}
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.ComponentModel;
namespace osu.Game.Rulesets.Mania.LAsEZMania
{
public enum EzManiaScrollingStyle
{
// [LocalisableDescription(typeof(RulesetSettingsStrings), nameof(RulesetSettingsStrings.ScrollingDirectionUp))]
[Description("40速 通配速度风格(不可用)")]
ScrollSpeedStyle,
// [LocalisableDescription(typeof(RulesetSettingsStrings), nameof(RulesetSettingsStrings.ScrollingDirectionDown))]
[Description("ms值 相对默认判定线")]
ScrollTimeStyle,
[Description("ms值 相对屏幕底部")]
ScrollTimeStyleFixed,
}
}

View File

@@ -11,6 +11,7 @@ using osu.Framework.Bindables;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Overlays.Settings.Sections.Gameplay;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects;

View File

@@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
using osu.Game.Screens.Play.HUD;
using osuTK;
@@ -64,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.LAsEZMania
float width = (float)columnWidth.Value;
int index = KeyFlow.IndexOf(counter);
if (StageDefinition.EzIsSpecialColumn(index))
if (EzColumnTypeManager.GetColumnType(StageDefinition.Columns, index) == "S1")
width *= (float)specialFactor.Value;
counter.Width = width;

View File

@@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Framework.Localisation;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Configuration;
@@ -86,13 +87,19 @@ namespace osu.Game.Rulesets.Mania
case Ez2Skin:
if (GlobalConfigStore.EzConfig == null)
throw new ArgumentNullException(nameof(GlobalConfigStore.Config));
{
Logger.Log("!EGlobalConfigStore.EzConfig", LoggingTarget.Runtime, LogLevel.Important);
break;
}
return new ManiaEz2SkinTransformer(skin, beatmap, GlobalConfigStore.EzConfig);
case EzStyleProSkin:
if (GlobalConfigStore.EzConfig == null)
throw new ArgumentNullException(nameof(GlobalConfigStore.Config));
{
Logger.Log("!GlobalConfigStore.EzConfig", LoggingTarget.Runtime, LogLevel.Important);
break;
}
return new ManiaEzStyleProSkinTransformer(skin, beatmap, GlobalConfigStore.EzConfig);

View File

@@ -9,6 +9,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania
@@ -45,17 +46,19 @@ namespace osu.Game.Rulesets.Mania
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollSpeed),
KeyboardStep = 1,
},
new SettingsSlider<double, ManiaScrollBaseSpeedSlider>
new SettingsSlider<double, ManiaScrollBaseSlider>
{
LabelText = "Scroll Base MS (when 200 Speed)",
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollBaseSpeed),
KeyboardStep = 1,
Keywords = new[] { "base" }
},
new SettingsSlider<double, ManiaScrollTimePerSpeedSlider>
new SettingsSlider<double, ManiaScrollMsPerSpeedSlider>
{
LabelText = "MS / Scroll Speed",
LabelText = "MS / Speed",
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollTimePerSpeed),
KeyboardStep = 1,
Keywords = new[] { "mps" }
},
new SettingsCheckbox
{
@@ -85,20 +88,63 @@ namespace osu.Game.Rulesets.Mania
this.config = config;
}
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(
(int)DrawableManiaRuleset.ComputeScrollTime(Current.Value, config.Get<double>(ManiaRulesetSetting.ScrollBaseSpeed), config.Get<double>(ManiaRulesetSetting.ScrollTimePerSpeed)),
Current.Value
);
public override LocalisableString TooltipText
{
get
{
double baseSpeed = config.Get<double>(ManiaRulesetSetting.ScrollBaseSpeed);
double timePerSpeed = config.Get<double>(ManiaRulesetSetting.ScrollTimePerSpeed);
int computedTime = (int)DrawableManiaRuleset.ComputeScrollTime(Current.Value, baseSpeed, timePerSpeed);
LocalisableString speedInfo = RulesetSettingsStrings.ScrollSpeedTooltip(computedTime, Current.Value);
return $"{baseSpeed}base - ( {Current.Value} - 200) * {timePerSpeed}mps\n = {speedInfo}";
}
}
}
private partial class ManiaScrollBaseSpeedSlider : RoundedSliderBar<double>
private partial class ManiaScrollBaseSlider : RoundedSliderBar<double>
{
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(
(int)Current.Value, 200);
private ManiaRulesetConfigManager config = null!;
[BackgroundDependencyLoader]
private void load(ManiaRulesetConfigManager config)
{
this.config = config;
}
public override LocalisableString TooltipText
{
get
{
double speed = config.Get<double>(ManiaRulesetSetting.ScrollSpeed);
double timePerSpeed = config.Get<double>(ManiaRulesetSetting.ScrollTimePerSpeed);
int computedTime = (int)DrawableManiaRuleset.ComputeScrollTime(speed, Current.Value, timePerSpeed);
LocalisableString speedInfo = RulesetSettingsStrings.ScrollSpeedTooltip(computedTime, speed);
return $"{Current.Value}base - ( {speed} - 200) * {timePerSpeed}mps\n = {speedInfo}";
}
}
}
private partial class ManiaScrollTimePerSpeedSlider : RoundedSliderBar<double>
private partial class ManiaScrollMsPerSpeedSlider : RoundedSliderBar<double>
{
private ManiaRulesetConfigManager config = null!;
[BackgroundDependencyLoader]
private void load(ManiaRulesetConfigManager config)
{
this.config = config;
}
public override LocalisableString TooltipText
{
get
{
double speed = config.Get<double>(ManiaRulesetSetting.ScrollSpeed);
double baseSpeed = config.Get<double>(ManiaRulesetSetting.ScrollBaseSpeed);
int computedTime = (int)DrawableManiaRuleset.ComputeScrollTime(speed, baseSpeed, Current.Value);
LocalisableString speedInfo = RulesetSettingsStrings.ScrollSpeedTooltip(computedTime, speed);
return $"{baseSpeed}base - ( {speed} - 200) * {Current.Value}mps\n = {speedInfo}";
}
}
}
}
}

View File

@@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Mania.Mods.LAsMods
hitWindows.SetDifficulty(difficulty.OverallDifficulty);
}
public override void ResetHitWindows()
public override void ResetSettingsToDefaults()
{
hitWindows.ResetHitWindows();
}

View File

@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Mania.Mods.YuLiangSSSMods
HitWindows.SetDifficulty(difficulty.OverallDifficulty);
}
public override void ResetHitWindows()
public override void ResetSettingsToDefaults()
{
HitWindows.ResetHitWindows();
}

View File

@@ -55,12 +55,12 @@ namespace osu.Game.Rulesets.Mania.Scoring
public override void SetDifficulty(double difficulty)
{
perfect = IBeatmapDifficultyInfo.DifficultyRange(difficulty, perfect_window_range) * multiplier;
great = IBeatmapDifficultyInfo.DifficultyRange(difficulty, great_window_range) * multiplier;
good = IBeatmapDifficultyInfo.DifficultyRange(difficulty, good_window_range) * multiplier;
ok = IBeatmapDifficultyInfo.DifficultyRange(difficulty, ok_window_range) * multiplier;
meh = IBeatmapDifficultyInfo.DifficultyRange(difficulty, meh_window_range) * multiplier;
miss = IBeatmapDifficultyInfo.DifficultyRange(difficulty, miss_window_range) * multiplier;
perfect = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, perfect_window_range) * multiplier) + 0.5;
great = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, great_window_range) * multiplier) + 0.5;
good = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, good_window_range) * multiplier) + 0.5;
ok = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, ok_window_range) * multiplier) + 0.5;
meh = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, meh_window_range) * multiplier) + 0.5;
miss = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, miss_window_range) * multiplier) + 0.5;
}
public override double WindowFor(HitResult result)

View File

@@ -13,7 +13,6 @@ using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens;
using osuTK.Graphics;
@@ -22,14 +21,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
public partial class Ez2ColumnBackground : CompositeDrawable, IKeyBindingHandler<ManiaAction>
{
private readonly Bindable<float> overlayHeight = new Bindable<float>();
private readonly Bindable<double> hitPosition = new Bindable<double>();
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Bindable<double> hitPosition = new Bindable<double>();
private Color4 brightColour;
private Color4 dimColour;
private Box background = null!;
private Box backgroundOverlay = null!;
private Box? separator;
private Box separator = new Box();
private Bindable<Color4> accentColour = null!;
[Resolved]
@@ -43,12 +41,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
public Ez2ColumnBackground()
{
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
RelativeSizeAxes = Axes.Both;
Masking = true;
// Masking = true;
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
private void load()
{
InternalChild = new Container
{
@@ -99,46 +99,27 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
brightColour = colour.NewValue.Opacity(0.6f);
dimColour = colour.NewValue.Opacity(0);
}, true);
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
}
protected override void LoadComplete()
{
base.LoadComplete();
ezSkinConfig.BindWith(EzSkinSetting.HitPosition, hitPosition);
hitPosition = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
hitPosition.BindValueChanged(_ => OnConfigChanged(), true);
OnConfigChanged();
}
private void OnConfigChanged()
{
if (separator != null)
{
separator.Height = DrawHeight - (float)hitPosition.Value;
separator.Height = DrawHeight - (float)hitPosition.Value;
if (drawSeparator(column.Index, stageDefinition))
{
separator.Alpha = 0.2f;
}
else
{
separator.Alpha = 0;
}
}
}
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
if (direction.NewValue == ScrollingDirection.Up)
if (drawSeparator(column.Index, stageDefinition))
{
backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.TopLeft;
separator.Alpha = 0.2f;
}
else
{
backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.BottomLeft;
separator.Alpha = 0;
}
}
@@ -150,9 +131,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
brightColour = noteColour.Opacity(0.9f);
dimColour = noteColour.Opacity(0);
backgroundOverlay.Colour = direction.Value == ScrollingDirection.Up
? ColourInfo.GradientVertical(brightColour, dimColour)
: ColourInfo.GradientVertical(dimColour, brightColour);
backgroundOverlay.Colour = ColourInfo.GradientVertical(dimColour, brightColour);
overlayHeight.Value = 0.5f;

View File

@@ -15,7 +15,6 @@ using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens;
@@ -46,9 +45,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
[Resolved]
private IGameplayClock gameplayClock { get; set; } = null!;
[Resolved]
private StageDefinition stageDefinition { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
@@ -60,9 +56,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{
if (stageDefinition.Columns == 14 && column.Index == 13)
return;
hitPosition.Value = (float)ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition).Value;
InternalChild = directionContainer = new Container

View File

@@ -15,16 +15,15 @@ using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.Skinning.Ez2;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
namespace osu.Game.Rulesets.Mania.Skinning.Ez2
{
public partial class Ez2KeyAreaPro : CompositeDrawable, IKeyBindingHandler<ManiaAction>
public partial class Ez2KeyAreaPlus : CompositeDrawable, IKeyBindingHandler<ManiaAction>
{
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
@@ -46,7 +45,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
[Resolved]
private IGameplayClock gameplayClock { get; set; } = null!;
public Ez2KeyAreaPro()
public Ez2KeyAreaPlus()
{
RelativeSizeAxes = Axes.Both;
}

View File

@@ -9,6 +9,7 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.Skinning.Ez2HUD;
using osu.Game.Rulesets.Mania.Skinning.EzStylePro;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
@@ -183,7 +184,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
// if (Skin is Ez2Skin && resultComponent.Component >= HitResult.Perfect)
// return Drawable.Empty();
return new Ez2ColumnBackground();
return new EzColumnBackground();
case ManiaSkinComponents.KeyArea:
return new Ez2KeyArea();
@@ -224,7 +225,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
bool isSpecialColumn = stage.EzIsSpecialColumn(columnIndex);
float width = (float)columnWidthBindable.Value * (isSpecialColumn ? (float)specialFactorBindable.Value : 1f);
float hitPositionValue = (float)hitPosition.Value; // + (float)virtualHitPosition.Value - 110f;
// float hitPositionValue = (float)hitPosition.Value; // + (float)virtualHitPosition.Value - 110f;
if (stage.Columns == 14 && columnIndex == 13)
width = 0f;
@@ -234,8 +235,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
case LegacyManiaSkinConfigurationLookups.ColumnWidth:
return SkinUtils.As<TValue>(new Bindable<float>(width));
case LegacyManiaSkinConfigurationLookups.HitPosition:
return SkinUtils.As<TValue>(new Bindable<float>(hitPositionValue));
// case LegacyManiaSkinConfigurationLookups.HitPosition:
// return SkinUtils.As<TValue>(new Bindable<float>(hitPositionValue));
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:

View File

@@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.UseRelativeSize))]
public BindableBool UseRelativeSize { get; } = new BindableBool(true);
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.Colour), nameof(SkinnableComponentStrings.ColourDescription))]
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.Colour))]
public BindableColour4 AccentColour { get; } = new BindableColour4(Colour4.White);
[Resolved]

View File

@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
public Bindable<EzSelectorNameSet> NameDropdown { get; } = new Bindable<EzSelectorNameSet>((EzSelectorNameSet)4);
[SettingSource("Effect Type", "Effect Type")]
public Bindable<EffectType> Effect { get; } = new Bindable<EffectType>(EffectType.Scale);
public Bindable<EzComEffectType> Effect { get; } = new Bindable<EzComEffectType>(EzComEffectType.Scale);
// [SettingSource("Effect Origin", "Effect Origin", SettingControlType = typeof(AnchorDropdown))]
// public Bindable<Anchor> EffectOrigin { get; } = new Bindable<Anchor>(Anchor.TopCentre)
@@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
Precision = 0.01f,
};
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.Colour), nameof(SkinnableComponentStrings.ColourDescription))]
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.Colour))]
public BindableColour4 AccentColour { get; } = new BindableColour4(Colour4.White);
public EzComboText Text = null!;
@@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
{
switch (Effect.Value)
{
case EffectType.Scale:
case EzComEffectType.Scale:
EzEffectHelper.ApplyScaleAnimation(
Text.TextContainer,
wasIncrease,
@@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
EffectEndDuration.Value);
break;
case EffectType.Bounce:
case EzComEffectType.Bounce:
EzEffectHelper.ApplyBounceAnimation(
Text.TextContainer,
wasIncrease,

View File

@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
public Bindable<EzSelectorNameSet> NameDropdown { get; } = new Bindable<EzSelectorNameSet>((EzSelectorNameSet)4);
[SettingSource("Effect Type", "Effect Type")]
public Bindable<EffectType> Effect { get; } = new Bindable<EffectType>(EffectType.Scale);
public Bindable<EzComEffectType> Effect { get; } = new Bindable<EzComEffectType>(EzComEffectType.Scale);
[SettingSource("Effect Origin", "Effect Origin", SettingControlType = typeof(AnchorDropdown))]
public Bindable<Anchor> EffectOrigin { get; } = new Bindable<Anchor>(Anchor.TopCentre)
@@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
Precision = 0.01f,
};
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.Colour), nameof(SkinnableComponentStrings.ColourDescription))]
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.Colour))]
public BindableColour4 AccentColour { get; } = new BindableColour4(Colour4.White);
public EzComboText Text = null!;
@@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
{
switch (Effect.Value)
{
case EffectType.Scale:
case EzComEffectType.Scale:
EzEffectHelper.ApplyScaleAnimation(
Text.TextContainer,
wasIncrease,
@@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
EffectEndDuration.Value);
break;
case EffectType.Bounce:
case EzComEffectType.Bounce:
EzEffectHelper.ApplyBounceAnimation(
Text.TextContainer,
wasIncrease,

View File

@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
Precision = 0.01f,
};
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.Colour), nameof(SkinnableComponentStrings.ColourDescription))]
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.Colour))]
public BindableColour4 AccentColour { get; } = new BindableColour4(Colour4.White);
private Container timingContainer = null!;

View File

@@ -51,7 +51,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2HUD
protected override void LoadComplete()
{
base.LoadComplete();
updateWidths();
triggers.BindTo(controller.Triggers);
triggers.BindCollectionChanged(triggersChanged, true);
columnWidth.BindValueChanged(_ => updateWidths(), true);

View File

@@ -0,0 +1,168 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Screens;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
{
public partial class EzColumnBackground : CompositeDrawable, IKeyBindingHandler<ManiaAction>
{
private Bindable<double> hitPosition = new Bindable<double>();
private Color4 brightColour;
private Color4 dimColour;
private Box backgroundOverlay = null!;
private readonly Box separator = new Box
{
Name = "Separator",
Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre,
Width = 2,
Colour = Color4.White.Opacity(0.5f),
Alpha = 0,
};
private Bindable<Color4> accentColour = null!;
[Resolved]
protected Column Column { get; private set; } = null!;
[Resolved]
private StageDefinition stageDefinition { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
public EzColumnBackground()
{
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
RelativeSizeAxes = Axes.Both;
// Masking = true;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.8f).Darken(3),
},
backgroundOverlay = new Box
{
Name = "Background Gradient Overlay",
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = 0.5f,
Blending = BlendingParameters.Additive,
Alpha = 0
},
}
};
accentColour = new Bindable<Color4>(ezSkinConfig.GetColumnColor(stageDefinition.Columns, Column.Index));
accentColour.BindValueChanged(colour =>
{
var newColour = colour.NewValue.Darken(3);
if (newColour.A != 0)
{
newColour = newColour.Opacity(0.8f);
}
backgroundOverlay.Colour = newColour;
brightColour = colour.NewValue.Opacity(0.6f);
dimColour = colour.NewValue.Opacity(0);
}, true);
}
protected override void LoadComplete()
{
base.LoadComplete();
bool hasSeparator = Column.TopLevelContainer.Children
.OfType<Box>()
.Any(b => b.Name == "Separator");
if (!hasSeparator)
Column.TopLevelContainer.Add(separator);
hitPosition = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
hitPosition.BindValueChanged(_ => OnConfigChanged(), true);
}
protected virtual Color4 NoteColor
{
get
{
int keyMode = stageDefinition.Columns;
int columnIndex = Column.Index;
return ezSkinConfig.GetColumnColor(keyMode, columnIndex);
}
}
private void OnConfigChanged()
{
separator.Height = DrawHeight - (float)hitPosition.Value;
if (drawSeparator(Column.Index, stageDefinition))
separator.Alpha = 0.25f;
else
separator.Alpha = 0;
}
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{
if (e.Action == Column.Action.Value)
{
var noteColour = NoteColor;
brightColour = noteColour.Opacity(0.9f);
dimColour = noteColour.Opacity(0);
backgroundOverlay.Colour = ColourInfo.GradientVertical(dimColour, brightColour);
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
}
return false;
}
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{
if (e.Action == Column.Action.Value)
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
}
//TODO: 这里的逻辑可以优化,避免重复计算
private bool drawSeparator(int columnIndex, StageDefinition stage)
{
return stage.Columns switch
{
12 => columnIndex is 0 or 10,
14 => columnIndex is 0 or 5 or 6 or 11,
16 => columnIndex is 0 or 5 or 9 or 14,
_ => false
};
}
}
}

View File

@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -9,10 +8,8 @@ using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
using osuTK;
@@ -21,22 +18,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
{
public partial class EzHitExplosion : CompositeDrawable, IHitExplosion
{
// public override bool RemoveWhenNotAlive => true;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Bindable<double> columnWidth = new Bindable<double>();
private IBindable<double> noteHeightBindable = new Bindable<double>();
private IBindable<double> columnWidthBindable = new Bindable<double>();
private IBindable<double> specialFactorBindable = new Bindable<double>();
private IBindable<double> hitPosition = new Bindable<double>();
private TextureAnimation? animation;
private TextureAnimation? animationP;
private Container container = null!;
[UsedImplicitly]
private float baseYPosition;
private IBindable<double> noteHeightBindable = new Bindable<double>();
private IBindable<double> columnWidthBindable = new Bindable<double>();
private IBindable<double> specialFactorBindable = new Bindable<double>();
// public override bool RemoveWhenNotAlive => true;
[Resolved]
private Column column { get; set; } = null!;
@@ -45,10 +35,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
private StageDefinition stageDefinition { get; set; } = null!;
[Resolved]
private EzLocalTextureFactory factory { get; set; } = null!;
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
private EzLocalTextureFactory factory { get; set; } = null!;
public EzHitExplosion()
{
@@ -57,27 +47,24 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
private void load()
{
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
noteHeightBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.NonSquareNoteHeight);
columnWidthBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.ColumnWidth);
specialFactorBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.SpecialFactor);
hitPosition = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
noteHeightBindable.BindValueChanged(_ => updateY(), true);
columnWidthBindable.BindValueChanged(_ => updateY(), true);
specialFactorBindable.BindValueChanged(_ => updateY(), true);
hitPosition.BindValueChanged(_ => updateY(), true);
}
protected override void LoadComplete()
{
base.LoadComplete();
onSkinChanged();
factory.OnNoteChanged += onSkinChanged;
}
@@ -85,10 +72,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
{
base.Dispose(isDisposing);
if (isDisposing)
{
factory.OnNoteChanged -= onSkinChanged;
}
if (isDisposing) { factory.OnNoteChanged -= onSkinChanged; }
}
private void loadAnimation()
@@ -98,9 +82,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
animation = factory.CreateAnimation("noteflare");
animationP = factory.CreateAnimation("noteflaregood");
animation.Loop = false;
animationP.Loop = false;
container = new Container
{
Anchor = Anchor.BottomCentre,
@@ -116,18 +97,18 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
private void updateY()
{
bool isSpecialColumn = stageDefinition.EzIsSpecialColumn(column.Index);
columnWidth.Value = columnWidthBindable.Value * (isSpecialColumn ? specialFactorBindable.Value : 1);
bool isSpecialColumn = EzColumnTypeManager.GetColumnType(stageDefinition.Columns, column.Index) == "S1";
double columnWidth = columnWidthBindable.Value * (isSpecialColumn ? specialFactorBindable.Value : 1);
bool isSquare = factory.IsSquareNote("whitenote");
float aspectRatio = factory.GetRatio("whitenote");
float moveY = isSquare
? (float)columnWidth.Value / 2 * aspectRatio
? (float)columnWidth / 2 * aspectRatio
: (float)noteHeightBindable.Value / 2 * aspectRatio;
baseYPosition = 110f - (float)hitPosition.Value - moveY;
Position = new Vector2(0, baseYPosition);
// baseYPosition = LegacyManiaSkinConfiguration.DEFAULT_HIT_POSITION - (float)hitPosition.Value - moveY;
Position = new Vector2(0, -moveY);
}
private void onSkinChanged()
@@ -135,12 +116,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
loadAnimation();
}
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
Rotation = direction.NewValue == ScrollingDirection.Up ? 90f : 0;
Anchor = Origin = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
}
public void Animate(JudgementResult result)
{
loadAnimation();

View File

@@ -3,20 +3,15 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
{
internal partial class EzHitTarget : EzNote
{
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private IBindable<double> hitPosition = new Bindable<double>();
// private IBindable<double> hitPosition = new Bindable<double>();
protected override bool ShowSeparators => false;
protected override bool UseColorization => false;
@@ -28,8 +23,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
[Resolved]
private IGameplayClock gameplayClock { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
// [Resolved]
// private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
public EzHitTarget()
{
@@ -39,16 +34,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
private void load()
{
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
hitPosition = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
hitPosition.BindValueChanged(_ => updateY(), true);
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
// hitPosition = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
// hitPosition.BindValueChanged(_ => updateY(), true);
}
private float baseYPosition;
// private float baseYPosition = 0f;
private double beatInterval;
protected override void LoadComplete()
@@ -65,18 +59,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
double progress = (gameplayClock.CurrentTime % beatInterval) / beatInterval;
// 平滑正弦波效果
double smoothValue = 0.3 * Math.Sin(progress * 2 * Math.PI);
Y = baseYPosition + (float)(smoothValue * 6);
Y = (float)(smoothValue * 6);
}
private void updateY()
{
baseYPosition = 110f - (float)hitPosition.Value;
Position = new Vector2(0, baseYPosition);
}
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
Anchor = Origin = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
}
//DrawableManiaRuleset中关联设置后此处不必设置
// private void updateY()
// {
// baseYPosition = LegacyManiaSkinConfiguration.DEFAULT_HIT_POSITION - (float)hitPosition.Value;
// Position = new Vector2(0, baseYPosition);
// }
}
}

View File

@@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
@@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
private Bindable<double> nonSquareNoteHeight = null!;
private TextureAnimation animation = null!;
private Container? container;
private Container container = null!;
private EzNoteSideLine? noteSeparatorsL;
private EzNoteSideLine? noteSeparatorsR;
@@ -45,13 +44,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
nonSquareNoteHeight = ezSkinConfig.GetBindable<double>(EzSkinSetting.NonSquareNoteHeight);
enabledColor = ezSkinConfig.GetBindable<bool>(EzSkinSetting.ColorSettingsEnabled);
OnSkinChanged();
}
protected override void LoadComplete()
{
base.LoadComplete();
OnSkinChanged();
nonSquareNoteHeight.ValueChanged += _ => updateSizes();
nonSquareNoteHeight.BindValueChanged(_ => updateSizes(), true);
ezSkinConfig.OnSettingsChanged += OnConfigChanged;
factory.OnNoteChanged += OnSkinChanged;
}
@@ -84,14 +84,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
if (enabledColor.Value)
return "white";
if (stageDefinition.EzIsSpecialColumn(column.Index))
if (EzColumnTypeManager.GetColumnType(stageDefinition.Columns, column.Index) == "S1")
return "green";
int logicalIndex = 0;
for (int i = 0; i < column.Index; i++)
{
if (!stageDefinition.EzIsSpecialColumn(i))
if (EzColumnTypeManager.GetColumnType(stageDefinition.Columns, i) != "S1")
logicalIndex++;
}
@@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
AddInternal(animation);
}
Schedule(updateSizes);
OnConfigChanged();
}
private void updateSizes()
@@ -176,11 +176,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
Height = noteHeight;
if (container != null)
if (container.Children.Count > 0 && container.Child is Container c)
{
container.Height = noteHeight / 2;
if (container.Child is Container containerA)
containerA.Height = noteHeight;
c.Height = noteHeight;
}
}
@@ -191,7 +190,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
noteColor = NoteColor;
animation.Colour = noteColor;
container.Colour = noteColor;
noteSeparatorsL?.UpdateGlowEffect(noteColor);
noteSeparatorsR?.UpdateGlowEffect(noteColor);

View File

@@ -8,10 +8,10 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
@@ -19,17 +19,16 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
public partial class EzHoldNoteHittingLayer : CompositeDrawable
{
public readonly Bindable<bool> IsHitting = new Bindable<bool>();
private readonly IBindable<double> noteHeightBindable = new Bindable<double>();
private readonly IBindable<double> columnWidthBindable = new Bindable<double>();
private readonly IBindable<double> specialFactorBindable = new Bindable<double>();
private readonly IBindable<double> hitPosition = new Bindable<double>();
private TextureAnimation? animation;
// [UsedImplicitly]
private readonly Bindable<Vector2> noteSize = new Bindable<Vector2>();
private IBindable<double> noteHeightBindable = new Bindable<double>();
private IBindable<double> columnWidthBindable = new Bindable<double>();
private IBindable<double> specialFactorBindable = new Bindable<double>();
private float baseYPosition;
public IBindable<double> HitPosition { get; set; } = new Bindable<double>();
// private IBindable<double> hitPosition = new Bindable<double>();
// private float baseYPosition;
[Resolved]
private Column column { get; set; } = null!;
@@ -38,50 +37,30 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
private StageDefinition stageDefinition { get; set; } = null!;
[Resolved]
private EzLocalTextureFactory factory { get; set; } = null!;
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
private EzLocalTextureFactory factory { get; set; } = null!;
public EzHoldNoteHittingLayer()
{
Anchor = Anchor.BottomCentre;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
// AutoSizeAxes = Axes.Both;
Blending = BlendingParameters.Additive;
}
[BackgroundDependencyLoader]
private void load()
{
noteHeightBindable.BindTo(ezSkinConfig.GetBindable<double>(EzSkinSetting.NonSquareNoteHeight));
columnWidthBindable.BindTo(ezSkinConfig.GetBindable<double>(EzSkinSetting.ColumnWidth));
specialFactorBindable.BindTo(ezSkinConfig.GetBindable<double>(EzSkinSetting.SpecialFactor));
hitPosition.BindTo(ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition));
hitPosition.BindValueChanged(_ => updateY(), true);
noteHeightBindable.BindValueChanged(_ => updateY(), true);
columnWidthBindable.BindValueChanged(_ => updateY());
specialFactorBindable.BindValueChanged(_ => updateY());
noteSize.BindValueChanged(_ => updateY(), true);
IsHitting.BindValueChanged(hitting =>
{
ClearTransforms();
// Logger.Log($"IsHitting changed to: {hitting.NewValue}", LoggingTarget.Runtime, LogLevel.Debug);
// animation.IsPlaying = hitting.NewValue;
if (hitting.NewValue && animation.IsNotNull())
{
Alpha = 1;
animation.Restart();
}
else
{
Alpha = 0;
}
}, true);
noteHeightBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.NonSquareNoteHeight);
columnWidthBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.ColumnWidth);
specialFactorBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.SpecialFactor);
HitPosition = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
noteHeightBindable.BindValueChanged(_ => UpdateLNsLight(), true);
columnWidthBindable.BindValueChanged(_ => UpdateLNsLight(), true);
specialFactorBindable.BindValueChanged(_ => UpdateLNsLight(), true);
// hitPosition.BindValueChanged(_ => updateY(), true);
}
protected override void LoadComplete()
@@ -89,6 +68,25 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
base.LoadComplete();
onSkinChanged();
factory.OnNoteChanged += onSkinChanged;
HitPosition.BindValueChanged(pos => Y =
LegacyManiaSkinConfiguration.DEFAULT_HIT_POSITION - (float)pos.NewValue, true);
IsHitting.BindValueChanged(hitting =>
{
ClearTransforms();
// Logger.Log($"IsHitting changed to: {hitting.NewValue}", LoggingTarget.Runtime, LogLevel.Debug);
// animation.IsPlaying = hitting.NewValue;
if (hitting.NewValue && animation.IsNotNull() && animation.FrameCount > 0)
{
Alpha = 1;
animation.Restart();
}
else
{
Alpha = 0;
}
}, true);
}
protected override void Dispose(bool isDisposing)
@@ -126,32 +124,22 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
animation.Loop = true;
AddInternal(animation);
updateY();
UpdateLNsLight();
}
private void updateY()
public void UpdateLNsLight()
{
bool isSpecialColumn = stageDefinition.EzIsSpecialColumn(column.Index);
bool isSpecialColumn = EzColumnTypeManager.GetColumnType(stageDefinition.Columns, column.Index) == "S1";
double columnWidth = columnWidthBindable.Value * (isSpecialColumn ? specialFactorBindable.Value : 1);
bool isSquare = factory.IsSquareNote("whitenote");
var tempContainer = factory.CreateAnimation("whitenote");
float aspectRatio = 1f;
float aspectRatio = factory.GetRatio("whitenote");
if (tempContainer.FrameCount > 0)
{
aspectRatio = tempContainer.CurrentFrame.Height / (float)tempContainer.CurrentFrame.Width;
}
tempContainer.Dispose();
float moveY = isSquare
? (float)columnWidth / 2 * aspectRatio
: (float)noteHeightBindable.Value * aspectRatio;
baseYPosition = 110f - (float)hitPosition.Value - moveY;
Position = new Vector2(0, baseYPosition);
Invalidate();
Position = new Vector2(0, moveY);
}
private void onSkinChanged()

View File

@@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Mania.Skinning.Legacy;
@@ -34,7 +33,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
private Container middleScaleContainer = null!;
private Container middleInnerContainer = null!;
private EzSkinSettingsManager ezSkinConfig = null!;
private IBindable<double> noteHeight = new Bindable<double>();
private IBindable<double> hitPosition = new Bindable<double>();
private Bindable<bool> enabledColor = null!;
private Drawable? container;
private Drawable? lightContainer;
@@ -46,6 +46,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
[Resolved]
private StageDefinition stageDefinition { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
[Resolved]
private EzLocalTextureFactory factory { get; set; } = null!;
@@ -60,12 +63,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
}
[BackgroundDependencyLoader(true)]
private void load(EzSkinSettingsManager ezSkinConfig, DrawableHitObject drawableObject)
private void load(DrawableHitObject drawableObject)
{
this.ezSkinConfig = ezSkinConfig;
holdNote = (DrawableHoldNote)drawableObject;
isHitting.BindTo(holdNote.IsHolding);
noteHeight = ezSkinConfig.GetBindable<double>(EzSkinSetting.NonSquareNoteHeight);
hitPosition = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
enabledColor = ezSkinConfig.GetBindable<bool>(EzSkinSetting.ColorSettingsEnabled);
}
@@ -85,6 +89,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
{
base.LoadComplete();
OnSkinChanged();
noteHeight.BindValueChanged(_ => OnSettingsChanged(), true);
factory.OnNoteChanged += OnSkinChanged;
ezSkinConfig.OnSettingsChanged += OnSettingsChanged;
isHitting.BindValueChanged(onIsHittingChanged, true);
@@ -104,6 +109,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
private void OnSkinChanged()
{
if (lightContainer != null)
{
column.TopLevelContainer.Remove(lightContainer, false);
lightContainer.Expire();
lightContainer = null;
}
loadAnimation();
hittingLayer = new EzHoldNoteHittingLayer
{
@@ -115,6 +127,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
Alpha = 0,
Child = hittingLayer
};
hittingLayer.HitPosition.BindTo(hitPosition);
}
private void onIsHittingChanged(ValueChangedEvent<bool> isHitting)
@@ -157,14 +170,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
if (enabledColor.Value)
return "white";
if (stageDefinition.EzIsSpecialColumn(column.Index))
if (EzColumnTypeManager.GetColumnType(stageDefinition.Columns, column.Index) == "S1")
return "green";
int logicalIndex = 0;
for (int i = 0; i < column.Index; i++)
{
if (!stageDefinition.EzIsSpecialColumn(i))
if (EzColumnTypeManager.GetColumnType(stageDefinition.Columns, column.Index) != "S1")
logicalIndex++;
}
@@ -172,15 +185,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
}
}
protected virtual string ComponentName => $"{ColorPrefix}longnote/middle";
protected virtual string ComponentName2 => $"{ColorPrefix}longnote/tail";
private void loadAnimation()
{
ClearInternal();
string backupComponentName = $"{ColorPrefix}note";
middleAnimation = factory.CreateAnimation(ComponentName);
tailAnimation = factory.CreateAnimation(ComponentName2);
middleAnimation = factory.CreateAnimation($"{ColorPrefix}longnote/middle");
tailAnimation = factory.CreateAnimation($"{ColorPrefix}longnote/tail");
if (middleAnimation.FrameCount == 0)
{

View File

@@ -3,21 +3,22 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.Skinning.Legacy;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
{
public partial class EzStageKeys : CompositeDrawable
public partial class EzKeyArea : LegacyManiaColumnElement, IKeyBindingHandler<ManiaAction>
{
private TextureAnimation animation = null!;
private Drawable container = null!;
private Drawable upSprite = null!;
private Drawable downSprite = null!;
protected virtual bool IsKeyPress => true;
protected virtual bool UseColorization => true;
@@ -33,18 +34,21 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
public EzKeyArea()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X;
FillMode = FillMode.Fill;
}
protected override void LoadComplete()
{
base.LoadComplete();
OnSkinChanged();
factory.OnNoteChanged += OnSkinChanged;
factory.OnStageChanged += OnSkinChanged;
ezSkinConfig.OnSettingsChanged += OnConfigChanged;
}
@@ -54,11 +58,55 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
if (isDisposing)
{
factory.OnNoteChanged -= OnSkinChanged;
factory.OnStageChanged -= OnSkinChanged;
ezSkinConfig.OnSettingsChanged -= OnConfigChanged;
}
}
protected virtual string KeyBasicSuffix
{
get
{
if (EzColumnTypeManager.GetColumnType(stageDefinition.Columns, column.Index) == "S1")
return "02";
int logicalIndex = 0;
for (int i = 0; i < column.Index; i++)
{
if (EzColumnTypeManager.GetColumnType(stageDefinition.Columns, i) == "S1")
logicalIndex++;
}
return logicalIndex % 2 == 0 ? "00" : "01";
}
}
private void loadAnimation()
{
ClearInternal();
upSprite = factory.CreateStage("keybase");
downSprite = factory.CreateStage("keypress");
downSprite.Alpha = 0;
container = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
upSprite,
downSprite,
}
};
OnConfigChanged();
AddInternal(container);
}
private void updateSizes()
{
bool isSquare = factory.IsSquareNote("whitenote");
@@ -67,25 +115,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
: (float)(ezSkinConfig.GetBindable<double>(EzSkinSetting.NonSquareNoteHeight).Value);
}
private void loadAnimation()
{
ClearInternal();
animation = factory.CreateAnimation(ComponentName);
container = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Child = animation
};
OnConfigChanged();
AddInternal(container);
}
private void OnSkinChanged() => loadAnimation();
private void OnConfigChanged()
{
Schedule(() =>
@@ -95,32 +124,26 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
});
}
protected virtual string KeyPressPrefix => IsKeyPress switch
{
true => "keypress/KeyBasicPress_",
_ => "keybase/KeyBasicBase_",
};
private void OnSkinChanged() => loadAnimation();
protected virtual string KeyBasicSuffix
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{
get
if (e.Action == column.Action.Value)
{
if (stageDefinition.EzIsSpecialColumn(column.Index))
return "02";
int logicalIndex = 0;
for (int i = 0; i < column.Index; i++)
{
if (!stageDefinition.EzIsSpecialColumn(i))
logicalIndex++;
}
return logicalIndex % 2 == 0 ? "00" : "01";
upSprite.FadeTo(0);
downSprite.FadeTo(1);
}
return false;
}
protected virtual string StagePrefix => EzStageBottom.StagePrefix;
protected virtual string ComponentName => $"{StagePrefix}/{KeyPressPrefix}{KeyBasicSuffix}";
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{
if (e.Action == column.Action.Value)
{
upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1);
downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0);
}
}
}
}

View File

@@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
@@ -88,14 +87,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
if (enabledColor.Value)
return "white";
if (stageDefinition.EzIsSpecialColumn(column.Index))
if (EzColumnTypeManager.GetColumnType(stageDefinition.Columns, column.Index) == "S1")
return "green";
int logicalIndex = 0;
for (int i = 0; i < column.Index; i++)
{
if (!stageDefinition.EzIsSpecialColumn(i))
if (EzColumnTypeManager.GetColumnType(stageDefinition.Columns, i) != "S1")
logicalIndex++;
}

View File

@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
@@ -29,20 +30,21 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
private void load()
{
RelativeSizeAxes = Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
hitPositon = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
columnWidth = ezSkinConfig.GetBindable<double>(EzSkinSetting.ColumnWidth);
hitPositon.BindValueChanged(_ => OnConfigChanged());
columnWidth.BindValueChanged(_ => OnConfigChanged());
OnSkinChanged();
}
protected override void LoadComplete()
{
base.LoadComplete();
OnSkinChanged();
factory.OnNoteChanged += OnSkinChanged;
factory.OnStageChanged += OnSkinChanged;
}
protected override void Dispose(bool isDisposing)
@@ -51,24 +53,27 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
if (isDisposing)
{
factory.OnNoteChanged -= OnSkinChanged;
factory.OnStageChanged -= OnSkinChanged;
}
}
public static string StagePrefix => "Stage/fivekey";
protected static string ComponentName => $"{StagePrefix}/Body";
// protected override void Update()
// {
// base.Update();
// updateSizes();
// }
private void loadAnimation()
{
ClearInternal();
var stageBottom = factory.CreateStage(ComponentName);
var stageBottom = factory.CreateStage("Body");
sprite = new Container
{
RelativeSizeAxes = Axes.None,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
// Masking = true,
Child = stageBottom
};
// sprite.Depth = float.MinValue;
@@ -76,23 +81,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
AddInternal(sprite);
}
protected override void Update()
{
base.Update();
updateSizes();
}
private void updateSizes()
{
if (sprite == null)
return;
float actualPanelWidth = DrawWidth;
double scale = actualPanelWidth / 410.0;
sprite.Scale = new Vector2((float)scale);
float scale = actualPanelWidth / 410.0f;
sprite.Scale = new Vector2(scale);
Y = 274;
Position = new Vector2(0, 660 + 110 - (float)hitPositon.Value);
sprite.Y = LegacyManiaSkinConfiguration.DEFAULT_HIT_POSITION - (float)hitPositon.Value - DrawHeight * 0.865f;
// Position = new Vector2(0, 415 + 110 - (float)hitPositon.Value);
}
private void OnConfigChanged()

View File

@@ -7,16 +7,15 @@ using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.Skinning.Ez2;
using osu.Game.Rulesets.Mania.Skinning.Ez2HUD;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osu.Game.Skinning;
using osu.Game.Skinning.Components;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
{
@@ -180,7 +179,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
// if (Skin is Ez2Skin && resultComponent.Component >= HitResult.Perfect)
// return Drawable.Empty();
return new Ez2ColumnBackground();
return new EzColumnBackground();
case ManiaSkinComponents.KeyArea:
return new Ez2KeyArea();
@@ -227,7 +226,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
{
int columnIndex = maniaLookup.ColumnIndex ?? 0;
var stage = beatmap.GetStageForColumnIndex(columnIndex);
bool isSpecialColumn = stage.EzIsSpecialColumn(columnIndex);
bool isSpecialColumn = EzColumnTypeManager.GetColumnType(stage.Columns, columnIndex) == "S1";
float width = (float)columnWidthBindable.Value * (isSpecialColumn ? (float)specialFactorBindable.Value : 1f);
// float hitPositionValue = (float)hitPosition.Value; // + (float)virtualHitPosition.Value - 110f;
@@ -242,9 +241,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
// case LegacyManiaSkinConfigurationLookups.HitPosition:
// return SkinUtils.As<TValue>(new Bindable<float>(hitPositionValue));
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
var colour = stage.GetColourForLayout(columnIndex);
return SkinUtils.As<TValue>(new Bindable<Color4>(colour));
// case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
// var colour = stage.GetColourForLayout(columnIndex);
// return SkinUtils.As<TValue>(new Bindable<Color4>(colour));
case LegacyManiaSkinConfigurationLookups.BarLineHeight:
return SkinUtils.As<TValue>(new Bindable<float>(1));

View File

@@ -5,8 +5,8 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning.Legacy
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
public partial class HitTargetInsetContainer : Container
{
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Bindable<double> hitPositonBindable = new Bindable<double>();
protected override Container<Drawable> Content => content;
private readonly Container content;
@@ -28,14 +28,21 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
}
[BackgroundDependencyLoader]
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
private void load(ISkinSource skin, EzSkinSettingsManager ezSkinConfig, IScrollingInfo scrollingInfo)
{
hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? Stage.HIT_TARGET_POSITION;
hitPositonBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
hitPositonBindable.BindValueChanged(_ => UpdateHitPosition(), true);
hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? (float)hitPositonBindable.Value;
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
}
protected virtual void UpdateHitPosition()
{
hitPosition = (float)hitPositonBindable.Value;
}
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
content.Padding = direction.NewValue == ScrollingDirection.Up

View File

@@ -14,7 +14,6 @@ using osu.Game.Skinning;
using osu.Game.Skinning.Components;
using osuTK;
using osuTK.Graphics;
using EffectType = osu.Game.Skinning.Components.EffectType;
namespace osu.Game.Rulesets.Mania.Skinning.SbI
{
@@ -71,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.SbI
combo1.Anchor = Anchor.TopCentre;
combo1.Origin = Anchor.Centre;
combo1.Y = 200;
combo1.Effect.Value = EffectType.None;
combo1.Effect.Value = EzComEffectType.None;
combo1.NameDropdown.Value = (EzSelectorNameSet)43;
}

View File

@@ -11,9 +11,9 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Layout;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.Skinning;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
using osu.Game.Skinning;
using osuTK;
@@ -68,28 +68,28 @@ namespace osu.Game.Rulesets.Mania.UI
[Resolved]
private ISkinSource skin { get; set; } = null!;
[Resolved]
private SkinManager skinManager { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
private readonly Bindable<ManiaMobileLayout> mobileLayout = new Bindable<ManiaMobileLayout>();
private readonly Bindable<double> columnWidthBindable = new Bindable<double>();
private readonly Bindable<double> specialFactorBindable = new Bindable<double>();
private readonly Bindable<EzColumnWidthStyle> ezColumnWidthStyle = new Bindable<EzColumnWidthStyle>();
[BackgroundDependencyLoader]
private void load(ManiaRulesetConfigManager? rulesetConfig)
{
rulesetConfig?.BindWith(ManiaRulesetSetting.MobileLayout, mobileLayout);
ezSkinConfig.BindWith(EzSkinSetting.ColumnWidthStyle, ezColumnWidthStyle);
ezSkinConfig.BindWith(EzSkinSetting.ColumnWidth, columnWidthBindable);
ezSkinConfig.BindWith(EzSkinSetting.SpecialFactor, specialFactorBindable);
columnWidthBindable.BindValueChanged(v =>
{
updateColumnSize();
});
specialFactorBindable.BindValueChanged(v =>
{
updateColumnSize();
});
ezColumnWidthStyle.BindValueChanged(v => updateColumnSize());
columnWidthBindable.BindValueChanged(v => updateColumnSize());
specialFactorBindable.BindValueChanged(v => updateColumnSize());
mobileLayout.BindValueChanged(_ => invalidateLayout());
skin.SourceChanged += invalidateLayout;
@@ -156,8 +156,6 @@ namespace osu.Game.Rulesets.Mania.UI
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i))
?.Value;
bool isSpecialColumn = stageDefinition.EzIsSpecialColumn(i);
if (width == 0)
{
columns[i].Width = 0;
@@ -165,7 +163,26 @@ namespace osu.Game.Rulesets.Mania.UI
continue;
}
width = (float)columnWidthBindable.Value * (isSpecialColumn ? (float)specialFactorBindable.Value : 1);
bool isSpecialColumn =
EzColumnTypeManager.GetColumnType(stageDefinition.Columns, i) == "S1";
float ezWidth = (float)columnWidthBindable.Value * (isSpecialColumn ? (float)specialFactorBindable.Value : 1);
switch (ezColumnWidthStyle.Value)
{
case EzColumnWidthStyle.EzStyleProOnly:
var skinInfo = skinManager.CurrentSkinInfo.Value;
if (skinInfo.Value.Name.Contains("Ez Style Pro"))
width = ezWidth;
break;
case EzColumnWidthStyle.GlobalWidth:
width = ezWidth;
break;
case EzColumnWidthStyle.GlobalTotalWidth:
width = ezWidth * 10 / stageDefinition.Columns;
break;
}
// only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration)
width ??= isSpecialColumn ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH;

View File

@@ -23,12 +23,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
private Bindable<double> hitPositonBindable = new Bindable<double>();
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{
Direction.BindTo(scrollingInfo.Direction);
Direction.BindValueChanged(_ => UpdateHitPosition(), true);
hitPositonBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
hitPositonBindable.BindValueChanged(_ => UpdateHitPosition(), true);
skin.SourceChanged += onSkinChanged;
}
@@ -38,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
float hitPosition = skin.GetConfig<ManiaSkinConfigurationLookup, float>(
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
?? Stage.HIT_TARGET_POSITION;
?? (float)hitPositonBindable.Value;
Padding = Direction.Value == ScrollingDirection.Up
? new MarginPadding { Top = hitPosition }

View File

@@ -18,6 +18,7 @@ using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Skinning;
@@ -27,6 +28,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
@@ -79,6 +81,12 @@ namespace osu.Game.Rulesets.Mania.UI
[Resolved]
private GameHost gameHost { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
private Bindable<double> hitPositonBindable = new Bindable<double>();
private readonly Bindable<bool> globalHitPosition = new Bindable<bool>();
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
: base(ruleset, beatmap, mods)
{
@@ -125,6 +133,11 @@ namespace osu.Game.Rulesets.Mania.UI
Config.BindWith(ManiaRulesetSetting.MobileLayout, mobileLayout);
mobileLayout.BindValueChanged(_ => updateMobileLayout(), true);
hitPositonBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
hitPositonBindable.BindValueChanged(_ => skinChanged(), true);
ezSkinConfig.BindWith(EzSkinSetting.GlobalHitPosition, globalHitPosition);
globalHitPosition.BindValueChanged(_ => skinChanged(), true);
}
private ManiaTouchInputArea? touchInputArea;
@@ -167,9 +180,14 @@ namespace osu.Game.Rulesets.Mania.UI
private void skinChanged()
{
hitPosition = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
?? Stage.HIT_TARGET_POSITION;
if (globalHitPosition.Value)
hitPosition = (float)hitPositonBindable.Value;
else
{
hitPosition = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
?? (float)hitPositonBindable.Value;
}
pendingSkinChange = null;
}
@@ -192,7 +210,7 @@ namespace osu.Game.Rulesets.Mania.UI
case EzManiaScrollingStyle.ScrollTimeStyleFixed:
// Ensure the travel time from the top of the screen to the hit position remains constant.
scale = length_to_default_hit_position / lengthToHitPosition;
scale = lengthToHitPosition / 768;
break;
}

View File

@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
@@ -36,22 +35,21 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
[Test]
public void TestPlayfieldBasedSize()
{
ModFlashlight mod = new OsuModFlashlight();
OsuModFlashlight flashlight;
CreateModTest(new ModTestData
{
Mod = mod,
Mods = [flashlight = new OsuModFlashlight(), new OsuModBarrelRoll()],
PassCondition = () =>
{
var flashlightOverlay = Player.DrawableRuleset.Overlays
.ChildrenOfType<ModFlashlight<OsuHitObject>.Flashlight>()
.First();
return Precision.AlmostEquals(mod.DefaultFlashlightSize * .5f, flashlightOverlay.GetSize());
// the combo check is here because the flashlight radius decreases for the first time at 100 combo
// and hardcoding it here eliminates the need to meddle in flashlight internals further by e.g. exposing `GetComboScaleFor()`
return flashlightOverlay.GetSize() < flashlight.DefaultFlashlightSize && Player.GameplayState.ScoreProcessor.Combo.Value < 100;
}
});
AddStep("adjust playfield scale", () =>
Player.DrawableRuleset.Playfield.Scale = new Vector2(.5f));
}
[Test]

View File

@@ -13,7 +13,6 @@ 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;
@@ -22,21 +21,6 @@ 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]
@@ -46,12 +30,21 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
Autoplay = false,
CreateBeatmap = () => new Beatmap
{
HitObjects = new List<HitObject> { hitObject }
Difficulty = { OverallDifficulty = 9 },
HitObjects = new List<HitObject>
{
new HitCircle
{
StartTime = 1000,
Position = new Vector2(100, 100),
HitWindows = new OsuHitWindows()
}
}
},
ReplayFrames = new List<ReplayFrame>
{
new OsuReplayFrame(0, new Vector2()),
new OsuReplayFrame(hitObject.StartTime, hitObject.Position),
new OsuReplayFrame(100, new Vector2(100)),
},
PassCondition = () => Player.ScoreProcessor.Combo.Value == 1
});
@@ -63,13 +56,22 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
Autoplay = false,
CreateBeatmap = () => new Beatmap
{
HitObjects = new List<HitObject> { hitObject }
Difficulty = { OverallDifficulty = 9 },
HitObjects = new List<HitObject>
{
new HitCircle
{
StartTime = 1000,
Position = new Vector2(100, 100),
HitWindows = new OsuHitWindows()
}
}
},
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)),
new OsuReplayFrame(0, new Vector2(78, 78)), // must be an edge hit for the cursor to not stay on the object for too long
new OsuReplayFrame(1000 - OsuModRelax.RELAX_LENIENCY, new Vector2(78, 78)),
new OsuReplayFrame(1000, new Vector2(0)),
},
PassCondition = () => Player.ScoreProcessor.Combo.Value == 1
});

View File

@@ -17,7 +17,6 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
[Ignore("These tests are expected to fail until an acceptable solution for various replay playback issues concerning rounding of replay frame times & hit windows is found.")]
public partial class TestSceneLegacyReplayPlayback : LegacyReplayPlaybackTestScene
{
protected override Ruleset CreateRuleset() => new OsuRuleset();

View File

@@ -14,26 +14,27 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneOsuHitObjectSamples)));
[TestCase("normal-hitnormal")]
[TestCase("hitnormal")]
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
[TestCase("normal-hitnormal2", "normal-hitnormal")]
[TestCase("hitnormal", "hitnormal")]
public void TestDefaultCustomSampleFromBeatmap(string beatmapSkinSampleName, string userSkinSampleName)
{
SetupSkins(expectedSample, expectedSample);
SetupSkins(beatmapSkinSampleName, userSkinSampleName);
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
AssertBeatmapLookup(expectedSample);
AssertBeatmapLookup(beatmapSkinSampleName);
}
[TestCase("normal-hitnormal")]
[TestCase("hitnormal")]
public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample)
[TestCase("", "normal-hitnormal")]
[TestCase("normal-hitnormal", "normal-hitnormal")]
[TestCase("", "hitnormal")]
public void TestDefaultCustomSampleFromUserSkinFallback(string beatmapSkinSampleName, string userSkinSampleName)
{
SetupSkins(string.Empty, expectedSample);
SetupSkins(beatmapSkinSampleName, userSkinSampleName);
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
AssertUserLookup(expectedSample);
AssertUserLookup(userSkinSampleName);
}
[TestCase("normal-hitnormal2")]

View File

@@ -13,7 +13,6 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
[Ignore("These tests are expected to fail until an acceptable solution for various replay playback issues concerning rounding of replay frame times & hit windows is found.")]
public partial class TestSceneReplayStability : ReplayStabilityTestScene
{
private static readonly object[][] test_cases =
@@ -23,53 +22,49 @@ namespace osu.Game.Rulesets.Osu.Tests
// while round brackets `()` represent *open* or *exclusive* bounds.
// OD = 5 test cases.
// GREAT hit window is [ -50ms, 50ms]
// OK hit window is [-100ms, 100ms]
// MEH hit window is [-150ms, 150ms]
// MISS hit window is [-400ms, 400ms]
// GREAT hit window is [ -49.5ms, 49.5ms]
// OK hit window is [ -99.5ms, 99.5ms]
// MEH hit window is [-149.5ms, 149.5ms]
new object[] { 5f, 49d, HitResult.Great },
new object[] { 5f, 49.2d, HitResult.Great },
new object[] { 5f, 49.7d, HitResult.Great },
new object[] { 5f, 50d, HitResult.Great },
new object[] { 5f, 49.7d, HitResult.Ok },
new object[] { 5f, 50d, HitResult.Ok },
new object[] { 5f, 50.4d, HitResult.Ok },
new object[] { 5f, 50.9d, HitResult.Ok },
new object[] { 5f, 51d, HitResult.Ok },
new object[] { 5f, 99d, HitResult.Ok },
new object[] { 5f, 99.2d, HitResult.Ok },
new object[] { 5f, 99.7d, HitResult.Ok },
new object[] { 5f, 100d, HitResult.Ok },
new object[] { 5f, 99.7d, HitResult.Meh },
new object[] { 5f, 100d, HitResult.Meh },
new object[] { 5f, 100.4d, HitResult.Meh },
new object[] { 5f, 100.9d, HitResult.Meh },
new object[] { 5f, 101d, HitResult.Meh },
new object[] { 5f, 149d, HitResult.Meh },
new object[] { 5f, 149.2d, HitResult.Meh },
new object[] { 5f, 149.7d, HitResult.Meh },
new object[] { 5f, 150d, HitResult.Meh },
new object[] { 5f, 149.7d, HitResult.Miss },
new object[] { 5f, 150d, HitResult.Miss },
new object[] { 5f, 150.4d, HitResult.Miss },
new object[] { 5f, 150.9d, HitResult.Miss },
new object[] { 5f, 151d, HitResult.Miss },
// OD = 5.7 test cases.
// GREAT hit window is [ -45.8ms, 45.8ms]
// OK hit window is [ -94.4ms, 94.4ms]
// MEH hit window is [-143.0ms, 143.0ms]
// MISS hit window is [-400.0ms, 400.0ms]
new object[] { 5.7f, 45d, HitResult.Great },
new object[] { 5.7f, 45.2d, HitResult.Great },
new object[] { 5.7f, 45.8d, HitResult.Great },
new object[] { 5.7f, 45.9d, HitResult.Ok },
new object[] { 5.7f, 46d, HitResult.Ok },
new object[] { 5.7f, 46.4d, HitResult.Ok },
new object[] { 5.7f, 94d, HitResult.Ok },
new object[] { 5.7f, 94.2d, HitResult.Ok },
new object[] { 5.7f, 94.4d, HitResult.Ok },
new object[] { 5.7f, 94.48d, HitResult.Ok },
new object[] { 5.7f, 94.9d, HitResult.Meh },
new object[] { 5.7f, 95d, HitResult.Meh },
new object[] { 5.7f, 95.4d, HitResult.Meh },
// GREAT hit window is [ -44.5ms, 44.5ms]
// OK hit window is [ -93.5ms, 93.5ms]
// MEH hit window is [-142.5ms, 142.5ms]
new object[] { 5.7f, 44d, HitResult.Great },
new object[] { 5.7f, 44.2d, HitResult.Great },
new object[] { 5.7f, 44.8d, HitResult.Ok },
new object[] { 5.7f, 45d, HitResult.Ok },
new object[] { 5.7f, 45.4d, HitResult.Ok },
new object[] { 5.7f, 93d, HitResult.Ok },
new object[] { 5.7f, 93.4d, HitResult.Ok },
new object[] { 5.7f, 93.9d, HitResult.Meh },
new object[] { 5.7f, 94d, HitResult.Meh },
new object[] { 5.7f, 94.4d, HitResult.Meh },
new object[] { 5.7f, 142d, HitResult.Meh },
new object[] { 5.7f, 142.7d, HitResult.Meh },
new object[] { 5.7f, 143d, HitResult.Meh },
new object[] { 5.7f, 142.2d, HitResult.Meh },
new object[] { 5.7f, 142.7d, HitResult.Miss },
new object[] { 5.7f, 143d, HitResult.Miss },
new object[] { 5.7f, 143.4d, HitResult.Miss },
new object[] { 5.7f, 143.9d, HitResult.Miss },
new object[] { 5.7f, 144d, HitResult.Miss },

View File

@@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 100, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 100, slider_end_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 99, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 99, slider_end_position, OsuAction.LeftButton),
});
assertHeadJudgement(HitResult.Ok);
@@ -70,8 +70,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 100, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 100, slider_end_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 99, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 99, slider_end_position, OsuAction.LeftButton),
}, s =>
{
s.SliderVelocityMultiplier = 2;
@@ -91,8 +91,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_end_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 149, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 149, slider_end_position, OsuAction.LeftButton),
}, s =>
{
s.TickDistanceMultiplier = 0.2f;
@@ -116,8 +116,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_end_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 149, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 149, slider_end_position, OsuAction.LeftButton),
}, s =>
{
s.SliderVelocityMultiplier = 2;
@@ -165,8 +165,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 149, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 149, slider_start_position, OsuAction.LeftButton),
}, s =>
{
s.Path = new SliderPath(PathType.LINEAR, new[]
@@ -195,8 +195,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 149, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 149, slider_start_position, OsuAction.LeftButton),
}, s =>
{
s.Path = new SliderPath(PathType.LINEAR, new[]
@@ -224,8 +224,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 149, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 149, slider_start_position, OsuAction.LeftButton),
}, s =>
{
s.Path = new SliderPath(PathType.PERFECT_CURVE, new[]
@@ -259,8 +259,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 149, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 149, slider_start_position, OsuAction.LeftButton),
}, s =>
{
s.Path = new SliderPath(PathType.PERFECT_CURVE, new[]
@@ -289,8 +289,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 149, slider_start_position, OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 149, slider_start_position, OsuAction.LeftButton),
}, s =>
{
s.Path = new SliderPath(PathType.PERFECT_CURVE, new[]
@@ -320,8 +320,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position - new Vector2(20), OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_start_position - new Vector2(20), OsuAction.LeftButton),
new OsuReplayFrame(time_slider_start + 149, slider_start_position - new Vector2(20), OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 149, slider_start_position - new Vector2(20), OsuAction.LeftButton),
}, s =>
{
s.Path = new SliderPath(PathType.PERFECT_CURVE, new[]

View File

@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
get
{
if (UserAdjustedSettingsCount != 1)
if (!IsExactlyOneSettingChanged(CircleSize, ApproachRate, OverallDifficulty, DrainRate))
return string.Empty;
if (!CircleSize.IsDefault) return format("CS", CircleSize);

View File

@@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Osu.Scoring
public override void SetDifficulty(double difficulty)
{
great = IBeatmapDifficultyInfo.DifficultyRange(difficulty, GREAT_WINDOW_RANGE);
ok = IBeatmapDifficultyInfo.DifficultyRange(difficulty, OK_WINDOW_RANGE);
meh = IBeatmapDifficultyInfo.DifficultyRange(difficulty, MEH_WINDOW_RANGE);
great = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, GREAT_WINDOW_RANGE)) - 0.5;
ok = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, OK_WINDOW_RANGE)) - 0.5;
meh = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, MEH_WINDOW_RANGE)) - 0.5;
}
public override double WindowFor(HitResult result)

View File

@@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
PerformTest(new List<ReplayFrame>
{
new TaikoReplayFrame(0),
new TaikoReplayFrame(hit_time - hitWindows.WindowFor(HitResult.Great), TaikoAction.LeftCentre),
new TaikoReplayFrame(hit_time - (hitWindows.WindowFor(HitResult.Great) + 0.1), TaikoAction.LeftCentre),
}, beatmap);
AssertJudgementCount(1);

View File

@@ -15,7 +15,6 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
[Ignore("These tests are expected to fail until an acceptable solution for various replay playback issues concerning rounding of replay frame times & hit windows is found.")]
public partial class TestSceneLegacyReplayPlayback : LegacyReplayPlaybackTestScene
{
protected override string? ExportLocation => null;
@@ -177,7 +176,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
ScoreInfo = new ScoreInfo
{
Ruleset = CreateRuleset().RulesetInfo,
Mods = [new TaikoModHardRock()]
Mods = [new TaikoModEasy()]
}
};

View File

@@ -12,7 +12,6 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
[Ignore("These tests are expected to fail until an acceptable solution for various replay playback issues concerning rounding of replay frame times & hit windows is found.")]
public partial class TestSceneReplayStability : ReplayStabilityTestScene
{
private static readonly object[][] test_cases =
@@ -22,40 +21,38 @@ namespace osu.Game.Rulesets.Taiko.Tests
// while round brackets `()` represent *open* or *exclusive* bounds.
// OD = 5 test cases.
// GREAT hit window is [-35ms, 35ms]
// OK hit window is [-80ms, 80ms]
// MISS hit window is [-95ms, 95ms]
// GREAT hit window is [-34.5ms, 34.5ms]
// OK hit window is [-79.5ms, 79.5ms]
// MISS hit window is [-94.5ms, 94.5ms]
new object[] { 5f, -34d, HitResult.Great },
new object[] { 5f, -34.2d, HitResult.Great },
new object[] { 5f, -34.7d, HitResult.Great },
new object[] { 5f, -35d, HitResult.Great },
new object[] { 5f, -34.7d, HitResult.Ok },
new object[] { 5f, -35d, HitResult.Ok },
new object[] { 5f, -35.2d, HitResult.Ok },
new object[] { 5f, -35.8d, HitResult.Ok },
new object[] { 5f, -36d, HitResult.Ok },
new object[] { 5f, -79d, HitResult.Ok },
new object[] { 5f, -79.3d, HitResult.Ok },
new object[] { 5f, -79.7d, HitResult.Ok },
new object[] { 5f, -80d, HitResult.Ok },
new object[] { 5f, -79.7d, HitResult.Miss },
new object[] { 5f, -80d, HitResult.Miss },
new object[] { 5f, -80.2d, HitResult.Miss },
new object[] { 5f, -80.8d, HitResult.Miss },
new object[] { 5f, -81d, HitResult.Miss },
// OD = 7.8 test cases.
// GREAT hit window is [-26.6ms, 26.6ms]
// OK hit window is [-63.2ms, 63.2ms]
// MISS hit window is [-81.0ms, 81.0ms]
new object[] { 7.8f, -26d, HitResult.Great },
new object[] { 7.8f, -26.4d, HitResult.Great },
new object[] { 7.8f, -26.59d, HitResult.Great },
new object[] { 7.8f, -26.8d, HitResult.Ok },
new object[] { 7.8f, -27d, HitResult.Ok },
new object[] { 7.8f, -27.1d, HitResult.Ok },
new object[] { 7.8f, -63d, HitResult.Ok },
new object[] { 7.8f, -63.18d, HitResult.Ok },
new object[] { 7.8f, -63.4d, HitResult.Ok },
new object[] { 7.8f, -63.7d, HitResult.Miss },
new object[] { 7.8f, -64d, HitResult.Miss },
new object[] { 7.8f, -64.2d, HitResult.Miss },
// GREAT hit window is [-25.5ms, 25.5ms]
// OK hit window is [-62.5ms, 62.5ms]
// MISS hit window is [-80.5ms, 80.5ms]
new object[] { 7.8f, -25d, HitResult.Great },
new object[] { 7.8f, -25.4d, HitResult.Great },
new object[] { 7.8f, -25.8d, HitResult.Ok },
new object[] { 7.8f, -26d, HitResult.Ok },
new object[] { 7.8f, -26.1d, HitResult.Ok },
new object[] { 7.8f, -62d, HitResult.Ok },
new object[] { 7.8f, -62.4d, HitResult.Ok },
new object[] { 7.8f, -62.7d, HitResult.Miss },
new object[] { 7.8f, -63d, HitResult.Miss },
new object[] { 7.8f, -63.2d, HitResult.Miss },
};
[TestCaseSource(nameof(test_cases))]

View File

@@ -14,26 +14,27 @@ namespace osu.Game.Rulesets.Taiko.Tests
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples)));
[TestCase("taiko-normal-hitnormal")]
[TestCase("hitnormal")]
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
[TestCase("taiko-normal-hitnormal2", "taiko-normal-hitnormal")]
[TestCase("hitnormal", "hitnormal")]
public void TestDefaultCustomSampleFromBeatmap(string beatmapSkinSampleName, string userSkinSampleName)
{
SetupSkins(expectedSample, expectedSample);
SetupSkins(beatmapSkinSampleName, userSkinSampleName);
CreateTestWithBeatmap("taiko-hitobject-beatmap-custom-sample-bank.osu");
AssertBeatmapLookup(expectedSample);
AssertBeatmapLookup(beatmapSkinSampleName);
}
[TestCase("taiko-normal-hitnormal")]
[TestCase("hitnormal")]
public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample)
[TestCase("", "taiko-normal-hitnormal")]
[TestCase("taiko-normal-hitnormal", "taiko-normal-hitnormal")]
[TestCase("", "hitnormal")]
public void TestDefaultCustomSampleFromUserSkinFallback(string beatmapSkinSampleName, string userSkinSampleName)
{
SetupSkins(string.Empty, expectedSample);
SetupSkins(beatmapSkinSampleName, userSkinSampleName);
CreateTestWithBeatmap("taiko-hitobject-beatmap-custom-sample-bank.osu");
AssertUserLookup(expectedSample);
AssertUserLookup(userSkinSampleName);
}
[TestCase("taiko-normal-hitnormal2")]

View File

@@ -25,16 +25,16 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
get
{
if (UserAdjustedSettingsCount != 1)
if (!IsExactlyOneSettingChanged(ScrollSpeed, OverallDifficulty, DrainRate))
return string.Empty;
if (!ScrollSpeed.IsDefault) return format("SC", ScrollSpeed);
if (!OverallDifficulty.IsDefault) return format("OD", OverallDifficulty);
if (!DrainRate.IsDefault) return format("HP", DrainRate);
if (!ScrollSpeed.IsDefault) return format("SC", ScrollSpeed, 2);
if (!OverallDifficulty.IsDefault) return format("OD", OverallDifficulty, 1);
if (!DrainRate.IsDefault) return format("HP", DrainRate, 1);
return string.Empty;
string format(string acronym, DifficultyBindable bindable) => $"{acronym}{bindable.Value!.Value.ToStandardFormattedString(1)}";
string format(string acronym, DifficultyBindable bindable, int digits) => $"{acronym}{bindable.Value!.Value.ToStandardFormattedString(digits)}";
}
}

View File

@@ -32,9 +32,9 @@ namespace osu.Game.Rulesets.Taiko.Scoring
public override void SetDifficulty(double difficulty)
{
great = IBeatmapDifficultyInfo.DifficultyRange(difficulty, GREAT_WINDOW_RANGE);
ok = IBeatmapDifficultyInfo.DifficultyRange(difficulty, OK_WINDOW_RANGE);
miss = IBeatmapDifficultyInfo.DifficultyRange(difficulty, MISS_WINDOW_RANGE);
great = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, GREAT_WINDOW_RANGE)) - 0.5;
ok = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, OK_WINDOW_RANGE)) - 0.5;
miss = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, MISS_WINDOW_RANGE)) - 0.5;
}
public override double WindowFor(HitResult result)

View File

@@ -64,11 +64,9 @@ namespace osu.Game.Tests.Gameplay
/// <summary>
/// Tests that a hitobject which provides a custom sample set of 2 retrieves the following samples from the beatmap skin:
/// normal-hitnormal2
/// normal-hitnormal
/// hitnormal
/// </summary>
[TestCase("normal-hitnormal2")]
[TestCase("normal-hitnormal")]
[TestCase("hitnormal")]
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
{
@@ -162,7 +160,6 @@ namespace osu.Game.Tests.Gameplay
/// Tests that a control point that provides a custom sample of 2 causes <see cref="TestDefaultCustomSampleFromBeatmap"/>.
/// </summary>
[TestCase("normal-hitnormal2")]
[TestCase("normal-hitnormal")]
[TestCase("hitnormal")]
public void TestControlPointCustomSampleFromBeatmap(string sampleName)
{

View File

@@ -342,13 +342,14 @@ namespace osu.Game.Tests.Mods
{
foreach (var mod in ruleset.CreateAllMods())
{
if (mod.ValidForFreestyleAsRequiredMod && mod.UserPlayable && !commonAcronyms.Contains(mod.Acronym))
Assert.Fail($"{mod.GetType().ReadableName()} declares {nameof(Mod.ValidForFreestyleAsRequiredMod)} but does not exist in all four basic rulesets!");
if (mod.ValidForFreestyleAsRequiredMod && !mod.UserPlayable)
Assert.Fail($"Mod {mod.GetType().ReadableName()} declares {nameof(Mod.ValidForFreestyleAsRequiredMod)} but is not playable!");
// downgraded to warning, because there are valid reasons why they may still not be specified to be valid for freestyle as required
// (see `TestModsValidForRequiredFreestyleAreConsistentlyCompatibleAcrossRulesets()` test case below).
if (!mod.ValidForFreestyleAsRequiredMod && mod.UserPlayable && commonAcronyms.Contains(mod.Acronym))
Assert.Warn($"{mod.GetType().ReadableName()} does not declare {nameof(Mod.ValidForFreestyleAsRequiredMod)} but exists in all four basic rulesets.");
if (mod.ValidForFreestyleAsRequiredMod && !mod.HasImplementation)
Assert.Fail($"Mod {mod.GetType().ReadableName()} declares {nameof(Mod.ValidForFreestyleAsRequiredMod)} but is not implemented!");
if (mod.ValidForFreestyleAsRequiredMod && mod.UserPlayable && mod.HasImplementation && !commonAcronyms.Contains(mod.Acronym))
Assert.Fail($"{mod.GetType().ReadableName()} declares {nameof(Mod.ValidForFreestyleAsRequiredMod)} but does not exist in all four basic rulesets!");
}
}
});

View File

@@ -32,7 +32,7 @@ using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using osu.Game.Storyboards.Drawables;
using osu.Game.Tests.Resources;
using osuTK;
@@ -325,7 +325,7 @@ namespace osu.Game.Tests.Visual.Background
private void setupUserSettings()
{
AddUntilStep("Song select is current", () => songSelect.IsCurrentScreen());
AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmapInfo != null);
AddUntilStep("Song select has selection", () => songSelect.Carousel?.CurrentSelection != null);
AddStep("Set default user settings", () =>
{
SelectedMods.Value = new[] { new OsuModNoFail() };
@@ -340,7 +340,7 @@ namespace osu.Game.Tests.Visual.Background
rulesets?.Dispose();
}
private partial class DummySongSelect : PlaySongSelect
private partial class DummySongSelect : SoloSongSelect
{
private FadeAccessibleBackground background;
@@ -355,7 +355,7 @@ namespace osu.Game.Tests.Visual.Background
public readonly Bindable<double> DimLevel = new BindableDouble();
public readonly Bindable<double> BlurLevel = new BindableDouble();
public new BeatmapCarousel Carousel => base.Carousel;
public BeatmapCarousel Carousel => this.ChildrenOfType<BeatmapCarousel>().SingleOrDefault();
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
@@ -368,7 +368,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBackgroundDimmed() => background.CurrentColour == OsuColour.Gray(1f - background.CurrentDim);
public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White;
public bool IsBackgroundUndimmed() => background.CurrentColour == new Color4(0.9f, 0.9f, 0.9f, 1f);
public bool IsUserBlurApplied() => Precision.AlmostEquals(background.CurrentBlur, new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR), 0.1f);
@@ -376,7 +376,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBackgroundVisible() => background.CurrentAlpha == 1;
public bool IsBackgroundBlur() => Precision.AlmostEquals(background.CurrentBlur, new Vector2(BACKGROUND_BLUR), 0.1f);
public bool IsBackgroundBlur() => Precision.AlmostBigger(background.CurrentBlur.X, 0, 0.1f);
public bool CheckBackgroundBlur(Vector2 expected) => Precision.AlmostEquals(background.CurrentBlur, expected, 0.1f);

View File

@@ -15,7 +15,7 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Overlays;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
@@ -190,7 +190,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("Set tags again", () => EditorBeatmap.BeatmapInfo.Metadata.Tags = tags_to_discard);
AddStep("Exit editor", () => Editor.Exit());
AddUntilStep("Wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
AddUntilStep("Wait for song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect);
AddAssert("Tags reverted correctly", () => Game.Beatmap.Value.BeatmapInfo.Metadata.Tags == tags_to_save);
}

View File

@@ -14,7 +14,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Visual.Editing
@@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Editing
() => Is.EqualTo(1));
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo?.Invoke());
AddUntilStep("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("entered song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
addStepClickLink("00:00:000 (1)", waitForSeek: false);
AddUntilStep("received 'must be in edit'",
@@ -151,12 +151,12 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("Present beatmap", () => Game.PresentBeatmap(beatmapSet));
AddUntilStep("Wait for song select", () =>
Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet)
&& Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect
&& songSelect.BeatmapSetsLoaded
&& Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect
&& songSelect.CarouselItemsPresented
);
AddStep("Switch ruleset", () => Game.Ruleset.Value = ruleset);
AddStep("Open editor for ruleset", () =>
((PlaySongSelect)Game.ScreenStack.CurrentScreen)
((SoloSongSelect)Game.ScreenStack.CurrentScreen)
.Edit(beatmapSet.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name))
);
AddUntilStep("Wait for editor open", () => editor?.ReadyForUse == true);

View File

@@ -9,7 +9,6 @@ using osu.Game.Audio;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay
@@ -74,8 +73,8 @@ namespace osu.Game.Tests.Visual.Gameplay
//
// We want to keep seeking while asserting various test conditions, so
// continue to seek until we unset the flag.
var gameplayClockContainer = Player.ChildrenOfType<GameplayClockContainer>().First();
gameplayClockContainer.Seek(gameplayClockContainer.CurrentTime > 30000 ? 0 : 60000);
var gameplayClockContainer = Player?.GameplayClockContainer;
gameplayClockContainer?.Seek(gameplayClockContainer.CurrentTime > 30000 ? 0 : 60000);
}
}

View File

@@ -6,11 +6,16 @@ using NUnit.Framework;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
@@ -157,6 +162,51 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("Jumped forwards", () => Player.GameplayClockContainer.CurrentTime - lastTime > 500);
}
[Test]
public void TestReplayDoesNotFailUntilRunningOutOfFrames()
{
var score = new Score
{
ScoreInfo = TestResources.CreateTestScoreInfo(Beatmap.Value.BeatmapInfo),
Replay = new Replay
{
Frames =
{
new OsuReplayFrame(0, Vector2.Zero),
new OsuReplayFrame(10000, Vector2.Zero),
}
}
};
score.ScoreInfo.Mods = [];
score.ScoreInfo.Rank = ScoreRank.F;
AddStep("set global state", () =>
{
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Ruleset.Value = Beatmap.Value.BeatmapInfo.Ruleset;
SelectedMods.Value = score.ScoreInfo.Mods;
});
AddStep("create player", () => Player = new TestReplayPlayer(score, showResults: false));
AddStep("load player", () => LoadScreen(Player));
AddUntilStep("wait for loaded", () => Player.IsCurrentScreen());
AddStep("seek to 8000", () => Player.Seek(8000));
AddUntilStep("wait for fail", () => Player.GameplayState.HasFailed);
AddAssert("player failed after 10000", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(10000));
}
[Test]
public void TestPlayerLoaderSettingsHover()
{
loadPlayerWithBeatmap();
AddUntilStep("wait for settings overlay hidden", () => settingsOverlay().Expanded.Value, () => Is.False);
AddStep("move mouse to right of screen", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopRight));
AddUntilStep("wait for settings overlay visible", () => settingsOverlay().Expanded.Value, () => Is.True);
AddStep("move mouse to centre of screen", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for settings overlay hidden", () => settingsOverlay().Expanded.Value, () => Is.False);
PlayerSettingsOverlay settingsOverlay() => Player.ChildrenOfType<PlayerSettingsOverlay>().Single();
}
private void loadPlayerWithBeatmap(IBeatmap? beatmap = null)
{
AddStep("create player", () =>

View File

@@ -26,8 +26,8 @@ using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.GameplayTest;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Filter;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Resources;
using osuTK.Input;
@@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("exit", () => getEditor().Exit());
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect
&& songSelect.Beatmap.Value is DummyWorkingBeatmap);
}
@@ -171,7 +171,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("switch ruleset at song select", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo);
AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0)));
AddStep("open editor", () => ((SoloSongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0)));
AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse);
AddAssert("editor ruleset is osu!", () => Game.Ruleset.Value, () => Is.EqualTo(new OsuRuleset().RulesetInfo));
@@ -187,8 +187,8 @@ namespace osu.Game.Tests.Visual.Navigation
});
AddAssert("gameplay ruleset is osu!", () => Game.Ruleset.Value, () => Is.EqualTo(new OsuRuleset().RulesetInfo));
AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(PlaySongSelect).Yield()));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(SoloSongSelect).Yield()));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect);
AddAssert("previous ruleset restored", () => Game.Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo));
}
@@ -289,8 +289,8 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("user request play", () => Game.MusicController.Play(requestedByUser: true));
AddUntilStep("music still stopped", () => !Game.MusicController.IsPlaying);
AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(PlaySongSelect).Yield()));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(SoloSongSelect).Yield()));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect);
AddUntilStep("wait for music playing", () => Game.MusicController.IsPlaying);
AddStep("user request stop", () => Game.MusicController.Stop(requestedByUser: true));
@@ -352,13 +352,13 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet));
AddUntilStep("wait for song select",
() => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet)
&& Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect
&& songSelect.BeatmapSetsLoaded);
&& Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect
&& songSelect.CarouselItemsPresented);
}
private void openEditor()
{
AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0)));
AddStep("open editor", () => ((SoloSongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0)));
AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse);
}

View File

@@ -5,7 +5,7 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Navigation
@@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Navigation
InputManager.Key(Key.P);
});
AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect);
}
[Test]
@@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("state is play", () => buttons.State == ButtonSystemState.Play);
AddStep("press P", () => InputManager.Key(Key.P));
AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect);
}
}
}

View File

@@ -15,7 +15,7 @@ using osu.Game.Input.Bindings;
using osu.Game.Overlays.Settings.Sections.Input;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Beatmaps.IO;
using osuTK.Input;
@@ -54,10 +54,10 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
PushAndConfirm(() => new PlaySongSelect());
PushAndConfirm(() => new SoloSongSelect());
AddUntilStep("wait for selection", () => !Game.Beatmap.IsDefault);
AddUntilStep("wait for carousel load", () => songSelect.BeatmapSetsLoaded);
AddUntilStep("wait for carousel load", () => songSelect.CarouselItemsPresented);
AddStep("enter gameplay", () => InputManager.Key(Key.Enter));
@@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Navigation
.AsEnumerable()
.First(k => k.RulesetName == "osu" && k.ActionInt == 0);
private Screens.Select.SongSelect songSelect => Game.ScreenStack.CurrentScreen as Screens.Select.SongSelect;
private SoloSongSelect songSelect => Game.ScreenStack.CurrentScreen as SoloSongSelect;
private Player player => Game.ScreenStack.CurrentScreen as Player;

View File

@@ -5,6 +5,7 @@ using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Game.Configuration;
using osu.Game.Screens.Play;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Beatmaps.IO;
using osuTK.Input;
@@ -83,9 +84,9 @@ namespace osu.Game.Tests.Visual.Navigation
private void loadToPlayerNonBreakTime()
{
Player? player = null;
Screens.Select.SongSelect songSelect = null!;
PushAndConfirm(() => songSelect = new TestSceneScreenNavigation.TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
SoloSongSelect songSelect = null!;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadOszIntoOsu(Game, virtualTrack: true).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);

View File

@@ -17,9 +17,9 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Beatmaps.IO;
using osuTK.Input;
using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation;
namespace osu.Game.Tests.Visual.Navigation
{
@@ -44,17 +44,17 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestPerformAtSongSelect()
{
PushAndConfirm(() => new TestPlaySongSelect());
PushAndConfirm(() => new SoloSongSelect());
AddStep("perform immediately", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(TestPlaySongSelect) }));
AddStep("perform immediately", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(SoloSongSelect) }));
AddAssert("did perform", () => actionPerformed);
AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen is TestPlaySongSelect);
AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen is SoloSongSelect);
}
[Test]
public void TestPerformAtMenuFromSongSelect()
{
PushAndConfirm(() => new TestPlaySongSelect());
PushAndConfirm(() => new SoloSongSelect());
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true));
AddUntilStep("returned to menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
@@ -69,8 +69,8 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("Press enter", () => InputManager.Key(Key.Enter));
AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen is PlayerLoader);
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(TestPlaySongSelect) }));
AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is TestPlaySongSelect);
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(SoloSongSelect) }));
AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect);
AddAssert("did perform", () => actionPerformed);
}
@@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Navigation
private void importAndWaitForSongSelect()
{
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
PushAndConfirm(() => new TestPlaySongSelect());
PushAndConfirm(() => new SoloSongSelect());
AddUntilStep("beatmap updated", () => Game.Beatmap.Value.BeatmapSetInfo.OnlineID == 241526);
}

View File

@@ -16,7 +16,7 @@ using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
namespace osu.Game.Tests.Visual.Navigation
{
@@ -81,11 +81,9 @@ namespace osu.Game.Tests.Visual.Navigation
presentAndConfirm(osuImport);
var maniaImport = importBeatmap(2, new ManiaRuleset().RulesetInfo);
confirmBeatmapInSongSelect(maniaImport);
presentAndConfirm(maniaImport);
var catchImport = importBeatmap(3, new CatchRuleset().RulesetInfo);
confirmBeatmapInSongSelect(catchImport);
presentAndConfirm(catchImport);
// Ruleset is always changed.
@@ -103,11 +101,9 @@ namespace osu.Game.Tests.Visual.Navigation
presentAndConfirm(osuImport);
var maniaImport = importBeatmap(2, new ManiaRuleset().RulesetInfo);
confirmBeatmapInSongSelect(maniaImport);
presentAndConfirm(maniaImport);
var catchImport = importBeatmap(3, new CatchRuleset().RulesetInfo);
confirmBeatmapInSongSelect(catchImport);
presentAndConfirm(catchImport);
// force ruleset to osu!mania
@@ -178,14 +174,14 @@ namespace osu.Game.Tests.Visual.Navigation
{
AddUntilStep("wait for carousel loaded", () =>
{
var songSelect = (Screens.Select.SongSelect)Game.ScreenStack.CurrentScreen;
var songSelect = (SoloSongSelect)Game.ScreenStack.CurrentScreen;
return songSelect.ChildrenOfType<BeatmapCarousel>().SingleOrDefault()?.IsLoaded == true;
});
AddUntilStep("beatmap in song select", () =>
{
var songSelect = (Screens.Select.SongSelect)Game.ScreenStack.CurrentScreen;
return songSelect.ChildrenOfType<BeatmapCarousel>().Single().BeatmapSets.Any(b => b.MatchesOnlineID(getImport()));
var songSelect = (SoloSongSelect)Game.ScreenStack.CurrentScreen;
return songSelect.ChildrenOfType<BeatmapCarousel>().Single().GetCarouselItems()!.Any(i => i.Model is BeatmapSetInfo bsi && bsi.MatchesOnlineID(getImport()));
});
}
@@ -193,7 +189,7 @@ namespace osu.Game.Tests.Visual.Navigation
{
AddStep("present beatmap", () => Game.PresentBeatmap(getImport()));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.OnlineID, () => Is.EqualTo(getImport().OnlineID));
AddAssert("correct ruleset selected", () => Game.Ruleset.Value, () => Is.EqualTo(getImport().Beatmaps.First().Ruleset));
}
@@ -203,7 +199,7 @@ namespace osu.Game.Tests.Visual.Navigation
Predicate<BeatmapInfo> pred = b => b.OnlineID == importedID * 1024 + 2;
AddStep("present difficulty", () => Game.PresentBeatmap(getImport(), pred));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineID, () => Is.EqualTo(importedID * 1024 + 2));
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.OnlineID, () => Is.EqualTo(expectedRulesetOnlineID ?? getImport().Beatmaps.First().Ruleset.OnlineID));
}

View File

@@ -18,7 +18,8 @@ using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using FilterControl = osu.Game.Screens.SelectV2.FilterControl;
namespace osu.Game.Tests.Visual.Navigation
{
@@ -96,9 +97,9 @@ namespace osu.Game.Tests.Visual.Navigation
public void TestFromSongSelectWithFilter([Values] ScorePresentType type)
{
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo?.Invoke());
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
AddStep("filter to nothing", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).FilterControl.CurrentTextSearch.Value = "fdsajkl;fgewq");
AddStep("filter to nothing", () => ((SoloSongSelect)Game.ScreenStack.CurrentScreen).ChildrenOfType<FilterControl>().Single().Search("fdsajkl;fgewq"));
AddUntilStep("wait for no results", () => Beatmap.IsDefault);
var firstImport = importScore(1, new CatchRuleset().RulesetInfo);
@@ -109,7 +110,7 @@ namespace osu.Game.Tests.Visual.Navigation
public void TestFromSongSelectWithConvertRulesetChange([Values] ScorePresentType type)
{
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo?.Invoke());
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
AddStep("set convert to false", () => Game.LocalConfig.SetValue(OsuSetting.ShowConvertedBeatmaps, false));
@@ -121,7 +122,7 @@ namespace osu.Game.Tests.Visual.Navigation
public void TestFromSongSelect([Values] ScorePresentType type)
{
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo?.Invoke());
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
var firstImport = importScore(1);
presentAndConfirm(firstImport, type);
@@ -134,7 +135,7 @@ namespace osu.Game.Tests.Visual.Navigation
public void TestFromSongSelectDifferentRuleset([Values] ScorePresentType type)
{
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo?.Invoke());
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
var firstImport = importScore(1);
presentAndConfirm(firstImport, type);
@@ -147,7 +148,7 @@ namespace osu.Game.Tests.Visual.Navigation
public void TestPresentTwoImportsWithSameOnlineIDButDifferentHashes([Values] ScorePresentType type)
{
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo?.Invoke());
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
var firstImport = importScore(1);
presentAndConfirm(firstImport, type);
@@ -160,7 +161,7 @@ namespace osu.Game.Tests.Visual.Navigation
public void TestScoreRefetchIgnoresEmptyHash()
{
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo?.Invoke());
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is SoloSongSelect songSelect && songSelect.CarouselItemsPresented);
importScore(-1, hash: string.Empty);
importScore(3, hash: @"deadbeef");

View File

@@ -26,7 +26,6 @@ using osu.Game.Graphics.Carousel;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Leaderboards;
using osu.Game.Online.Notifications.WebSocket;
using osu.Game.Online.Notifications.WebSocket.Events;
using osu.Game.Overlays;
@@ -49,20 +48,13 @@ using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Screens.Select.Options;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Beatmaps.IO;
using osu.Game.Tests.Resources;
using osu.Game.Utils;
using osuTK;
using osuTK.Input;
using BeatmapCarousel = osu.Game.Screens.Select.BeatmapCarousel;
using CollectionDropdown = osu.Game.Collections.CollectionDropdown;
using FilterControl = osu.Game.Screens.Select.FilterControl;
using FooterButtonRandom = osu.Game.Screens.Select.FooterButtonRandom;
namespace osu.Game.Tests.Visual.Navigation
{
@@ -146,62 +138,70 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestExitSongSelectWithEscape()
{
TestPlaySongSelect songSelect = null;
SoloSongSelect songSelect = null;
ModSelectOverlay modSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddStep("Show mods overlay", () =>
{
modSelect = songSelect!.ChildrenOfType<ModSelectOverlay>().Single();
modSelect.Show();
});
AddAssert("Overlay was shown", () => modSelect.State.Value == Visibility.Visible);
pushEscape();
AddAssert("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
AddAssert("Overlay was hidden", () => modSelect.State.Value == Visibility.Hidden);
exitViaEscapeAndConfirm();
}
[Test]
public void TestEnterGameplayWhileFilteringToNoSelection()
{
TestPlaySongSelect songSelect = null;
SoloSongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("force selection", () =>
AddStep("force selection and change filter immediately", () =>
{
songSelect.FinaliseSelection();
songSelect.FilterControl.CurrentTextSearch.Value = "test";
InputManager.Key(Key.Enter);
songSelect.ChildrenOfType<FilterControl>().Single().Search("test");
});
AddUntilStep("wait for player", () => !songSelect.IsCurrentScreen());
AddStep("return to song select", () => songSelect.MakeCurrent());
AddUntilStep("wait for selection lost", () => songSelect.Beatmap.IsDefault);
AddUntilStep("selection not lost", () => !songSelect.Beatmap.IsDefault);
AddUntilStep("placeholder visible", () => songSelect.ChildrenOfType<NoResultsPlaceholder>().Single().State.Value, () => Is.EqualTo(Visibility.Visible));
}
[Test]
public void TestSongSelectBackActionHandling()
{
TestPlaySongSelect songSelect = null;
SoloSongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for filter control", () => filterControlTextBox().IsLoaded);
AddStep("set filter", () => filterControlTextBox().Current.Value = "test");
AddStep("press back", () => InputManager.Click(MouseButton.Button1));
AddAssert("still at song select", () => Game.ScreenStack.CurrentScreen == songSelect);
AddAssert("still at song select", () => Game.ScreenStack.CurrentScreen, () => Is.EqualTo(songSelect));
AddAssert("filter cleared", () => string.IsNullOrEmpty(filterControlTextBox().Current.Value));
AddStep("set filter again", () => filterControlTextBox().Current.Value = "test");
AddStep("open collections dropdown", () =>
{
InputManager.MoveMouseTo(songSelect.ChildrenOfType<CollectionDropdown>().Single());
InputManager.MoveMouseTo(songSelect.ChildrenOfType<Screens.SelectV2.CollectionDropdown>().Single());
InputManager.Click(MouseButton.Left);
});
AddStep("press back once", () => InputManager.Click(MouseButton.Button1));
AddAssert("still at song select", () => Game.ScreenStack.CurrentScreen == songSelect);
AddAssert("collections dropdown closed", () => songSelect
.ChildrenOfType<CollectionDropdown>().Single()
.ChildrenOfType<Screens.SelectV2.CollectionDropdown>().Single()
.ChildrenOfType<Dropdown<CollectionFilterMenuItem>.DropdownMenu>().Single().State == MenuState.Closed);
AddStep("press back a second time", () => InputManager.Click(MouseButton.Button1));
@@ -210,17 +210,17 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("press back a third time", () => InputManager.Click(MouseButton.Button1));
ConfirmAtMainMenu();
TextBox filterControlTextBox() => songSelect.ChildrenOfType<FilterControl.FilterControlTextBox>().Single();
FilterControl.SongSelectSearchTextBox filterControlTextBox() => songSelect.ChildrenOfType<FilterControl.SongSelectSearchTextBox>().Single();
}
[Test]
public void TestSongSelectRandomRewindButton()
{
Guid? originalSelection = null;
TestPlaySongSelect songSelect = null;
SoloSongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("Add two beatmaps", () =>
{
@@ -247,41 +247,6 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestSongSelectScrollHandling()
{
TestPlaySongSelect songSelect = null;
double scrollPosition = 0;
AddStep("set game volume to max", () => Game.Dependencies.Get<FrameworkConfigManager>().SetValue(FrameworkSetting.VolumeUniversal, 1d));
AddUntilStep("wait for volume overlay to hide", () => Game.ChildrenOfType<VolumeOverlay>().SingleOrDefault()?.State.Value, () => Is.EqualTo(Visibility.Hidden));
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("store scroll position", () => scrollPosition = getCarouselScrollPosition());
AddStep("move to left side", () => InputManager.MoveMouseTo(
songSelect.ChildrenOfType<Screens.Select.SongSelect.LeftSideInteractionContainer>().Single().ScreenSpaceDrawQuad.TopLeft + new Vector2(1)));
AddStep("scroll down", () => InputManager.ScrollVerticalBy(-1));
AddAssert("carousel didn't move", getCarouselScrollPosition, () => Is.EqualTo(scrollPosition));
AddRepeatStep("alt-scroll down", () =>
{
InputManager.PressKey(Key.AltLeft);
InputManager.ScrollVerticalBy(-1);
InputManager.ReleaseKey(Key.AltLeft);
}, 5);
AddAssert("game volume decreased", () => Game.Dependencies.Get<FrameworkConfigManager>().Get<double>(FrameworkSetting.VolumeUniversal), () => Is.LessThan(1));
AddStep("move to carousel", () => InputManager.MoveMouseTo(songSelect.ChildrenOfType<BeatmapCarousel>().Single()));
AddStep("scroll down", () => InputManager.ScrollVerticalBy(-1));
AddAssert("carousel moved", getCarouselScrollPosition, () => Is.Not.EqualTo(scrollPosition));
double getCarouselScrollPosition() => Game.ChildrenOfType<UserTrackingScrollContainer<DrawableCarouselItem>>().Single().Current;
}
[Test]
public void TestNewSongSelectScrollHandling()
{
SoloSongSelect songSelect = null;
double scrollPosition = 0;
@@ -293,6 +258,8 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for beatmap", () => Game.ChildrenOfType<PanelBeatmapSet>().Any());
// TODO: this logic can likely be removed when we fix https://github.com/ppy/osu/issues/33379
// It should be probably be immediate in this case.
AddWaitStep("wait for scroll", 10);
AddStep("store scroll position", () => scrollPosition = getCarouselScrollPosition());
@@ -325,7 +292,7 @@ namespace osu.Game.Tests.Visual.Navigation
}, 5);
AddAssert("game volume decreased", () => Game.Dependencies.Get<FrameworkConfigManager>().Get<double>(FrameworkSetting.VolumeUniversal), () => Is.LessThan(1));
AddStep("move to carousel", () => InputManager.MoveMouseTo(songSelect.ChildrenOfType<Screens.SelectV2.BeatmapCarousel>().Single()));
AddStep("move to carousel", () => InputManager.MoveMouseTo(songSelect.ChildrenOfType<BeatmapCarousel>().Single()));
AddStep("scroll down", () => InputManager.ScrollVerticalBy(-1));
AddAssert("carousel moved", getCarouselScrollPosition, () => Is.Not.EqualTo(scrollPosition));
@@ -339,21 +306,21 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestOpenModSelectOverlayUsingAction()
{
TestPlaySongSelect songSelect = null;
SoloSongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddStep("Show mods overlay", () => InputManager.Key(Key.F1));
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
AddAssert("Overlay was shown", () => songSelect!.ChildrenOfType<ModSelectOverlay>().Single().State.Value == Visibility.Visible);
}
[Test]
public void TestAttemptPlayBeatmapWrongHashFails()
{
Screens.Select.SongSelect songSelect = null;
Screens.SelectV2.SongSelect songSelect = null;
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely());
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
@@ -384,11 +351,11 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestAttemptPlayBeatmapMissingFails()
{
Screens.Select.SongSelect songSelect = null;
Screens.SelectV2.SongSelect songSelect = null;
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely());
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
@@ -418,9 +385,9 @@ namespace osu.Game.Tests.Visual.Navigation
{
Player player = null;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
Screens.SelectV2.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
@@ -461,9 +428,9 @@ namespace osu.Game.Tests.Visual.Navigation
{
Player player = null;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
Screens.SelectV2.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadOszIntoOsu(Game).WaitSafely());
@@ -515,9 +482,9 @@ namespace osu.Game.Tests.Visual.Navigation
{
Player player = null;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
Screens.SelectV2.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadOszIntoOsu(Game).WaitSafely());
@@ -558,9 +525,9 @@ namespace osu.Game.Tests.Visual.Navigation
{
Player player = null;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
Screens.SelectV2.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
@@ -663,7 +630,7 @@ namespace osu.Game.Tests.Visual.Navigation
playToResults();
ScoreInfo score = null;
LeaderboardScore scorePanel = null;
BeatmapLeaderboardScore scorePanel = null;
AddStep("get score", () => score = ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score);
@@ -672,18 +639,11 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action!.Invoke());
AddStep("show local scores",
() => Game.ChildrenOfType<BeatmapDetailAreaTabControl>().First().Current.Value = new BeatmapDetailAreaLeaderboardTabItem<BeatmapLeaderboardScope>(BeatmapLeaderboardScope.Local));
() => Game.ChildrenOfType<Dropdown<BeatmapLeaderboardScope>>().First().Current.Value = BeatmapLeaderboardScope.Local);
AddUntilStep("wait for score displayed", () => (scorePanel = Game.ChildrenOfType<LeaderboardScore>().FirstOrDefault(s => s.Score.Equals(score))) != null);
AddUntilStep("wait for score displayed", () => (scorePanel = Game.ChildrenOfType<BeatmapLeaderboardScore>().FirstOrDefault(s => s.Score.Equals(score))) != null);
AddStep("open options", () => InputManager.Key(Key.F3));
AddStep("choose clear all scores", () => InputManager.Key(Key.Number4));
AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get<IDialogOverlay>()).IsLoaded);
AddUntilStep("wait for dialog", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog != null);
AddStep("confirm deletion", () => InputManager.Key(Key.Number1));
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog == null);
AddStep("Clear all scores", () => Game.Dependencies.Get<ScoreManager>().Delete());
AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true));
@@ -696,7 +656,7 @@ namespace osu.Game.Tests.Visual.Navigation
playToResults();
ScoreInfo score = null;
LeaderboardScore scorePanel = null;
BeatmapLeaderboardScore scorePanel = null;
AddStep("get score", () => score = ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score);
@@ -705,9 +665,9 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action!.Invoke());
AddStep("show local scores",
() => Game.ChildrenOfType<BeatmapDetailAreaTabControl>().First().Current.Value = new BeatmapDetailAreaLeaderboardTabItem<BeatmapLeaderboardScope>(BeatmapLeaderboardScope.Local));
() => Game.ChildrenOfType<Dropdown<BeatmapLeaderboardScope>>().First().Current.Value = BeatmapLeaderboardScope.Local);
AddUntilStep("wait for score displayed", () => (scorePanel = Game.ChildrenOfType<LeaderboardScore>().FirstOrDefault(s => s.Score.Equals(score))) != null);
AddUntilStep("wait for score displayed", () => (scorePanel = Game.ChildrenOfType<BeatmapLeaderboardScore>().FirstOrDefault(s => s.Score.Equals(score))) != null);
AddStep("right click panel", () =>
{
@@ -718,7 +678,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("click delete", () =>
{
var dropdownItem = Game
.ChildrenOfType<PlayBeatmapDetailArea>().First()
.ChildrenOfType<BeatmapLeaderboardWedge>().First()
.ChildrenOfType<OsuContextMenu>().First()
.ChildrenOfType<DrawableOsuMenuItem>().First(i => i.Item.Text.ToString() == "Delete");
@@ -744,9 +704,9 @@ namespace osu.Game.Tests.Visual.Navigation
IWorkingBeatmap beatmap() => Game.Beatmap.Value;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
Screens.SelectV2.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadOszIntoOsu(Game, virtualTrack: true).WaitSafely());
@@ -777,9 +737,9 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestMenuMakesMusic()
{
TestPlaySongSelect songSelect = null;
SoloSongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for no track", () => Game.MusicController.CurrentTrack.IsDummyDevice);
@@ -791,7 +751,7 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestPushSongSelectAndPressBackButtonImmediately()
{
AddStep("push song select", () => Game.ScreenStack.Push(new TestPlaySongSelect()));
AddStep("push song select", () => Game.ScreenStack.Push(new SoloSongSelect()));
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action!.Invoke());
ConfirmAtMainMenu();
@@ -800,18 +760,23 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestExitSongSelectWithClick()
{
TestPlaySongSelect songSelect = null;
SoloSongSelect songSelect = null;
ModSelectOverlay modSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddStep("Show mods overlay", () =>
{
modSelect = songSelect!.ChildrenOfType<ModSelectOverlay>().Single();
modSelect.Show();
});
AddAssert("Overlay was shown", () => modSelect.State.Value == Visibility.Visible);
AddStep("Move mouse to dimmed area", () => InputManager.MoveMouseTo(new Vector2(
songSelect.ScreenSpaceDrawQuad.TopLeft.X + 1,
songSelect.ScreenSpaceDrawQuad.TopLeft.Y + songSelect.ScreenSpaceDrawQuad.Height / 2)));
AddStep("Click left mouse button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
AddUntilStep("Overlay was hidden", () => modSelect.State.Value == Visibility.Hidden);
exitViaBackButtonAndConfirm();
}
@@ -876,10 +841,18 @@ namespace osu.Game.Tests.Visual.Navigation
{
AddUntilStep("Wait for toolbar to load", () => Game.Toolbar.IsLoaded);
TestPlaySongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
SoloSongSelect songSelect = null;
ModSelectOverlay modSelect = null;
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddStep("Show mods overlay", () =>
{
modSelect = songSelect!.ChildrenOfType<ModSelectOverlay>().Single();
modSelect.Show();
});
AddAssert("Overlay was shown", () => modSelect.State.Value == Visibility.Visible);
AddStep("Show mods overlay", () => modSelect.Show());
AddStep("Change ruleset to osu!taiko", () =>
{
@@ -890,7 +863,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.OnlineID == 1);
AddAssert("Mods overlay still visible", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
AddAssert("Mods overlay still visible", () => modSelect.State.Value == Visibility.Visible);
}
[Test]
@@ -900,10 +873,12 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
TestPlaySongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
SoloSongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("Show options overlay", () => songSelect.BeatmapOptionsOverlay.Show());
AddStep("Show options overlay", () => InputManager.Key(Key.F3));
AddUntilStep("Options overlay visible", () => this.ChildrenOfType<FooterButtonOptions.Popover>().SingleOrDefault()?.State.Value == Visibility.Visible);
AddStep("Change ruleset to osu!taiko", () =>
{
@@ -914,7 +889,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.OnlineID == 1);
AddAssert("Options overlay still visible", () => songSelect.BeatmapOptionsOverlay.State.Value == Visibility.Visible);
AddAssert("Options overlay still visible", () => this.ChildrenOfType<FooterButtonOptions.Popover>().Single().State.Value == Visibility.Visible);
}
[Test]
@@ -1186,7 +1161,7 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestExitGameFromSongSelect()
{
PushAndConfirm(() => new TestPlaySongSelect());
PushAndConfirm(() => new SoloSongSelect());
exitViaEscapeAndConfirm();
pushEscape(); // returns to osu! logo
@@ -1258,10 +1233,10 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("close settings sidebar", () => InputManager.Key(Key.Escape));
Screens.Select.SongSelect songSelect = null;
Screens.SelectV2.SongSelect songSelect = null;
AddRepeatStep("go to solo", () => InputManager.Key(Key.P), 3);
AddUntilStep("wait for song select", () => (songSelect = Game.ScreenStack.CurrentScreen as Screens.Select.SongSelect) != null);
AddUntilStep("wait for beatmap sets loaded", () => songSelect.BeatmapSetsLoaded);
AddUntilStep("wait for song select", () => (songSelect = Game.ScreenStack.CurrentScreen as Screens.SelectV2.SongSelect) != null);
AddUntilStep("wait for beatmap sets loaded", () => songSelect.CarouselItemsPresented);
AddStep("switch to osu! ruleset", () =>
{
@@ -1271,7 +1246,7 @@ namespace osu.Game.Tests.Visual.Navigation
});
AddStep("touch beatmap wedge", () =>
{
var wedge = Game.ChildrenOfType<BeatmapInfoWedge>().Single();
var wedge = Game.ChildrenOfType<BeatmapTitleWedge>().Single();
var touch = new Touch(TouchSource.Touch2, wedge.ScreenSpaceDrawQuad.Centre);
InputManager.BeginTouch(touch);
InputManager.EndTouch(touch);
@@ -1287,7 +1262,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf<ModTouchDevice>());
AddStep("touch beatmap wedge", () =>
{
var wedge = Game.ChildrenOfType<BeatmapInfoWedge>().Single();
var wedge = Game.ChildrenOfType<BeatmapTitleWedge>().Single();
var touch = new Touch(TouchSource.Touch2, wedge.ScreenSpaceDrawQuad.Centre);
InputManager.BeginTouch(touch);
InputManager.EndTouch(touch);
@@ -1304,7 +1279,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("click beatmap wedge", () =>
{
InputManager.MoveMouseTo(Game.ChildrenOfType<BeatmapInfoWedge>().Single());
InputManager.MoveMouseTo(Game.ChildrenOfType<BeatmapTitleWedge>().Single());
InputManager.Click(MouseButton.Left);
});
AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf<ModTouchDevice>());
@@ -1315,7 +1290,7 @@ namespace osu.Game.Tests.Visual.Navigation
{
BeatmapSetInfo beatmapSet = null;
PushAndConfirm(() => new TestPlaySongSelect());
PushAndConfirm(() => new SoloSongSelect());
AddStep("import beatmap", () => beatmapSet = BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely());
AddUntilStep("wait for selected", () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet));
AddStep("select", () => InputManager.Key(Key.Enter));
@@ -1345,9 +1320,9 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestExitSongSelectAndImmediatelyClickLogo()
{
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
Screens.SelectV2.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
@@ -1376,9 +1351,9 @@ namespace osu.Game.Tests.Visual.Navigation
{
BeatmapSetInfo beatmap = null;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
Screens.SelectV2.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
@@ -1407,9 +1382,9 @@ namespace osu.Game.Tests.Visual.Navigation
IWorkingBeatmap beatmap() => Game.Beatmap.Value;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
Screens.SelectV2.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
@@ -1447,12 +1422,5 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
ConfirmAtMainMenu();
}
public partial class TestPlaySongSelect : PlaySongSelect
{
public ModSelectOverlay ModSelectOverlay => ModSelect;
public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions;
}
}
}

View File

@@ -17,6 +17,7 @@ using osu.Framework.Testing;
using osu.Framework.Threading;
using osu.Game.Online.API;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.SkinEditor;
using osu.Game.Rulesets.Mods;
@@ -26,17 +27,19 @@ using osu.Game.Screens.Edit.Components;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osu.Game.Screens.SelectV2;
using osu.Game.Skinning;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Input;
using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation;
namespace osu.Game.Tests.Visual.Navigation
{
public partial class TestSceneSkinEditorNavigation : OsuGameTestScene
{
private TestPlaySongSelect songSelect;
private SoloSongSelect songSelect;
private ModSelectOverlay modSelect => songSelect.ChildrenOfType<ModSelectOverlay>().First();
private SkinEditor skinEditor => Game.ChildrenOfType<SkinEditor>().FirstOrDefault();
[Test]
@@ -331,10 +334,10 @@ namespace osu.Game.Tests.Visual.Navigation
public void TestModOverlayClosesOnOpeningSkinEditor()
{
advanceToSongSelect();
AddStep("open mod overlay", () => songSelect.ModSelectOverlay.Show());
AddStep("open mod overlay", () => modSelect.Show());
openSkinEditor();
AddUntilStep("mod overlay closed", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
AddUntilStep("mod overlay closed", () => modSelect.State.Value == Visibility.Hidden);
}
[Test]
@@ -448,8 +451,8 @@ namespace osu.Game.Tests.Visual.Navigation
private void advanceToSongSelect()
{
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
PushAndConfirm(() => songSelect = new SoloSongSelect());
AddUntilStep("wait for song select", () => songSelect.CarouselItemsPresented);
}
private void openSkinEditor()

View File

@@ -1,548 +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 System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Tests.Resources;
using osu.Game.Users;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
{
public partial class TestSceneBeatmapLeaderboard : OsuManualInputManagerTestScene
{
private readonly FailableLeaderboard leaderboard;
[Cached(typeof(IDialogOverlay))]
private readonly DialogOverlay dialogOverlay;
private ScoreManager scoreManager = null!;
private RulesetStore rulesetStore = null!;
private BeatmapManager beatmapManager = null!;
private PlaySongSelect songSelect = null!;
private LeaderboardManager leaderboardManager = null!;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.Cache(rulesetStore = new RealmRulesetStore(Realm));
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, API));
dependencies.CacheAs<Screens.Select.SongSelect>(songSelect = new PlaySongSelect());
dependencies.Cache(leaderboardManager = new LeaderboardManager());
Dependencies.Cache(Realm);
return dependencies;
}
[BackgroundDependencyLoader]
private void load()
{
LoadComponent(songSelect);
LoadComponent(leaderboardManager);
}
public TestSceneBeatmapLeaderboard()
{
Add(new OsuContextMenuContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
dialogOverlay = new DialogOverlay
{
Depth = -1
},
leaderboard = new FailableLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = BeatmapLeaderboardScope.Global,
}
}
});
}
[Test]
public void TestLocalScoresDisplay()
{
BeatmapInfo beatmapInfo = null!;
AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local);
AddStep(@"Set beatmap", () =>
{
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First();
leaderboard.BeatmapInfo = beatmapInfo;
});
clearScores();
checkDisplayedCount(0);
importMoreScores(() => beatmapInfo);
checkDisplayedCount(10);
importMoreScores(() => beatmapInfo);
checkDisplayedCount(20);
clearScores();
checkDisplayedCount(0);
}
[Test]
public void TestLocalScoresDisplayWorksWhenStartingOffline()
{
BeatmapInfo beatmapInfo = null!;
AddStep("Log out", () => API.Logout());
AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local);
AddStep(@"Set beatmap", () =>
{
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First();
leaderboard.BeatmapInfo = beatmapInfo;
});
clearScores();
importMoreScores(() => beatmapInfo);
checkDisplayedCount(10);
}
[Test]
public void TestLocalScoresDisplayOnBeatmapEdit()
{
BeatmapInfo beatmapInfo = null!;
string originalHash = string.Empty;
AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local);
AddStep(@"Import beatmap", () =>
{
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First();
leaderboard.BeatmapInfo = beatmapInfo;
});
clearScores();
checkDisplayedCount(0);
AddStep(@"Perform initial save to guarantee stable hash", () =>
{
IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap;
beatmapManager.Save(beatmapInfo, beatmap);
originalHash = beatmapInfo.Hash;
});
importMoreScores(() => beatmapInfo);
checkDisplayedCount(10);
checkStoredCount(10);
AddStep(@"Save with changes", () =>
{
IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap;
beatmap.Difficulty.ApproachRate = 12;
beatmapManager.Save(beatmapInfo, beatmap);
});
AddAssert("Hash changed", () => beatmapInfo.Hash, () => Is.Not.EqualTo(originalHash));
checkDisplayedCount(0);
checkStoredCount(10);
importMoreScores(() => beatmapInfo);
importMoreScores(() => beatmapInfo);
checkDisplayedCount(20);
checkStoredCount(30);
AddStep(@"Revert changes", () =>
{
IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap;
beatmap.Difficulty.ApproachRate = 8;
beatmapManager.Save(beatmapInfo, beatmap);
});
AddAssert("Hash restored", () => beatmapInfo.Hash, () => Is.EqualTo(originalHash));
checkDisplayedCount(10);
checkStoredCount(30);
clearScores();
checkDisplayedCount(0);
checkStoredCount(0);
}
[Test]
public void TestGlobalScoresDisplay()
{
AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Global);
AddStep(@"New Scores", () => leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo())));
AddStep(@"New Scores with teams", () => leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo()).Select(s =>
{
s.User.Team = new APITeam();
return s;
})));
}
[Test]
public void TestPersonalBest()
{
AddStep(@"Show personal best", showPersonalBest);
AddStep("null personal best position", showPersonalBestWithNullPosition);
}
[Test]
public void TestPlaceholderStates()
{
AddStep("ensure no scores displayed", () => leaderboard.SetScores(null));
AddStep(@"Network failure", () => leaderboard.SetErrorState(LeaderboardState.NetworkFailure));
AddStep(@"No team", () => leaderboard.SetErrorState(LeaderboardState.NoTeam));
AddStep(@"No supporter", () => leaderboard.SetErrorState(LeaderboardState.NotSupporter));
AddStep(@"Not logged in", () => leaderboard.SetErrorState(LeaderboardState.NotLoggedIn));
AddStep(@"Ruleset unavailable", () => leaderboard.SetErrorState(LeaderboardState.RulesetUnavailable));
AddStep(@"Beatmap unavailable", () => leaderboard.SetErrorState(LeaderboardState.BeatmapUnavailable));
AddStep(@"None selected", () => leaderboard.SetErrorState(LeaderboardState.NoneSelected));
}
[Test]
public void TestUseTheseModsDoesNotCopySystemMods()
{
AddStep(@"set scores", () => leaderboard.SetScores(leaderboard.Scores, new ScoreInfo
{
Position = 999,
Rank = ScoreRank.XH,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Ruleset = new OsuRuleset().RulesetInfo,
Mods = new Mod[] { new OsuModHidden(), new ModScoreV2(), },
User = new APIUser
{
Id = 6602580,
Username = @"waaiiru",
CountryCode = CountryCode.ES,
}
}));
AddUntilStep("wait for scores", () => this.ChildrenOfType<LeaderboardScore>().Count(), () => Is.GreaterThan(0));
AddStep("right click panel", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<LeaderboardScore>().Single());
InputManager.Click(MouseButton.Right);
});
AddStep("click use these mods", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<DrawableOsuMenuItem>().Single());
InputManager.Click(MouseButton.Left);
});
AddAssert("song select received HD", () => songSelect.Mods.Value.Any(m => m is OsuModHidden));
AddAssert("song select did not receive SV2", () => !songSelect.Mods.Value.Any(m => m is ModScoreV2));
}
private void showPersonalBestWithNullPosition()
{
leaderboard.SetScores(leaderboard.Scores, new ScoreInfo
{
Rank = ScoreRank.XH,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock() },
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 6602580,
Username = @"waaiiru",
CountryCode = CountryCode.ES,
},
});
}
private void showPersonalBest()
{
leaderboard.SetScores(leaderboard.Scores, new ScoreInfo
{
Position = 999,
Rank = ScoreRank.XH,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Ruleset = new OsuRuleset().RulesetInfo,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new APIUser
{
Id = 6602580,
Username = @"waaiiru",
CountryCode = CountryCode.ES,
}
});
}
private void importMoreScores(Func<BeatmapInfo> beatmapInfo)
{
AddStep(@"Import new scores", () =>
{
foreach (var score in GenerateSampleScores(beatmapInfo()))
scoreManager.Import(score);
});
}
private void clearScores()
{
AddStep("Clear all scores", () => scoreManager.Delete());
}
private void checkDisplayedCount(int expected) =>
AddUntilStep($"{expected} scores displayed", () => leaderboard.ChildrenOfType<LeaderboardScore>().Count(), () => Is.EqualTo(expected));
private void checkStoredCount(int expected) =>
AddUntilStep($"Total scores stored is {expected}", () => Realm.Run(r => r.All<ScoreInfo>().Count(s => !s.DeletePending)), () => Is.EqualTo(expected));
public static ScoreInfo[] GenerateSampleScores(BeatmapInfo beatmapInfo)
{
return new[]
{
new ScoreInfo
{
Rank = ScoreRank.XH,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now,
Mods = new Mod[]
{
new OsuModHidden(),
new OsuModFlashlight
{
FollowDelay = { Value = 200 },
SizeMultiplier = { Value = 5 },
},
new OsuModDifficultyAdjust
{
CircleSize = { Value = 11 },
ApproachRate = { Value = 10 },
OverallDifficulty = { Value = 10 },
DrainRate = { Value = 10 },
ExtendedLimits = { Value = true }
}
},
Ruleset = new OsuRuleset().RulesetInfo,
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
User = new APIUser
{
Id = 6602580,
Username = @"waaiiru",
CountryCode = CountryCode.ES,
},
},
new ScoreInfo
{
Rank = ScoreRank.X,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddSeconds(-30),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 4608074,
Username = @"Skycries",
CountryCode = CountryCode.BR,
},
},
new ScoreInfo
{
Rank = ScoreRank.SH,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddSeconds(-70),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 1014222,
Username = @"eLy",
CountryCode = CountryCode.JP,
},
},
new ScoreInfo
{
Rank = ScoreRank.S,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddMinutes(-40),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 1541390,
Username = @"Toukai",
CountryCode = CountryCode.CA,
},
},
new ScoreInfo
{
Rank = ScoreRank.A,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddHours(-2),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 2243452,
Username = @"Satoruu",
CountryCode = CountryCode.VE,
},
},
new ScoreInfo
{
Rank = ScoreRank.B,
Accuracy = 0.9826,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddHours(-25),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 2705430,
Username = @"Mooha",
CountryCode = CountryCode.FR,
},
},
new ScoreInfo
{
Rank = ScoreRank.C,
Accuracy = 0.9654,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddHours(-50),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 7151382,
Username = @"Mayuri Hana",
CountryCode = CountryCode.TH,
},
},
new ScoreInfo
{
Rank = ScoreRank.D,
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddHours(-72),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 2051389,
Username = @"FunOrange",
CountryCode = CountryCode.CA,
},
},
new ScoreInfo
{
Rank = ScoreRank.D,
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddMonths(-10),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 6169483,
Username = @"-Hebel-",
CountryCode = CountryCode.MX,
},
},
new ScoreInfo
{
Rank = ScoreRank.D,
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddYears(-2),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 6702666,
Username = @"prhtnsm",
CountryCode = CountryCode.DE,
},
},
};
}
private partial class FailableLeaderboard : BeatmapLeaderboard
{
public new void SetErrorState(LeaderboardState state) => base.SetErrorState(state);
public new void SetScores(IEnumerable<ScoreInfo>? scores, ScoreInfo? userScore = null) => base.SetScores(scores, userScore);
}
}
}

View File

@@ -23,6 +23,7 @@ using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Resources;
using osu.Game.Users;
using osu.Game.Utils;
@@ -248,7 +249,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
AddStep("present beatmap", () => Game.PresentBeatmap(getImport()));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is SoloSongSelect select && select.CarouselItemsPresented);
AddUntilStep("recommended beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineID, () => Is.EqualTo(getImport().Beatmaps[expectedDiff - 1].OnlineID));
}

File diff suppressed because it is too large Load Diff

View File

@@ -263,8 +263,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2
var results = await runGrouping(GroupMode.Difficulty, beatmapSets);
assertGroup(results, 0, "Below 1 Star", new[] { beatmapBelow1 }, ref total);
assertGroup(results, 1, "1 Star", new[] { beatmapAbove1 }, ref total);
assertGroup(results, 2, "2 Stars", new[] { beatmapAlmost2, beatmap2, beatmapAbove2 }, ref total);
assertGroup(results, 1, "1 Star", new[] { beatmapAbove1, beatmapAlmost2 }, ref total);
assertGroup(results, 2, "2 Stars", new[] { beatmap2, beatmapAbove2 }, ref total);
assertGroup(results, 3, "7 Stars", new[] { beatmap7 }, ref total);
assertTotal(results, total);
}

View File

@@ -28,7 +28,6 @@ using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Resources;
using osu.Game.Tests.Visual.SongSelect;
using osu.Game.Users;
using osuTK.Input;
@@ -118,8 +117,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2
{
setScope(BeatmapLeaderboardScope.Global);
AddStep(@"New Scores", () => leaderboard.SetScores(TestSceneBeatmapLeaderboard.GenerateSampleScores(new BeatmapInfo())));
AddStep(@"New Scores with teams", () => leaderboard.SetScores(TestSceneBeatmapLeaderboard.GenerateSampleScores(new BeatmapInfo()).Select(s =>
AddStep(@"New Scores", () => leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo())));
AddStep(@"New Scores with teams", () => leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo()).Select(s =>
{
s.User.Team = new APITeam();
return s;
@@ -150,7 +149,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
[Test]
public void TestUseTheseModsDoesNotCopySystemMods()
{
AddStep(@"set scores", () => leaderboard.SetScores(TestSceneBeatmapLeaderboard.GenerateSampleScores(new BeatmapInfo()), new ScoreInfo
AddStep(@"set scores", () => leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo()), new ScoreInfo
{
OnlineID = 1337,
Position = 999,
@@ -297,7 +296,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
private void showPersonalBestWithNullPosition()
{
leaderboard.SetScores(TestSceneBeatmapLeaderboard.GenerateSampleScores(new BeatmapInfo()), new ScoreInfo
leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo()), new ScoreInfo
{
OnlineID = 1337,
Rank = ScoreRank.XH,
@@ -318,7 +317,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
private void showPersonalBest()
{
leaderboard.SetScores(TestSceneBeatmapLeaderboard.GenerateSampleScores(new BeatmapInfo()), new ScoreInfo
leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo()), new ScoreInfo
{
OnlineID = 1337,
Position = 999,
@@ -347,7 +346,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
{
AddStep(@"Import new scores", () =>
{
foreach (var score in TestSceneBeatmapLeaderboard.GenerateSampleScores(beatmapInfo()))
foreach (var score in GenerateSampleScores(beatmapInfo()))
scoreManager.Import(score);
});
}
@@ -368,5 +367,216 @@ namespace osu.Game.Tests.Visual.SongSelectV2
public new void SetState(LeaderboardState state) => base.SetState(state);
public new void SetScores(IEnumerable<ScoreInfo> scores, ScoreInfo? userScore = null, int? totalCount = null) => base.SetScores(scores, userScore, totalCount);
}
public static ScoreInfo[] GenerateSampleScores(BeatmapInfo beatmapInfo)
{
return new[]
{
new ScoreInfo
{
Rank = ScoreRank.XH,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now,
Mods = new Mod[]
{
new OsuModHidden(),
new OsuModFlashlight
{
FollowDelay = { Value = 200 },
SizeMultiplier = { Value = 5 },
},
new OsuModDifficultyAdjust
{
CircleSize = { Value = 11 },
ApproachRate = { Value = 10 },
OverallDifficulty = { Value = 10 },
DrainRate = { Value = 10 },
ExtendedLimits = { Value = true }
}
},
Ruleset = new OsuRuleset().RulesetInfo,
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
User = new APIUser
{
Id = 6602580,
Username = @"waaiiru",
CountryCode = CountryCode.ES,
},
},
new ScoreInfo
{
Rank = ScoreRank.X,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddSeconds(-30),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 4608074,
Username = @"Skycries",
CountryCode = CountryCode.BR,
},
},
new ScoreInfo
{
Rank = ScoreRank.SH,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddSeconds(-70),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 1014222,
Username = @"eLy",
CountryCode = CountryCode.JP,
},
},
new ScoreInfo
{
Rank = ScoreRank.S,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddMinutes(-40),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 1541390,
Username = @"Toukai",
CountryCode = CountryCode.CA,
},
},
new ScoreInfo
{
Rank = ScoreRank.A,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddHours(-2),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 2243452,
Username = @"Satoruu",
CountryCode = CountryCode.VE,
},
},
new ScoreInfo
{
Rank = ScoreRank.B,
Accuracy = 0.9826,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddHours(-25),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 2705430,
Username = @"Mooha",
CountryCode = CountryCode.FR,
},
},
new ScoreInfo
{
Rank = ScoreRank.C,
Accuracy = 0.9654,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddHours(-50),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 7151382,
Username = @"Mayuri Hana",
CountryCode = CountryCode.TH,
},
},
new ScoreInfo
{
Rank = ScoreRank.D,
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddHours(-72),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 2051389,
Username = @"FunOrange",
CountryCode = CountryCode.CA,
},
},
new ScoreInfo
{
Rank = ScoreRank.D,
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddMonths(-10),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 6169483,
Username = @"-Hebel-",
CountryCode = CountryCode.MX,
},
},
new ScoreInfo
{
Rank = ScoreRank.D,
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
Date = DateTime.Now.AddYears(-2),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
BeatmapHash = beatmapInfo.Hash,
Ruleset = new OsuRuleset().RulesetInfo,
User = new APIUser
{
Id = 6702666,
Username = @"prhtnsm",
CountryCode = CountryCode.DE,
},
},
};
}
}
}

View File

@@ -16,6 +16,8 @@ namespace osu.Game.Beatmaps.Formats
public abstract class LegacyDecoder<T> : Decoder<T>
where T : new()
{
// If this is updated, a new release of `osu-server-beatmap-submission` is required with updated packages.
// See usage at https://github.com/ppy/osu-server-beatmap-submission/blob/master/osu.Server.BeatmapSubmission/Services/BeatmapPackageParser.cs#L96-L97.
public const int LATEST_VERSION = 14;
public const int MAX_COMBO_COLOUR_COUNT = 8;

View File

@@ -18,7 +18,7 @@ namespace osu.Game.Configuration
[LocalisableDescription(typeof(UserInterfaceStrings), nameof(UserInterfaceStrings.BeatmapWithStoryboard))]
BeatmapWithStoryboard,
[Description("Use 'Resource\\Webm\\*.webm' file from local folders")]
[Description("'EzResource/Webm/*.webm' from local folders")]
WebmSource,
}
}

View File

@@ -17,6 +17,7 @@ using osu.Game.Input.Bindings;
using osu.Game.Localisation;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods.Input;
using osu.Game.Overlays.Settings.Sections.Gameplay;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Screens.LAsEzExtensions;
@@ -51,7 +52,6 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.SongSelectGroupMode, GroupMode.None);
SetDefault(OsuSetting.SongSelectSortingMode, SortMode.Title);
SetDefault(OsuSetting.SelectEzMode, EzSelectMode.All);
SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation);
SetDefault(OsuSetting.ModSelectHotkeyStyle, ModSelectHotkeyStyle.Sequential);
@@ -157,10 +157,19 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.FloatingComments, false);
SetDefault(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised);
//新增自定义
SetDefault(OsuSetting.SelectEzMode, EzSelectMode.All);
SetDefault(OsuSetting.HitMode, MUGHitMode.EZ2AC);
SetDefault(OsuSetting.AccuracyCutoffS, 0.95, 0.95, 1, 0.005);
SetDefault(OsuSetting.AccuracyCutoffA, 0.9, 0.9, 1, 0.005);
// SetDefault(OsuSetting.ScrollBaseSpeed, 500, 100, 1000, 1.0);
// SetDefault(OsuSetting.ScrollTimePerSpeed, 5, 1.0, 40, 1.0);
// SetDefault(OsuSetting.ScrollStyle, EzManiaScrollingStyle.ScrollTimeStyleFixed);
// SetDefault(OsuSetting.ScrollPerKeyMode, false);
// SetDefault(OsuSetting.PerspectiveAngle, 90.0f, 30.0f, 90.0f);
SetDefault(OsuSetting.IncreaseFirstObjectVisibility, true);
SetDefault(OsuSetting.GameplayDisableWinKey, true);
@@ -175,8 +184,6 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
SetDefault(OsuSetting.ScreenshotCaptureMenuCursor, false);
SetDefault(OsuSetting.SongSelectRightMouseScroll, true);
SetDefault(OsuSetting.Scaling, ScalingMode.Off);
SetDefault(OsuSetting.ScalingGameMode, ScalingGameMode.Mania);
SetDefault(OsuSetting.SafeAreaConsiderations, true);
@@ -413,19 +420,27 @@ namespace osu.Game.Configuration
Skin,
ScreenshotFormat,
ScreenshotCaptureMenuCursor,
SongSelectRightMouseScroll,
BeatmapSkins,
BeatmapColours,
BeatmapHitsounds,
IncreaseFirstObjectVisibility,
ScoreDisplayMode,
SelectEzMode,
SelectManiaRulesetSubset,
ScalingGameMode,
HitMode,
//自定义
SelectEzMode,
ScalingGameMode,
AccuracyCutoffS,
AccuracyCutoffA,
HitMode,
// //mania用自定义
// SelectManiaRulesetSubset,
// ScrollBaseSpeed,
// ScrollTimePerSpeed,
// ScrollStyle,
//
// PerspectiveAngle,
// ScrollPerKeyMode,
ExternalLinkWarning,
PreferNoVideo,
Scaling,

View File

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Extensions;
@@ -85,6 +86,7 @@ namespace osu.Game.Database
/// </summary>
public void AddFile(TModel item, Stream contents, string filename, Realm realm)
{
filename = filename.ToStandardisedPath();
var existing = item.GetFile(filename);
if (existing != null)

View File

@@ -102,6 +102,8 @@ namespace osu.Game.Database
/// </summary>
private const int schema_version = 49;
public static int SchemaVersion => schema_version;
/// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
/// </summary>
@@ -202,10 +204,10 @@ namespace osu.Game.Database
if (!Filename.EndsWith(realm_extension, StringComparison.Ordinal))
Filename += realm_extension;
#if DEBUG
// #if DEBUG
if (!DebugUtils.IsNUnitRunning)
applyFilenameSchemaSuffix(ref Filename);
#endif
// #endif
// `prepareFirstRealmAccess()` triggers the first `getRealmInstance` call, which will implicitly run realm migrations and bring the schema up-to-date.
using (var realm = prepareFirstRealmAccess())
@@ -543,6 +545,44 @@ namespace osu.Game.Database
return writeTask;
}
/// <summary>
/// Write changes to realm asynchronously, guaranteeing order of execution.
/// </summary>
/// <param name="action">The work to run.</param>
public Task<T> WriteAsync<T>(Func<Realm, T> action)
{
ObjectDisposedException.ThrowIf(isDisposed, this);
// Required to ensure the write is tracked and accounted for before disposal.
// Can potentially be avoided if we have a need to do so in the future.
if (!ThreadSafety.IsUpdateThread)
throw new InvalidOperationException(@$"{nameof(WriteAsync)} must be called from the update thread.");
// CountdownEvent will fail if already at zero.
if (!pendingAsyncWrites.TryAddCount())
pendingAsyncWrites.Reset(1);
// Regardless of calling Realm.GetInstance or Realm.GetInstanceAsync, there is a blocking overhead on retrieval.
// Adding a forced Task.Run resolves this.
var writeTask = Task.Run(async () =>
{
T result;
total_writes_async.Value++;
// Not attempting to use Realm.GetInstanceAsync as there's seemingly no benefit to us (for now) and it adds complexity due to locking
// concerns in getRealmInstance(). On a quick check, it looks to be more suited to cases where realm is connecting to an online sync
// server, which we don't use. May want to report upstream or revisit in the future.
using (var realm = getRealmInstance())
// ReSharper disable once AccessToDisposedClosure (WriteAsync should be marked as [InstantHandle]).
result = await realm.WriteAsync(() => action(realm)).ConfigureAwait(false);
pendingAsyncWrites.Signal();
return result;
});
return writeTask;
}
/// <summary>
/// Subscribe to a realm collection and begin watching for asynchronous changes.
/// </summary>

View File

@@ -14,6 +14,7 @@ using osu.Game.Input.Bindings;
using osu.Game.Models;
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Skinning;
using Realms;
namespace osu.Game.Database
@@ -177,6 +178,7 @@ namespace osu.Game.Database
c.CreateMap<RealmUser, RealmUser>();
c.CreateMap<RealmFile, RealmFile>();
c.CreateMap<RealmNamedFileUsage, RealmNamedFileUsage>();
c.CreateMap<SkinInfo, SkinInfo>();
}
/// <summary>

View File

@@ -307,7 +307,7 @@ namespace osu.Game.Graphics.Carousel
/// <summary>
/// Retrieve a list of all <see cref="CarouselItem"/>s currently displayed.
/// </summary>
protected IReadOnlyCollection<CarouselItem>? GetCarouselItems() => carouselItems;
public IReadOnlyCollection<CarouselItem>? GetCarouselItems() => carouselItems;
private List<CarouselItem>? carouselItems;
@@ -670,7 +670,7 @@ namespace osu.Game.Graphics.Carousel
private void loadSamples(AudioManager audio)
{
sampleKeyboardTraversal = audio.Samples.Get(@"UI/button-hover");
sampleKeyboardTraversal = audio.Samples.Get(@"SongSelect/select-difficulty");
}
private void playTraversalSound()
@@ -1028,7 +1028,7 @@ namespace osu.Game.Graphics.Carousel
/// Implementation of scroll container which handles very large vertical lists by internally using <c>double</c> precision
/// for pre-display Y values.
/// </summary>
protected partial class CarouselScrollContainer : UserTrackingScrollContainer, IKeyBindingHandler<GlobalAction>
public partial class CarouselScrollContainer : UserTrackingScrollContainer, IKeyBindingHandler<GlobalAction>
{
public readonly Container Panels;

View File

@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -41,24 +40,20 @@ namespace osu.Game.Graphics.Containers
RelativeSizeAxes = Axes.Y;
Width = contractedWidth;
FillFlow = new FillFlowContainer
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
};
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = CreateScrollContainer().With(s =>
{
s.RelativeSizeAxes = Axes.Both;
s.ScrollbarVisible = false;
}).WithChild(FillFlow);
}).WithChild(
FillFlow = new FillFlowContainer
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
}
);
}
protected virtual OsuScrollContainer CreateScrollContainer() => new OsuScrollContainer();

View File

@@ -179,7 +179,7 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnMouseDown(MouseDownEvent e)
{
Content.ScaleTo(0.9f, 2000, Easing.OutQuint);
return base.OnMouseDown(e);
return true;
}
protected override void OnMouseUp(MouseUpEvent e)

View File

@@ -154,6 +154,11 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString TimelineShowTimingChanges => new TranslatableString(getKey(@"timeline_show_timing_changes"), @"Show timing changes");
/// <summary>
/// "Finish editing and import changes"
/// </summary>
public static LocalisableString FinishEditingExternally => new TranslatableString(getKey(@"Finish editing and import changes"), @"Finish editing and import changes");
/// <summary>
/// "Show breaks"
/// </summary>

View File

@@ -149,6 +149,11 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString DragAndDropImageInSkinEditor => new TranslatableString(getKey(@"drag_and_drop_image_in_skin_editor"), @"Drag and drop any image into the skin editor to load it in quickly!");
/// <summary>
/// "Try holding your right mouse button near the beatmap carousel to quickly scroll to an absolute position!"
/// </summary>
public static LocalisableString RightMouseAbsoluteScroll => new TranslatableString(getKey(@"right_mouse_absolute_scroll"), @"Try holding your right mouse button near the beatmap carousel to quickly scroll to an absolute position!");
/// <summary>
/// "a tip for you:"
/// </summary>

View File

@@ -19,6 +19,11 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString NoPPForUnrankedMods => new TranslatableString(getKey(@"no_pp_for_unranked_mods"), @"Performance points are not granted for this score because of unranked mods.");
/// <summary>
/// "Performance points are not granted for failed scores."
/// </summary>
public static LocalisableString NoPPForFailedScores => new TranslatableString(getKey(@"no_pp_for_failed_scores"), @"Performance points are not granted for failed scores.");
private static string getKey(string key) => $@"{prefix}:{key}";
}
}

View File

@@ -14,11 +14,6 @@ namespace osu.Game.Localisation.SkinComponents
/// </summary>
public static LocalisableString Attribute => new TranslatableString(getKey(@"attribute"), @"Attribute");
/// <summary>
/// "The attribute to be displayed."
/// </summary>
public static LocalisableString AttributeDescription => new TranslatableString(getKey(@"attribute_description"), @"The attribute to be displayed.");
/// <summary>
/// "Template"
/// </summary>

View File

@@ -14,31 +14,16 @@ namespace osu.Game.Localisation.SkinComponents
/// </summary>
public static LocalisableString SpriteName => new TranslatableString(getKey(@"sprite_name"), @"Sprite name");
/// <summary>
/// "The filename of the sprite"
/// </summary>
public static LocalisableString SpriteNameDescription => new TranslatableString(getKey(@"sprite_name_description"), @"The filename of the sprite");
/// <summary>
/// "Font"
/// </summary>
public static LocalisableString Font => new TranslatableString(getKey(@"font"), @"Font");
/// <summary>
/// "The font to use."
/// </summary>
public static LocalisableString FontDescription => new TranslatableString(getKey(@"font_description"), @"The font to use.");
/// <summary>
/// "Text"
/// </summary>
public static LocalisableString TextElementText => new TranslatableString(getKey(@"text_element_text"), @"Text");
/// <summary>
/// "The text to be displayed."
/// </summary>
public static LocalisableString TextElementTextDescription => new TranslatableString(getKey(@"text_element_text_description"), @"The text to be displayed.");
/// <summary>
/// "Corner radius"
/// </summary>
@@ -54,31 +39,16 @@ namespace osu.Game.Localisation.SkinComponents
/// </summary>
public static LocalisableString ShowLabel => new TranslatableString(getKey(@"show_label"), @"Show label");
/// <summary>
/// "Whether the component&#39;s label should be shown."
/// </summary>
public static LocalisableString ShowLabelDescription => new TranslatableString(getKey(@"show_label_description"), @"Whether the component's label should be shown.");
/// <summary>
/// "Colour"
/// </summary>
public static LocalisableString Colour => new TranslatableString(getKey(@"colour"), @"Colour");
/// <summary>
/// "The colour of the component."
/// </summary>
public static LocalisableString ColourDescription => new TranslatableString(getKey(@"colour_description"), @"The colour of the component.");
/// <summary>
/// "Text colour"
/// </summary>
public static LocalisableString TextColour => new TranslatableString(getKey(@"text_colour"), @"Text colour");
/// <summary>
/// "The colour of the text."
/// </summary>
public static LocalisableString TextColourDescription => new TranslatableString(getKey(@"text_colour_description"), @"The colour of the text.");
/// <summary>
/// "Text weight"
/// </summary>

View File

@@ -227,6 +227,8 @@ namespace osu.Game
private Bindable<string> configSkin;
private RealmDetachedBeatmapStore detachedBeatmapStore;
private readonly string[] args;
private readonly List<OsuFocusedOverlayContainer> focusedOverlays = new List<OsuFocusedOverlayContainer>();
@@ -312,6 +314,8 @@ namespace osu.Game
foreach (var overlay in focusedOverlays)
overlay.Hide();
ScreenFooter.ActiveOverlay?.Hide();
if (hideToolbar) Toolbar.Hide();
}
@@ -1000,6 +1004,10 @@ namespace osu.Game
protected override void Dispose(bool isDisposing)
{
// Without this, tests may deadlock due to cancellation token not becoming cancelled before disposal.
// To reproduce, run `TestSceneButtonSystemNavigation` ensuring `TestConstructor` runs before `TestFastShortcutKeys`.
detachedBeatmapStore?.Dispose();
base.Dispose(isDisposing);
SentryLogger.Dispose();
}
@@ -1243,7 +1251,7 @@ namespace osu.Game
loadComponentSingleFile(new MedalOverlay(), topMostOverlayContent.Add);
loadComponentSingleFile(new BackgroundDataStoreProcessor(), Add);
loadComponentSingleFile<BeatmapStore>(new RealmDetachedBeatmapStore(), Add, true);
loadComponentSingleFile<BeatmapStore>(detachedBeatmapStore = new RealmDetachedBeatmapStore(), Add, true);
Add(externalLinkOpener = new ExternalLinkOpener());
Add(new MusicKeyBindingHandler());

View File

@@ -78,9 +78,9 @@ namespace osu.Game
public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider
{
#if DEBUG
public const string GAME_NAME = "osu! (development)";
public const string GAME_NAME = "ez2osu! (development)";
#else
public const string GAME_NAME = "osu!";
public const string GAME_NAME = "ez2osu!";
#endif
public const string OSU_PROTOCOL = "osu://";

View File

@@ -38,6 +38,7 @@ namespace osu.Game.Overlays.BeatmapSet
public readonly Bindable<APIBeatmap?> Beatmap = new Bindable<APIBeatmap?>();
private APIBeatmapSet? beatmapSet;
private readonly Box background;
public APIBeatmapSet? BeatmapSet
{
@@ -68,12 +69,29 @@ namespace osu.Game.Overlays.BeatmapSet
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
Difficulties = new DifficultiesContainer
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2), Bottom = 10 },
OnLostHover = () => showBeatmap(Beatmap.Value, withStarRating: false),
Children = new Drawable[]
{
new Container
{
Masking = true,
CornerRadius = 10,
RelativeSizeAxes = Axes.Both,
Child = background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f
}
},
Difficulties = new DifficultiesContainer
{
AutoSizeAxes = Axes.Both,
OnLostHover = () => showBeatmap(Beatmap.Value, withStarRating: false),
},
}
},
infoContainer = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 11))
{
@@ -108,9 +126,10 @@ namespace osu.Game.Overlays.BeatmapSet
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
[BackgroundDependencyLoader]
private void load()
private void load(OverlayColourProvider colourProvider)
{
updateDisplay();
background.Colour = colourProvider.Background3;
}
protected override void LoadComplete()
@@ -123,6 +142,12 @@ namespace osu.Game.Overlays.BeatmapSet
Beatmap.TriggerChange();
}
protected override void Update()
{
base.Update();
Difficulties.MaximumSize = new Vector2(DrawWidth, float.MaxValue);
}
private void updateDisplay()
{
Difficulties.Clear();
@@ -240,8 +265,8 @@ namespace osu.Game.Overlays.BeatmapSet
public partial class DifficultySelectorButton : OsuClickableContainer, IStateful<DifficultySelectorState>
{
private const float transition_duration = 100;
private const float size = 54;
private const float background_size = size - 2;
private const float size = 40;
private const float background_size = size - 1;
private readonly Container background;
private readonly Box backgroundBox;
@@ -276,7 +301,6 @@ namespace osu.Game.Overlays.BeatmapSet
{
Beatmap = beatmapInfo;
Size = new Vector2(size);
Margin = new MarginPadding { Horizontal = tile_spacing / 2 };
Children = new Drawable[]
{
@@ -284,7 +308,8 @@ namespace osu.Game.Overlays.BeatmapSet
{
Size = new Vector2(background_size),
Masking = true,
CornerRadius = 4,
CornerRadius = 10,
BorderThickness = 3,
Child = backgroundBox = new Box
{
RelativeSizeAxes = Axes.Both,
@@ -338,6 +363,7 @@ namespace osu.Game.Overlays.BeatmapSet
private void load(OverlayColourProvider colourProvider)
{
backgroundBox.Colour = colourProvider.Background6;
background.BorderColour = colourProvider.Light2;
}
}

View File

@@ -25,7 +25,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Screens;
using osu.Game.Screens.Footer;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Visual;
using osuTK;
@@ -101,11 +101,14 @@ namespace osu.Game.Overlays.FirstRunSetup
}
}
private partial class NestedSongSelect : PlaySongSelect
private partial class NestedSongSelect : SoloSongSelect
{
protected override bool ControlGlobalMusic => false;
public override bool? ApplyModTrackAdjustments => false;
public NestedSongSelect()
{
ControlGlobalMusic = false;
}
}
private partial class UIScaleSlider : RoundedSliderBar<float>
@@ -145,16 +148,17 @@ namespace osu.Game.Overlays.FirstRunSetup
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
new DependencyContainer(new DependencyIsolationContainer(base.CreateChildDependencies(parent)));
private ScreenFooter footer;
[BackgroundDependencyLoader]
private void load(AudioManager audio, TextureStore textures, RulesetStore rulesets)
{
Beatmap.Value = new DummyWorkingBeatmap(audio, textures);
Beatmap.Default = Beatmap.Value = new DummyWorkingBeatmap(audio, textures);
Ruleset.Value = rulesets.AvailableRulesets.First();
OsuScreenStack stack;
OsuLogo logo;
ScreenFooter footer;
Padding = new MarginPadding(5);
@@ -192,6 +196,13 @@ namespace osu.Game.Overlays.FirstRunSetup
// intentionally load synchronously so it is included in the initial load of the first run screen.
stack.PushSynchronously(screen);
}
protected override void LoadComplete()
{
base.LoadComplete();
footer.Show();
}
}
private class DependencyIsolationContainer : IReadOnlyDependencyContainer

View File

@@ -448,7 +448,7 @@ namespace osu.Game.Overlays.Mods
}
else
{
modState.Mod.ResetHitWindows();
modState.Mod.ResetSettingsToDefaults();
modState.Active.Value = false;
}
}

View File

@@ -60,4 +60,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
};
}
}
public enum MUGHitMode
{
Lazer,
EZ2AC,
IIDX,
Melody,
}
}

View File

@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -113,15 +112,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input
AutoSizeAxes = Axes.Y,
}.With(t =>
{
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux)
{
t.NewLine();
var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedString(TabletSettingsStrings.NoTabletDetectedDescription(
RuntimeInfo.OS == RuntimeInfo.Platform.Windows
? @"https://opentabletdriver.net/Wiki/FAQ/Windows"
: @"https://opentabletdriver.net/Wiki/FAQ/Linux")));
t.AddLinks(formattedSource.Text, formattedSource.Links);
}
t.NewLine();
const string url = @"https://opentabletdriver.net/Wiki/FAQ/General";
var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedString(TabletSettingsStrings.NoTabletDetectedDescription(url)));
t.AddLinks(formattedSource.Text, formattedSource.Links);
}),
}
},

View File

@@ -178,9 +178,12 @@ namespace osu.Game.Overlays.Settings.Sections
base.LoadComplete();
currentSkin = skins.CurrentSkin.GetBoundCopy();
currentSkin.BindValueChanged(skin => Enabled.Value = skin.NewValue.SkinInfo.PerformRead(s => !s.Protected), true);
currentSkin.BindValueChanged(_ => updateState());
currentSkin.BindDisabledChanged(_ => updateState(), true);
}
private void updateState() => Enabled.Value = !currentSkin.Disabled && currentSkin.Value.SkinInfo.PerformRead(s => !s.Protected);
public Popover GetPopover()
{
return new RenameSkinPopover();
@@ -206,9 +209,12 @@ namespace osu.Game.Overlays.Settings.Sections
base.LoadComplete();
currentSkin = skins.CurrentSkin.GetBoundCopy();
currentSkin.BindValueChanged(skin => Enabled.Value = skin.NewValue.SkinInfo.PerformRead(s => !s.Protected), true);
currentSkin.BindValueChanged(_ => updateState());
currentSkin.BindDisabledChanged(_ => updateState(), true);
}
private void updateState() => Enabled.Value = !currentSkin.Disabled && currentSkin.Value.SkinInfo.PerformRead(s => !s.Protected);
private void export()
{
try
@@ -244,9 +250,12 @@ namespace osu.Game.Overlays.Settings.Sections
base.LoadComplete();
currentSkin = skins.CurrentSkin.GetBoundCopy();
currentSkin.BindValueChanged(skin => Enabled.Value = skin.NewValue.SkinInfo.PerformRead(s => !s.Protected), true);
currentSkin.BindValueChanged(_ => updateState());
currentSkin.BindDisabledChanged(_ => updateState(), true);
}
private void updateState() => Enabled.Value = !currentSkin.Disabled && currentSkin.Value.SkinInfo.PerformRead(s => !s.Protected);
private void delete()
{
dialogOverlay?.Push(new SkinDeleteDialog(currentSkin.Value));

View File

@@ -0,0 +1,284 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osu.Game.Localisation;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.SkinEditor
{
public partial class ExternalEditOverlay : OsuFocusedOverlayContainer
{
private const double transition_duration = 300;
private FillFlowContainer flow = null!;
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
[Resolved]
private GameHost gameHost { get; set; } = null!;
[Resolved]
private SkinManager skinManager { get; set; } = null!;
private ExternalEditOperation<SkinInfo>? editOperation;
private TaskCompletionSource? taskCompletionSource;
protected override bool DimMainContent => false;
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
InternalChild = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
// Since we're drawing this overlay on top of another overlay (SkinEditor), the dimming effect isn't applied. So we need to add a dimming effect manually.
new Box
{
Colour = Color4.Black.Opacity(0.5f),
RelativeSizeAxes = Axes.Both,
},
new Container
{
Masking = true,
CornerRadius = 20,
AutoSizeAxes = Axes.Both,
AutoSizeDuration = 500,
AutoSizeEasing = Easing.OutQuint,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
Colour = colourProvider.Background5,
RelativeSizeAxes = Axes.Both,
},
flow = new FillFlowContainer
{
Margin = new MarginPadding(20),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Spacing = new Vector2(15),
}
}
}
}
};
}
public async Task<Task> Begin(SkinInfo skinInfo)
{
if (taskCompletionSource != null)
throw new InvalidOperationException("Cannot start multiple concurrent external edits!");
Show();
showSpinner("Mounting external skin...");
setGlobalSkinDisabled(true);
await Task.Delay(500).ConfigureAwait(true);
try
{
editOperation = await skinManager.BeginExternalEditing(skinInfo).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.Log($"Failed to initialize external edit operation: {ex}", LoggingTarget.Database, LogLevel.Error);
setGlobalSkinDisabled(false);
Schedule(() => showSpinner("Export failed!"));
Scheduler.AddDelayed(Hide, 1000);
return Task.FromException(ex);
}
Schedule(() =>
{
flow.Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Skin is mounted externally",
Font = OsuFont.Default.With(size: 30),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
new OsuTextFlowContainer
{
Padding = new MarginPadding(5),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Width = 350,
AutoSizeAxes = Axes.Y,
Text = "Any changes made to the exported folder will be imported to the game, including file additions, modifications and deletions.",
},
new PurpleRoundedButton
{
Text = "Open folder",
Width = 350,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Action = openDirectory,
Enabled = { Value = false }
},
new DangerousRoundedButton
{
Text = EditorStrings.FinishEditingExternally,
Width = 350,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Action = () => finish().FireAndForget(),
Enabled = { Value = false }
}
};
});
Scheduler.AddDelayed(() =>
{
foreach (var b in flow.ChildrenOfType<RoundedButton>())
b.Enabled.Value = true;
openDirectory();
}, 1000);
return (taskCompletionSource = new TaskCompletionSource()).Task;
}
private void openDirectory()
{
if (editOperation == null)
return;
gameHost.OpenFileExternally(editOperation.MountedPath.TrimDirectorySeparator() + Path.DirectorySeparatorChar);
}
private async Task finish()
{
Debug.Assert(taskCompletionSource != null);
showSpinner("Cleaning up...");
await Task.Delay(500).ConfigureAwait(true);
try
{
await editOperation!.Finish().ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.Log($"Failed to finish external edit operation: {ex}", LoggingTarget.Database, LogLevel.Error);
showSpinner("Import failed!");
Scheduler.AddDelayed(Hide, 1000);
setGlobalSkinDisabled(false);
taskCompletionSource.SetException(ex);
taskCompletionSource = null;
return;
}
Schedule(() =>
{
var oldSkin = skinManager.CurrentSkin!.Value;
var newSkinInfo = oldSkin.SkinInfo.PerformRead(s => s);
// Create a new skin instance to ensure the skin is reloaded
// If there's a better way to reload the skin, this should be replaced with it.
setGlobalSkinDisabled(false);
skinManager.CurrentSkin.Value = newSkinInfo.CreateInstance(skinManager);
oldSkin.Dispose();
Hide();
});
taskCompletionSource.SetResult();
taskCompletionSource = null;
}
private void setGlobalSkinDisabled(bool disabled)
{
skinManager.CurrentSkin.Disabled = disabled;
skinManager.CurrentSkinInfo.Disabled = disabled;
}
protected override void PopIn()
{
this.FadeIn(transition_duration, Easing.OutQuint);
}
protected override void PopOut()
{
this.FadeOut(transition_duration, Easing.OutQuint).Finally(_ =>
{
// Set everything to a clean state
editOperation = null;
flow.Children = Array.Empty<Drawable>();
});
}
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
if (e.Repeat)
return false;
switch (e.Action)
{
case GlobalAction.Back:
case GlobalAction.Select:
if (editOperation == null) return base.OnPressed(e);
finish().FireAndForget();
return true;
}
return base.OnPressed(e);
}
private void showSpinner(string text)
{
foreach (var b in flow.ChildrenOfType<RoundedButton>())
b.Enabled.Value = false;
flow.Children = new Drawable[]
{
new OsuSpriteText
{
Text = text,
Font = OsuFont.Default.With(size: 30),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
new LoadingSpinner
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
State = { Value = Visibility.Visible }
},
};
}
}
}

View File

@@ -49,6 +49,8 @@ namespace osu.Game.Overlays.SkinEditor
public readonly BindableList<ISerialisableDrawable> SelectedComponents = new BindableList<ISerialisableDrawable>();
public bool ExternalEditInProgress => externalEditOperation != null && !externalEditOperation.IsCompleted;
protected override bool StartHidden => true;
private Drawable? targetScreen;
@@ -105,6 +107,11 @@ namespace osu.Game.Overlays.SkinEditor
[Resolved]
private IDialogOverlay? dialogOverlay { get; set; }
[Resolved]
private ExternalEditOverlay? externalEditOverlay { get; set; }
private Task? externalEditOperation;
public SkinEditor()
{
}
@@ -160,6 +167,7 @@ namespace osu.Game.Overlays.SkinEditor
{
new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()) { Hotkey = new Hotkey(PlatformAction.Save) },
new EditorMenuItem(CommonStrings.Export, MenuItemType.Standard, () => skins.ExportCurrentSkin()) { Action = { Disabled = !RuntimeInfo.IsDesktop } },
new EditorMenuItem(EditorStrings.EditExternally, MenuItemType.Standard, () => _ = editExternally()) { Action = { Disabled = !RuntimeInfo.IsDesktop } },
new OsuMenuItemSpacer(),
new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new RevertConfirmDialog(revert))),
new OsuMenuItemSpacer(),
@@ -277,6 +285,15 @@ namespace osu.Game.Overlays.SkinEditor
selectedTarget.BindValueChanged(targetChanged, true);
}
private async Task editExternally()
{
Save();
var skin = currentSkin.Value.SkinInfo.PerformRead(s => s.Detach());
externalEditOperation = await externalEditOverlay!.Begin(skin).ConfigureAwait(false);
}
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
{
switch (e.Action)
@@ -512,6 +529,7 @@ namespace osu.Game.Overlays.SkinEditor
private void populateSettings()
{
//过滤选择组件
settingsSidebar.PopulateSettings(content =>
{
foreach (var component in SelectedComponents.OfType<Drawable>())

View File

@@ -28,7 +28,7 @@ using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using osu.Game.Users;
using osu.Game.Utils;
@@ -49,9 +49,15 @@ namespace osu.Game.Overlays.SkinEditor
[Resolved]
private IPerformFromScreenRunner? performer { get; set; }
[Resolved]
private IOverlayManager? overlayManager { get; set; }
[Cached]
public readonly EditorClipboard Clipboard = new EditorClipboard();
[Cached]
private readonly ExternalEditOverlay externalEditOverlay = new ExternalEditOverlay();
[Resolved]
private OsuGame game { get; set; } = null!;
@@ -69,6 +75,7 @@ namespace osu.Game.Overlays.SkinEditor
private OsuScreen? lastTargetScreen;
private InvokeOnDisposal? nestedInputManagerDisable;
private IDisposable? externalEditOverlayRegistration;
private readonly LayoutValue drawSizeLayout;
@@ -86,6 +93,13 @@ namespace osu.Game.Overlays.SkinEditor
config.BindWith(OsuSetting.BeatmapSkins, beatmapSkins);
}
protected override void LoadComplete()
{
base.LoadComplete();
externalEditOverlayRegistration = overlayManager?.RegisterBlockingOverlay(externalEditOverlay);
}
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
switch (e.Action)
@@ -180,7 +194,7 @@ namespace osu.Game.Overlays.SkinEditor
// the validity of the current game-wide beatmap + ruleset combination is enforced by song select.
// if we're anywhere else, the state is unknown and may not make sense, so forcibly set something that does.
if (screen is not PlaySongSelect)
if (screen is not SoloSongSelect)
ruleset.Value = beatmap.Value.BeatmapInfo.Ruleset;
var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod();
@@ -194,7 +208,7 @@ namespace osu.Game.Overlays.SkinEditor
if (replayGeneratingMod != null)
screen.Push(new EndlessPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods)));
}, new[] { typeof(Player), typeof(PlaySongSelect) });
}, new[] { typeof(Player), typeof(SoloSongSelect) });
}
protected override void Update()
@@ -334,6 +348,22 @@ namespace osu.Game.Overlays.SkinEditor
leasedBeatmapSkins = null;
}
public new void ToggleVisibility()
{
if (skinEditor?.ExternalEditInProgress == true)
return;
base.ToggleVisibility();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
externalEditOverlayRegistration?.Dispose();
externalEditOverlayRegistration = null;
}
private partial class EndlessPlayer : ReplayPlayer
{
protected override UserActivity? InitialActivity => null;

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