diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml
new file mode 100644
index 0000000000..4274d01bab
--- /dev/null
+++ b/.github/workflows/test-diffcalc.yml
@@ -0,0 +1,163 @@
+# Listens for new PR comments containing !pp check [id], and runs a diffcalc comparison against master.
+# Usage:
+# !pp check 0 | Runs only the osu! ruleset.
+# !pp check 0 2 | Runs only the osu! and catch rulesets.
+#
+
+name: Diffcalc Consistency Checks
+on:
+ issue_comment:
+ types: [ created ]
+
+env:
+ DB_USER: root
+ DB_HOST: 127.0.0.1
+ CONCURRENCY: 4
+ ALLOW_DOWNLOAD: 1
+ SAVE_DOWNLOADED: 1
+
+jobs:
+ diffcalc:
+ name: Diffcalc
+ runs-on: ubuntu-latest
+ continue-on-error: true
+
+ if: |
+ github.event.issue.pull_request &&
+ contains(github.event.comment.body, '!pp check') &&
+ (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER')
+
+ strategy:
+ fail-fast: false
+ matrix:
+ ruleset:
+ - { name: osu, id: 0 }
+ - { name: taiko, id: 1 }
+ - { name: catch, id: 2 }
+ - { name: mania, id: 3 }
+
+ services:
+ mysql:
+ image: mysql:8.0
+ env:
+ MYSQL_ALLOW_EMPTY_PASSWORD: yes
+ ports:
+ - 3306:3306
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+
+ steps:
+ - name: Verify ruleset
+ if: contains(github.event.comment.body, matrix.ruleset.id) == false
+ run: |
+ echo "${{ github.event.comment.body }} doesn't contain ${{ matrix.ruleset.id }}"
+ exit 1
+
+ - name: Verify MySQL connection from host
+ run: |
+ sudo apt-get install -y mysql-client
+ mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "SHOW DATABASES"
+
+ - name: Create directory structure
+ run: |
+ mkdir -p $GITHUB_WORKSPACE/master/
+ mkdir -p $GITHUB_WORKSPACE/pr/
+
+ # Checkout osu
+ - name: Checkout osu (master)
+ uses: actions/checkout@v2
+ with:
+ repository: ppy/osu
+ path: 'master/osu'
+ - name: Checkout osu (pr)
+ uses: actions/checkout@v2
+ with:
+ path: 'pr/osu'
+
+ # Checkout osu-difficulty-calculator
+ - name: Checkout osu-difficulty-calculator (master)
+ uses: actions/checkout@v2
+ with:
+ repository: ppy/osu-difficulty-calculator
+ path: 'master/osu-difficulty-calculator'
+ - name: Checkout osu-difficulty-calculator (pr)
+ uses: actions/checkout@v2
+ with:
+ repository: ppy/osu-difficulty-calculator
+ path: 'pr/osu-difficulty-calculator'
+
+ - name: Install .NET 5.0.x
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: "5.0.x"
+
+ # Sanity checks to make sure diffcalc is not run when incompatible.
+ - name: Build diffcalc (master)
+ run: |
+ cd $GITHUB_WORKSPACE/master/osu-difficulty-calculator
+ ./UseLocalOsu.sh
+ dotnet build
+ - name: Build diffcalc (pr)
+ run: |
+ cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator
+ ./UseLocalOsu.sh
+ dotnet build
+
+ # Initial data imports
+ - name: Download + import data
+ run: |
+ PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g')
+ BEATMAPS_DATA_NAME=$(curl https://data.ppy.sh/ | grep osu_files | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g')
+
+ # Set env variable for further steps.
+ echo "BEATMAPS_PATH=$GITHUB_WORKSPACE/$BEATMAPS_DATA_NAME" >> $GITHUB_ENV
+
+ cd $GITHUB_WORKSPACE
+
+ wget https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2
+ wget https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2
+ tar -xf $PERFORMANCE_DATA_NAME.tar.bz2
+ tar -xf $BEATMAPS_DATA_NAME.tar.bz2
+
+ cd $GITHUB_WORKSPACE/$PERFORMANCE_DATA_NAME
+
+ mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master"
+ mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr"
+
+ cat *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_master
+ cat *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_pr
+
+ # Run diffcalc
+ - name: Run diffcalc (master)
+ env:
+ DB_NAME: osu_master
+ run: |
+ cd $GITHUB_WORKSPACE/master/osu-difficulty-calculator/osu.Server.DifficultyCalculator
+ dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }}
+ - name: Run diffcalc (pr)
+ env:
+ DB_NAME: osu_pr
+ run: |
+ cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator/osu.Server.DifficultyCalculator
+ dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }}
+
+ # Print diffs
+ - name: Print diffs
+ run: |
+ mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "
+ SELECT
+ m.beatmap_id,
+ m.mods,
+ m.diff_unified as 'sr_master',
+ p.diff_unified as 'sr_pr',
+ (p.diff_unified - m.diff_unified) as 'diff'
+ FROM osu_master.osu_beatmap_difficulty m
+ JOIN osu_pr.osu_beatmap_difficulty p
+ ON m.beatmap_id = p.beatmap_id
+ AND m.mode = p.mode
+ AND m.mods = p.mods
+ WHERE abs(m.diff_unified - p.diff_unified) > 0.1
+ ORDER BY abs(m.diff_unified - p.diff_unified)
+ DESC
+ LIMIT 10000;"
+
+ # Todo: Run ppcalc
\ No newline at end of file
diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml
index 8fa7608b8e..498a710df9 100644
--- a/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml
+++ b/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml
@@ -1,8 +1,8 @@
-
-
-
+
+
+
@@ -14,7 +14,7 @@
-
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 8f922f74a7..786ce2589d 100644
--- a/README.md
+++ b/README.md
@@ -31,12 +31,11 @@ If you are looking to install or test osu! without setting up a development envi
**Latest build:**
-| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
+| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
| ------------- | ------------- | ------------- | ------------- | ------------- |
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.
-- When running on Windows 7 or 8.1, *[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net50&pivots=os-windows#dependencies)** may be required to correctly run .NET 5 applications if your operating system is not up-to-date with the latest service packs.
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
## Developing a custom ruleset
diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
index 3dd6be7307..e28053d0ca 100644
--- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
+++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index 0c4bfe0ed7..027bd0b7e2 100644
--- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
index bb0a487274..e2c715d385 100644
--- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index 0c4bfe0ed7..027bd0b7e2 100644
--- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/osu.Android.props b/osu.Android.props
index 24d07b4588..7378450c38 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,8 +51,8 @@
-
-
+
+
diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs
index 063e02d349..0bcbfc4baf 100644
--- a/osu.Android/OsuGameActivity.cs
+++ b/osu.Android/OsuGameActivity.cs
@@ -20,8 +20,21 @@ namespace osu.Android
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
- [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-archive")]
- [IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed", "application/x-osu-archive" })]
+ [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")]
+ [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")]
+ [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-replay")]
+ [IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[]
+ {
+ "application/zip",
+ "application/octet-stream",
+ "application/download",
+ "application/x-zip",
+ "application/x-zip-compressed",
+ // newer official mime types (see https://osu.ppy.sh/wiki/en/osu%21_File_Formats).
+ "application/x-osu-beatmap-archive",
+ "application/x-osu-skin-archive",
+ "application/x-osu-replay",
+ })]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
public class OsuGameActivity : AndroidGameActivity
{
@@ -66,12 +79,14 @@ namespace osu.Android
case Intent.ActionSendMultiple:
{
var uris = new List();
+
for (int i = 0; i < intent.ClipData?.ItemCount; i++)
{
var content = intent.ClipData?.GetItemAt(i);
if (content != null)
uris.Add(content.Uri);
}
+
handleImportFromUris(uris.ToArray());
break;
}
diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs
index 832d26b0ef..dcb88efeb6 100644
--- a/osu.Desktop/DiscordRichPresence.cs
+++ b/osu.Desktop/DiscordRichPresence.cs
@@ -139,8 +139,8 @@ namespace osu.Desktop
{
switch (activity)
{
- case UserActivity.SoloGame solo:
- return solo.Beatmap.ToString();
+ case UserActivity.InGame game:
+ return game.Beatmap.ToString();
case UserActivity.Editing edit:
return edit.Beatmap.ToString();
diff --git a/osu.Game.Benchmarks/BenchmarkMod.cs b/osu.Game.Benchmarks/BenchmarkMod.cs
new file mode 100644
index 0000000000..c5375e9f09
--- /dev/null
+++ b/osu.Game.Benchmarks/BenchmarkMod.cs
@@ -0,0 +1,34 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using BenchmarkDotNet.Attributes;
+using osu.Game.Rulesets.Osu.Mods;
+
+namespace osu.Game.Benchmarks
+{
+ public class BenchmarkMod : BenchmarkTest
+ {
+ private OsuModDoubleTime mod;
+
+ [Params(1, 10, 100)]
+ public int Times { get; set; }
+
+ public override void SetUp()
+ {
+ base.SetUp();
+ mod = new OsuModDoubleTime();
+ }
+
+ [Benchmark]
+ public int ModHashCode()
+ {
+ var hashCode = new HashCode();
+
+ for (int i = 0; i < Times; i++)
+ hashCode.Add(mod);
+
+ return hashCode.ToHashCode();
+ }
+ }
+}
diff --git a/osu.Game.Benchmarks/BenchmarkRuleset.cs b/osu.Game.Benchmarks/BenchmarkRuleset.cs
new file mode 100644
index 0000000000..2835ec9499
--- /dev/null
+++ b/osu.Game.Benchmarks/BenchmarkRuleset.cs
@@ -0,0 +1,62 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Engines;
+using osu.Game.Online.API;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
+
+namespace osu.Game.Benchmarks
+{
+ public class BenchmarkRuleset : BenchmarkTest
+ {
+ private OsuRuleset ruleset;
+ private APIMod apiModDoubleTime;
+ private APIMod apiModDifficultyAdjust;
+
+ public override void SetUp()
+ {
+ base.SetUp();
+ ruleset = new OsuRuleset();
+ apiModDoubleTime = new APIMod { Acronym = "DT" };
+ apiModDifficultyAdjust = new APIMod { Acronym = "DA" };
+ }
+
+ [Benchmark]
+ public void BenchmarkToModDoubleTime()
+ {
+ apiModDoubleTime.ToMod(ruleset);
+ }
+
+ [Benchmark]
+ public void BenchmarkToModDifficultyAdjust()
+ {
+ apiModDifficultyAdjust.ToMod(ruleset);
+ }
+
+ [Benchmark]
+ public void BenchmarkGetAllMods()
+ {
+ ruleset.CreateAllMods().Consume(new Consumer());
+ }
+
+ [Benchmark]
+ public void BenchmarkGetAllModsForReference()
+ {
+ ruleset.AllMods.Consume(new Consumer());
+ }
+
+ [Benchmark]
+ public void BenchmarkGetForAcronym()
+ {
+ ruleset.CreateModFromAcronym("DT");
+ }
+
+ [Benchmark]
+ public void BenchmarkGetForType()
+ {
+ ruleset.CreateMod();
+ }
+ }
+}
diff --git a/osu.Game.Benchmarks/Program.cs b/osu.Game.Benchmarks/Program.cs
index c55075fea6..439ced53ab 100644
--- a/osu.Game.Benchmarks/Program.cs
+++ b/osu.Game.Benchmarks/Program.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
namespace osu.Game.Benchmarks
@@ -11,7 +12,7 @@ namespace osu.Game.Benchmarks
{
BenchmarkSwitcher
.FromAssembly(typeof(Program).Assembly)
- .Run(args);
+ .Run(args, DefaultConfig.Instance.WithOption(ConfigOptions.DisableOptimizationsValidator, true));
}
}
}
diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
index da8a0540f4..03f39f226c 100644
--- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
+++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs
index f5ef5c5e18..5e73a89069 100644
--- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs
@@ -210,9 +210,9 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
new Vector2(50, 200),
}), 0.5);
AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count);
- AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.PerfectCurve);
+ AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve);
addAddVertexSteps(150, 150);
- AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.Linear);
+ AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.Linear);
}
private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () =>
diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs
index 5e4b6d9e1a..8fa96fb8c9 100644
--- a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs
+++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs
@@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests
} while (rng.Next(2) != 0);
int length = sliderPath.ControlPoints.Count - start + 1;
- sliderPath.ControlPoints[start].Type.Value = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
+ sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
} while (rng.Next(3) != 0);
if (rng.Next(5) == 0)
@@ -210,13 +210,13 @@ namespace osu.Game.Rulesets.Catch.Tests
path.ConvertToSliderPath(sliderPath, sliderStartY);
Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3));
- Assert.That(sliderPath.ControlPoints[0].Position.Value.X, Is.EqualTo(path.Vertices[0].X));
+ Assert.That(sliderPath.ControlPoints[0].Position.X, Is.EqualTo(path.Vertices[0].X));
assertInvariants(path.Vertices, true);
foreach (var point in sliderPath.ControlPoints)
{
- Assert.That(point.Type.Value, Is.EqualTo(PathType.Linear).Or.Null);
- Assert.That(sliderStartY + point.Position.Value.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
+ Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null);
+ Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
}
for (int i = 0; i < 10; i++)
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index 484da8e22e..6457ec92da 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
index 3a5322ce82..a891ec6c0a 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
@@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
case JuiceStream juiceStream:
// Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead.
- lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.Value.X;
+ lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.X;
// Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead.
lastStartTime = juiceStream.StartTime;
diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs
index 8aaeef045f..1a43a10c81 100644
--- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs
+++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs
@@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
path.ConvertFromSliderPath(sliderPath);
// If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices.
- if (sliderPath.ControlPoints.Any(p => p.Type.Value != null && p.Type.Value != PathType.Linear))
+ if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear))
{
path.ResampleVertices(hitObject.NestedHitObjects
.Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used.
diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs
index 36072d7fcb..8cb0804ab7 100644
--- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs
+++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs
@@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Catch.Edit
juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX);
foreach (var point in juiceStream.Path.ControlPoints)
- point.Position.Value *= new Vector2(-1, 1);
+ point.Position *= new Vector2(-1, 1);
EditorBeatmap.Update(juiceStream);
return true;
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs
index 932c8cad85..a97e940a64 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs
@@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Catch.Mods
///
private static void mirrorJuiceStreamPath(JuiceStream juiceStream)
{
- var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
+ var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
foreach (var point in controlPoints)
- point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y);
+ point.Position = new Vector2(-point.Position.X, point.Position.Y);
juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value);
}
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 3088d024d1..a8ad34fcbe 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.Objects
if (value != null)
{
- path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
+ path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs
index f1cdb39e91..7207833fe6 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs
@@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Catch.Objects
for (int i = 1; i < vertices.Count; i++)
{
- sliderPath.ControlPoints[^1].Type.Value = PathType.Linear;
+ sliderPath.ControlPoints[^1].Type = PathType.Linear;
float deltaX = vertices[i].X - lastPosition.X;
double length = vertices[i].Distance - currentDistance;
diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs
new file mode 100644
index 0000000000..0f520215a1
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs
@@ -0,0 +1,64 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Screens.Edit;
+using osu.Game.Screens.Edit.Compose;
+using osu.Game.Skinning;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Mania.Tests.Editor
+{
+ public class TestSceneManiaComposeScreen : EditorClockTestScene
+ {
+ [Resolved]
+ private SkinManager skins { get; set; }
+
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("setup compose screen", () =>
+ {
+ var editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }))
+ {
+ BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
+ };
+
+ Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
+
+ Child = new DependencyProvidingContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ CachedDependencies = new (Type, object)[]
+ {
+ (typeof(EditorBeatmap), editorBeatmap),
+ (typeof(IBeatSnapProvider), editorBeatmap),
+ },
+ Child = new ComposeScreen { State = { Value = Visibility.Visible } },
+ };
+ });
+
+ AddUntilStep("wait for composer", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded == true);
+ }
+
+ [Test]
+ public void TestDefaultSkin()
+ {
+ AddStep("set default skin", () => skins.CurrentSkinInfo.Value = SkinInfo.Default);
+ }
+
+ [Test]
+ public void TestLegacySkin()
+ {
+ AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.Info);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index 6df555617b..674a22df98 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs
new file mode 100644
index 0000000000..a206aafb8a
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs
@@ -0,0 +1,46 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Screens.Edit.Setup;
+
+namespace osu.Game.Rulesets.Mania.Edit.Setup
+{
+ public class ManiaSetupSection : RulesetSetupSection
+ {
+ private LabelledSwitchButton specialStyle;
+
+ public ManiaSetupSection()
+ : base(new ManiaRuleset().RulesetInfo)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Children = new Drawable[]
+ {
+ specialStyle = new LabelledSwitchButton
+ {
+ Label = "Use special (N+1) style",
+ Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 5k (4+1) or 8key (7+1) configurations.",
+ Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ specialStyle.Current.BindValueChanged(_ => updateBeatmap());
+ }
+
+ private void updateBeatmap()
+ {
+ Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index f4b6e10af4..1f79dae280 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -27,11 +27,13 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Edit;
+using osu.Game.Rulesets.Mania.Edit.Setup;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Skinning.Legacy;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Scoring;
+using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Ranking.Statistics;
namespace osu.Game.Rulesets.Mania
@@ -390,6 +392,8 @@ namespace osu.Game.Rulesets.Mania
{
return new ManiaFilterCriteria();
}
+
+ public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection();
}
public enum PlayfieldType
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index d1310d42eb..2923a2af2f 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -275,9 +275,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return false;
beginHoldAt(Time.Current - Head.HitObject.StartTime);
- Head.UpdateResult();
- return true;
+ return Head.UpdateResult();
}
private void beginHoldAt(double timeOffset)
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
index be600f0d47..8458345998 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Origin = Anchor.TopCentre;
}
- public void UpdateResult() => base.UpdateResult(true);
+ public bool UpdateResult() => base.UpdateResult(true);
protected override void UpdateInitialTransforms()
{
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
index 5aff4e200b..9ac223a0d7 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
@@ -6,7 +6,6 @@ using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Rulesets.Mania.UI;
@@ -29,11 +28,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
[Resolved(canBeNull: true)]
private ManiaPlayfield playfield { get; set; }
- ///
- /// Gets the samples that are played by this object during gameplay.
- ///
- public ISampleInfo[] GetGameplaySamples() => Samples.Samples;
-
protected override float SamplePlaybackPosition
{
get
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 9b5893b268..f5e30efd91 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Linq;
using osuTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -19,6 +18,7 @@ using osuTK;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.UI
{
@@ -28,12 +28,6 @@ namespace osu.Game.Rulesets.Mania.UI
public const float COLUMN_WIDTH = 80;
public const float SPECIAL_COLUMN_WIDTH = 70;
- ///
- /// For hitsounds played by this (i.e. not as a result of hitting a hitobject),
- /// a certain number of samples are allowed to be played concurrently so that it feels better when spam-pressing the key.
- ///
- private const int max_concurrent_hitsounds = OsuGameBase.SAMPLE_CONCURRENCY;
-
///
/// The index of this column as part of the whole playfield.
///
@@ -45,10 +39,10 @@ namespace osu.Game.Rulesets.Mania.UI
internal readonly Container TopLevelContainer;
private readonly DrawablePool hitExplosionPool;
private readonly OrderedHitPolicy hitPolicy;
- private readonly Container hitSounds;
-
public Container UnderlayElements => HitObjectArea.UnderlayElements;
+ private readonly GameplaySampleTriggerSource sampleTriggerSource;
+
public Column(int index)
{
Index = index;
@@ -64,6 +58,7 @@ namespace osu.Game.Rulesets.Mania.UI
InternalChildren = new[]
{
hitExplosionPool = new DrawablePool(5),
+ sampleTriggerSource = new GameplaySampleTriggerSource(HitObjectContainer),
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
background.CreateProxy(),
HitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both },
@@ -72,12 +67,6 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Both
},
background,
- hitSounds = new Container
- {
- Name = "Column samples pool",
- RelativeSizeAxes = Axes.Both,
- Children = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new SkinnableSound()).ToArray()
- },
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
};
@@ -133,29 +122,12 @@ namespace osu.Game.Rulesets.Mania.UI
HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result)));
}
- private int nextHitSoundIndex;
-
public bool OnPressed(ManiaAction action)
{
if (action != Action.Value)
return false;
- var nextObject =
- HitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ??
- // fallback to non-alive objects to find next off-screen object
- HitObjectContainer.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ??
- HitObjectContainer.Objects.LastOrDefault();
-
- if (nextObject is DrawableManiaHitObject maniaObject)
- {
- var hitSound = hitSounds[nextHitSoundIndex];
-
- hitSound.Samples = maniaObject.GetGameplaySamples();
- hitSound.Play();
-
- nextHitSoundIndex = (nextHitSoundIndex + 1) % max_concurrent_hitsounds;
- }
-
+ sampleTriggerSource.Play();
return true;
}
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
index 34d972e60f..8581f016b1 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
@@ -37,12 +37,11 @@ namespace osu.Game.Rulesets.Mania.UI
public override void PlayAnimation()
{
- base.PlayAnimation();
-
switch (Result)
{
case HitResult.None:
case HitResult.Miss:
+ base.PlayAnimation();
break;
default:
@@ -52,6 +51,8 @@ namespace osu.Game.Rulesets.Mania.UI
this.Delay(50)
.ScaleTo(0.75f, 250)
.FadeOut(200);
+
+ // osu!mania uses a custom fade length, so the base call is intentionally omitted.
break;
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
index 35b79aa8ac..5a1aa42ed1 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddAssert("last connection displayed", () =>
{
- var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position.Value == new Vector2(300));
+ var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position == new Vector2(300));
return lastConnection.DrawWidth > 50;
});
}
@@ -166,14 +166,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
AddStep($"move mouse to control point {index}", () =>
{
- Vector2 position = slider.Path.ControlPoints[index].Position.Value;
+ Vector2 position = slider.Path.ControlPoints[index].Position;
InputManager.MoveMouseTo(visualiser.Pieces[0].Parent.ToScreenSpace(position));
});
}
private void assertControlPointPathType(int controlPointIndex, PathType? type)
{
- AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type.Value == type);
+ AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type == type);
}
private void addContextMenuItemStep(string contextMenuText)
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs
index 24b947c854..6bfe7f892b 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs
@@ -108,9 +108,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
[Test]
public void TestDragControlPointPathAfterChangingType()
{
- AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type.Value = PathType.Bezier);
+ AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.Bezier);
AddStep("add point", () => slider.Path.ControlPoints.Add(new PathControlPoint(new Vector2(500, 10))));
- AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type.Value = PathType.PerfectCurve);
+ AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PerfectCurve);
moveMouseToControlPoint(4);
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
@@ -137,15 +137,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
AddStep($"move mouse to control point {index}", () =>
{
- Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value;
+ Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position;
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
});
}
- private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => slider.Path.ControlPoints[index].Type.Value == type);
+ private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => slider.Path.ControlPoints[index].Type == type);
private void assertControlPointPosition(int index, Vector2 position) =>
- AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, slider.Path.ControlPoints[index].Position.Value, 1));
+ AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, slider.Path.ControlPoints[index].Position, 1));
private class TestSliderBlueprint : SliderSelectionBlueprint
{
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs
index 8235e1bc79..e724015905 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs
@@ -385,10 +385,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected);
- private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type.Value == type);
+ private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type == type);
private void assertControlPointPosition(int index, Vector2 position) =>
- AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position.Value, 1));
+ AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position, 1));
private Slider getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null;
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs
index 0d828a79c8..cc43eb3852 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs
@@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
AddStep($"move mouse to control point {index}", () =>
{
- Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value;
+ Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position;
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
});
}
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index 68be34d153..f5f1159542 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs
index eb7011e8b0..d66c9ea4bf 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
///
private void updateConnectingPath()
{
- Position = slider.StackedPosition + ControlPoint.Position.Value;
+ Position = slider.StackedPosition + ControlPoint.Position;
path.ClearVertices();
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return;
path.AddVertex(Vector2.Zero);
- path.AddVertex(slider.Path.ControlPoints[nextIndex].Position.Value - ControlPoint.Position.Value);
+ path.AddVertex(slider.Path.ControlPoints[nextIndex].Position - ControlPoint.Position);
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
index 5b476526c9..2cc95e1891 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
@@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
private IBindable sliderPosition;
private IBindable sliderScale;
- private IBindable controlPointPosition;
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
{
@@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
updatePathType();
});
- controlPoint.Type.BindValueChanged(_ => updateMarkerDisplay());
+ controlPoint.Changed += updateMarkerDisplay;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
@@ -117,9 +116,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
sliderPosition = slider.PositionBindable.GetBoundCopy();
sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
- controlPointPosition = ControlPoint.Position.GetBoundCopy();
- controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
-
sliderScale = slider.ScaleBindable.GetBoundCopy();
sliderScale.BindValueChanged(_ => updateMarkerDisplay());
@@ -174,8 +170,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
if (e.Button == MouseButton.Left)
{
- dragStartPosition = ControlPoint.Position.Value;
- dragPathType = PointsInSegment[0].Type.Value;
+ dragStartPosition = ControlPoint.Position;
+ dragPathType = PointsInSegment[0].Type;
changeHandler?.BeginChange();
return true;
@@ -186,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
protected override void OnDrag(DragEvent e)
{
- Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position.Value).ToArray();
+ Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray();
var oldPosition = slider.Position;
var oldStartTime = slider.StartTime;
@@ -202,15 +198,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)
- slider.Path.ControlPoints[i].Position.Value -= movementDelta;
+ slider.Path.ControlPoints[i].Position -= movementDelta;
}
else
- ControlPoint.Position.Value = dragStartPosition + (e.MousePosition - e.MouseDownPosition);
+ ControlPoint.Position = dragStartPosition + (e.MousePosition - e.MouseDownPosition);
if (!slider.Path.HasValidLength)
{
for (var i = 0; i < slider.Path.ControlPoints.Count; i++)
- slider.Path.ControlPoints[i].Position.Value = oldControlPoints[i];
+ slider.Path.ControlPoints[i].Position = oldControlPoints[i];
slider.Position = oldPosition;
slider.StartTime = oldStartTime;
@@ -218,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
}
// Maintain the path type in case it got defaulted to bezier at some point during the drag.
- PointsInSegment[0].Type.Value = dragPathType;
+ PointsInSegment[0].Type = dragPathType;
}
protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange();
@@ -230,19 +226,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
///
private void updatePathType()
{
- if (ControlPoint.Type.Value != PathType.PerfectCurve)
+ if (ControlPoint.Type != PathType.PerfectCurve)
return;
if (PointsInSegment.Count > 3)
- ControlPoint.Type.Value = PathType.Bezier;
+ ControlPoint.Type = PathType.Bezier;
if (PointsInSegment.Count != 3)
return;
- ReadOnlySpan points = PointsInSegment.Select(p => p.Position.Value).ToArray();
+ ReadOnlySpan points = PointsInSegment.Select(p => p.Position).ToArray();
RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points);
if (boundingBox.Width >= 640 || boundingBox.Height >= 480)
- ControlPoint.Type.Value = PathType.Bezier;
+ ControlPoint.Type = PathType.Bezier;
}
///
@@ -250,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
///
private void updateMarkerDisplay()
{
- Position = slider.StackedPosition + ControlPoint.Position.Value;
+ Position = slider.StackedPosition + ControlPoint.Position;
markerRing.Alpha = IsSelected.Value ? 1 : 0;
@@ -265,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
private Color4 getColourFromNodeType()
{
- if (!(ControlPoint.Type.Value is PathType pathType))
+ if (!(ControlPoint.Type is PathType pathType))
return colours.Yellow;
switch (pathType)
@@ -284,6 +280,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
}
}
- public LocalisableString TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty;
+ public LocalisableString TooltipText => ControlPoint.Type.ToString() ?? string.Empty;
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
index 5bbdf9688f..6269a41350 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -173,12 +173,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
int thirdPointIndex = indexInSegment + 2;
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
- piece.PointsInSegment[thirdPointIndex].Type.Value = piece.PointsInSegment[0].Type.Value;
+ piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type;
break;
}
- piece.ControlPoint.Type.Value = type;
+ piece.ControlPoint.Type = type;
}
[Resolved(CanBeNull = true)]
@@ -241,7 +241,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
private MenuItem createMenuItemForPathType(PathType? type)
{
int totalCount = Pieces.Count(p => p.IsSelected.Value);
- int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type.Value == type);
+ int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == type);
var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.ToString().Humanize(), MenuItemType.Standard, _ =>
{
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
index 8b20df9a68..b9e4ed6fcb 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
@@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
Debug.Assert(lastPoint != null);
segmentStart = lastPoint;
- segmentStart.Type.Value = PathType.Linear;
+ segmentStart.Type = PathType.Linear;
currentSegmentLength = 1;
}
@@ -153,15 +153,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
case 1:
case 2:
- segmentStart.Type.Value = PathType.Linear;
+ segmentStart.Type = PathType.Linear;
break;
case 3:
- segmentStart.Type.Value = PathType.PerfectCurve;
+ segmentStart.Type = PathType.PerfectCurve;
break;
default:
- segmentStart.Type.Value = PathType.Bezier;
+ segmentStart.Type = PathType.Bezier;
break;
}
}
@@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
// The cursor does not overlap a previous control point, so it can be added if not already existing.
if (cursor == null)
{
- HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = { Value = Vector2.Zero } });
+ HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = Vector2.Zero });
// The path type should be adjusted in the progression of updatePathType() (Linear -> PC -> Bezier).
currentSegmentLength++;
@@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
}
// Update the cursor position.
- cursor.Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position;
+ cursor.Position = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position;
}
else if (cursor != null)
{
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
index e810d2fe0c..89724876fa 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
@@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
Debug.Assert(placementControlPointIndex != null);
- HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position;
+ HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position = e.MousePosition - HitObject.Position;
}
protected override void OnDragEnd(DragEndEvent e)
@@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
for (int i = 0; i < controlPoints.Count - 1; i++)
{
- float dist = new Line(controlPoints[i].Position.Value, controlPoints[i + 1].Position.Value).DistanceToPoint(position);
+ float dist = new Line(controlPoints[i].Position, controlPoints[i + 1].Position).DistanceToPoint(position);
if (dist < minDistance)
{
@@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
}
// Move the control points from the insertion index onwards to make room for the insertion
- controlPoints.Insert(insertionIndex, new PathControlPoint { Position = { Value = position } });
+ controlPoints.Insert(insertionIndex, new PathControlPoint { Position = position });
return insertionIndex;
}
@@ -207,8 +207,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
// The first control point in the slider must have a type, so take it from the previous "first" one
// Todo: Should be handled within SliderPath itself
- if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type.Value == null)
- controlPoints[1].Type.Value = controlPoints[0].Type.Value;
+ if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type == null)
+ controlPoints[1].Type = controlPoints[0].Type;
controlPoints.Remove(c);
}
@@ -222,9 +222,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
// The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position
// So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0)
- Vector2 first = controlPoints[0].Position.Value;
+ Vector2 first = controlPoints[0].Position;
foreach (var c in controlPoints)
- c.Position.Value -= first;
+ c.Position -= first;
HitObject.Position += first;
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
index 358a44e0e6..4a57d36eb4 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
@@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Osu.Edit
{
foreach (var point in slider.Path.ControlPoints)
{
- point.Position.Value = new Vector2(
- (direction == Direction.Horizontal ? -1 : 1) * point.Position.Value.X,
- (direction == Direction.Vertical ? -1 : 1) * point.Position.Value.Y
+ point.Position = new Vector2(
+ (direction == Direction.Horizontal ? -1 : 1) * point.Position.X,
+ (direction == Direction.Vertical ? -1 : 1) * point.Position.Y
);
}
}
@@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Edit
if (h is IHasPath path)
{
foreach (var point in path.Path.ControlPoints)
- point.Position.Value = RotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta);
+ point.Position = RotatePointAroundOrigin(point.Position, Vector2.Zero, delta);
}
}
@@ -163,9 +163,9 @@ namespace osu.Game.Rulesets.Osu.Edit
private void scaleSlider(Slider slider, Vector2 scale)
{
- referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type.Value).ToList();
+ referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type).ToList();
- Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value));
+ Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position));
// Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0.
scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size;
@@ -178,13 +178,13 @@ namespace osu.Game.Rulesets.Osu.Edit
foreach (var point in slider.Path.ControlPoints)
{
- oldControlPoints.Enqueue(point.Position.Value);
- point.Position.Value *= pathRelativeDeltaScale;
+ oldControlPoints.Enqueue(point.Position);
+ point.Position *= pathRelativeDeltaScale;
}
// Maintain the path types in case they were defaulted to bezier at some point during scaling
for (int i = 0; i < slider.Path.ControlPoints.Count; ++i)
- slider.Path.ControlPoints[i].Type.Value = referencePathTypes[i];
+ slider.Path.ControlPoints[i].Type = referencePathTypes[i];
//if sliderhead or sliderend end up outside playfield, revert scaling.
Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider });
@@ -194,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Edit
return;
foreach (var point in slider.Path.ControlPoints)
- point.Position.Value = oldControlPoints.Dequeue();
+ point.Position = oldControlPoints.Dequeue();
}
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
diff --git a/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs
new file mode 100644
index 0000000000..8cb778a2e1
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs
@@ -0,0 +1,52 @@
+// Copyright (c) ppy Pty Ltd . 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.Game.Graphics.UserInterfaceV2;
+using osu.Game.Screens.Edit.Setup;
+
+namespace osu.Game.Rulesets.Osu.Edit.Setup
+{
+ public class OsuSetupSection : RulesetSetupSection
+ {
+ private LabelledSliderBar stackLeniency;
+
+ public OsuSetupSection()
+ : base(new OsuRuleset().RulesetInfo)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Children = new[]
+ {
+ stackLeniency = new LabelledSliderBar
+ {
+ Label = "Stack Leniency",
+ Description = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.",
+ Current = new BindableFloat(Beatmap.BeatmapInfo.StackLeniency)
+ {
+ Default = 0.7f,
+ MinValue = 0,
+ MaxValue = 1,
+ Precision = 0.1f
+ }
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ stackLeniency.Current.BindValueChanged(_ => updateBeatmap());
+ }
+
+ private void updateBeatmap()
+ {
+ Beatmap.BeatmapInfo.StackLeniency = stackLeniency.Current.Value;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs
index 82bca0a4e2..30ff6b8984 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs
@@ -4,6 +4,7 @@
#nullable enable
using System;
+using System.Diagnostics;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Performance;
using osu.Game.Rulesets.Objects;
@@ -20,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
Start = start;
LifetimeStart = Start.StartTime;
-
- bindEvents();
}
private OsuHitObject? end;
@@ -41,31 +40,39 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
}
}
+ private bool wasBound;
+
private void bindEvents()
{
UnbindEvents();
+ if (End == null)
+ return;
+
// Note: Positions are bound for instantaneous feedback from positional changes from the editor, before ApplyDefaults() is called on hitobjects.
Start.DefaultsApplied += onDefaultsApplied;
Start.PositionBindable.ValueChanged += onPositionChanged;
- if (End != null)
- {
- End.DefaultsApplied += onDefaultsApplied;
- End.PositionBindable.ValueChanged += onPositionChanged;
- }
+ End.DefaultsApplied += onDefaultsApplied;
+ End.PositionBindable.ValueChanged += onPositionChanged;
+
+ wasBound = true;
}
public void UnbindEvents()
{
+ if (!wasBound)
+ return;
+
+ Debug.Assert(End != null);
+
Start.DefaultsApplied -= onDefaultsApplied;
Start.PositionBindable.ValueChanged -= onPositionChanged;
- if (End != null)
- {
- End.DefaultsApplied -= onDefaultsApplied;
- End.PositionBindable.ValueChanged -= onPositionChanged;
- }
+ End.DefaultsApplied -= onDefaultsApplied;
+ End.PositionBindable.ValueChanged -= onPositionChanged;
+
+ wasBound = false;
}
private void onDefaultsApplied(HitObject obj) => refreshLifetimes();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
index 79655c33e4..e4df41a4fe 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -74,10 +74,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override void PlayAnimation()
{
- base.PlayAnimation();
-
if (Result != HitResult.Miss)
- JudgementText.TransformSpacingTo(Vector2.Zero).Then().TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
+ {
+ JudgementText
+ .ScaleTo(new Vector2(0.8f, 1))
+ .ScaleTo(new Vector2(1.2f, 1), 1800, Easing.OutQuint);
+ }
+
+ base.PlayAnimation();
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 8ba9597dc3..c4420b1e87 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Objects
if (value != null)
{
- path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
+ path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index b13cdff1ec..f4a93a571d 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -30,9 +30,11 @@ using osu.Game.Skinning;
using System;
using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
+using osu.Game.Rulesets.Osu.Edit.Setup;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Skinning.Legacy;
using osu.Game.Rulesets.Osu.Statistics;
+using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Ranking.Statistics;
namespace osu.Game.Rulesets.Osu
@@ -305,5 +307,7 @@ namespace osu.Game.Rulesets.Osu
}
};
}
+
+ public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection();
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs
index 4dd7b2d69c..8602ebc88b 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
pathVersion.BindValueChanged(_ => Refresh());
accentColour = drawableObject.AccentColour.GetBoundCopy();
- accentColour.BindValueChanged(accent => updateAccentColour(skin, accent.NewValue), true);
+ accentColour.BindValueChanged(accent => AccentColour = GetBodyAccentColour(skin, accent.NewValue), true);
config?.BindWith(OsuRulesetSetting.SnakingInSliders, SnakingIn);
config?.BindWith(OsuRulesetSetting.SnakingOutSliders, configSnakingOut);
@@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
}
}
- private void updateAccentColour(ISkinSource skin, Color4 defaultAccentColour)
- => AccentColour = skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? defaultAccentColour;
+ protected virtual Color4 GetBodyAccentColour(ISkinSource skin, Color4 hitObjectAccentColour) =>
+ skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? hitObjectAccentColour;
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs
index ed4e04184b..7b7a89d5e2 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
///
/// A which changes its curve depending on the snaking progress.
///
- public class SnakingSliderBody : SliderBody, ISliderProgress
+ public abstract class SnakingSliderBody : SliderBody, ISliderProgress
{
public readonly List CurrentCurve = new List();
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs
index 587ff4b573..75d847d54d 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs
@@ -62,6 +62,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
protected override bool InterpolateMovements => !disjointTrail;
protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1);
+ protected override bool AvoidDrawingNearCursor => !disjointTrail;
protected override void Update()
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs
index 1c8dfeac52..29a0745193 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs
@@ -5,6 +5,7 @@ using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Skinning.Default;
+using osu.Game.Skinning;
using osu.Game.Utils;
using osuTK.Graphics;
@@ -14,6 +15,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
protected override DrawableSliderPath CreateSliderPath() => new LegacyDrawableSliderPath();
+ protected override Color4 GetBodyAccentColour(ISkinSource skin, Color4 hitObjectAccentColour)
+ {
+ // legacy skins use a constant value for slider track alpha, regardless of the source colour.
+ return base.GetBodyAccentColour(skin, hitObjectAccentColour).Opacity(0.7f);
+ }
+
private class LegacyDrawableSliderPath : DrawableSliderPath
{
private const float shadow_portion = 1 - (OsuLegacySkinTransformer.LEGACY_CIRCLE_RADIUS / OsuHitObject.OBJECT_RADIUS);
@@ -22,8 +29,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
// Roughly matches osu!stable's slider border portions.
=> base.CalculatedBorderPortion * 0.77f;
- public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, 0.7f);
-
protected override Color4 ColourAt(float position)
{
float realBorderPortion = shadow_portion + CalculatedBorderPortion;
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 7a95111c91..62cab4d6d7 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -138,6 +138,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected virtual bool InterpolateMovements => true;
protected virtual float IntervalMultiplier => 1.0f;
+ protected virtual bool AvoidDrawingNearCursor => false;
private Vector2? lastPosition;
private readonly InputResampler resampler = new InputResampler();
@@ -171,8 +172,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Vector2 direction = diff / distance;
float interval = partSize.X / 2.5f * IntervalMultiplier;
+ float stopAt = distance - (AvoidDrawingNearCursor ? interval : 0);
- for (float d = interval; d < distance; d += interval)
+ for (float d = interval; d < stopAt; d += interval)
{
lastPosition = pos1 + direction * d;
addPart(lastPosition.Value);
diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
index 57ec51cf64..bfd6ac3ad3 100644
--- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
+++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
@@ -119,9 +119,9 @@ namespace osu.Game.Rulesets.Osu.Utils
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
- var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
+ var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
foreach (var point in controlPoints)
- point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y);
+ point.Position = new Vector2(-point.Position.X, point.Position.Y);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
}
@@ -140,9 +140,9 @@ namespace osu.Game.Rulesets.Osu.Utils
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
- var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
+ var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
foreach (var point in controlPoints)
- point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
+ point.Position = new Vector2(point.Position.X, -point.Position.Y);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs
index f9b8e9a985..269a855219 100644
--- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
Origin = Anchor.Centre,
Children = new Drawable[]
{
- new TaikoPlayfield(new ControlPointInfo()),
+ new TaikoPlayfield(),
hoc = new ScrollingHitObjectContainer()
}
};
@@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
Origin = Anchor.Centre,
Children = new Drawable[]
{
- new TaikoPlayfield(new ControlPointInfo()),
+ new TaikoPlayfield(),
hoc = new ScrollingHitObjectContainer()
}
};
diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs
index 055a292fe8..24db046748 100644
--- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs
@@ -5,7 +5,6 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Taiko.UI;
using osuTK;
@@ -17,6 +16,13 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
+ var playfield = new TaikoPlayfield();
+
+ var beatmap = CreateWorkingBeatmap(new TaikoRuleset().RulesetInfo).GetPlayableBeatmap(new TaikoRuleset().RulesetInfo);
+
+ foreach (var h in beatmap.HitObjects)
+ playfield.Add(h);
+
SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
{
RelativeSizeAxes = Axes.Both,
@@ -25,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200),
- Child = new InputDrum(new ControlPointInfo())
+ Child = new InputDrum(playfield.HitObjectContainer)
}
});
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs
index f96297a06d..6f2fcd08f1 100644
--- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
Beatmap.Value.Track.Start();
});
- AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo())
+ AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index 532fdc5cb0..b9b295767e 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs
deleted file mode 100644
index e4dc261363..0000000000
--- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Audio;
-using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Skinning;
-
-namespace osu.Game.Rulesets.Taiko.Audio
-{
- ///
- /// Stores samples for the input drum.
- /// The lifetime of the samples is adjusted so that they are only alive during the appropriate sample control point.
- ///
- public class DrumSampleContainer : LifetimeManagementContainer
- {
- private readonly ControlPointInfo controlPoints;
- private readonly Dictionary mappings = new Dictionary();
-
- private readonly IBindableList samplePoints = new BindableList();
-
- public DrumSampleContainer(ControlPointInfo controlPoints)
- {
- this.controlPoints = controlPoints;
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- samplePoints.BindTo(controlPoints.SamplePoints);
- samplePoints.BindCollectionChanged((_, __) => recreateMappings(), true);
- }
-
- private void recreateMappings()
- {
- mappings.Clear();
- ClearInternal();
-
- SampleControlPoint[] points = samplePoints.Count == 0
- ? new[] { controlPoints.SamplePointAt(double.MinValue) }
- : samplePoints.ToArray();
-
- for (int i = 0; i < points.Length; i++)
- {
- var samplePoint = points[i];
-
- var lifetimeStart = i > 0 ? samplePoint.Time : double.MinValue;
- var lifetimeEnd = i + 1 < points.Length ? points[i + 1].Time : double.MaxValue;
-
- AddInternal(mappings[samplePoint.Time] = new DrumSample(samplePoint)
- {
- LifetimeStart = lifetimeStart,
- LifetimeEnd = lifetimeEnd
- });
- }
- }
-
- public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time];
-
- public class DrumSample : CompositeDrawable
- {
- public override bool RemoveWhenNotAlive => false;
-
- public PausableSkinnableSound Centre { get; private set; }
- public PausableSkinnableSound Rim { get; private set; }
-
- private readonly SampleControlPoint samplePoint;
-
- private Bindable sampleBank;
- private BindableNumber sampleVolume;
-
- public DrumSample(SampleControlPoint samplePoint)
- {
- this.samplePoint = samplePoint;
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- sampleBank = samplePoint.SampleBankBindable.GetBoundCopy();
- sampleBank.BindValueChanged(_ => recreate());
-
- sampleVolume = samplePoint.SampleVolumeBindable.GetBoundCopy();
- sampleVolume.BindValueChanged(_ => recreate());
-
- recreate();
- }
-
- private void recreate()
- {
- InternalChildren = new Drawable[]
- {
- Centre = new PausableSkinnableSound(samplePoint.GetSampleInfo()),
- Rim = new PausableSkinnableSound(samplePoint.GetSampleInfo(HitSampleInfo.HIT_CLAP))
- };
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index 90c99316b1..9b73e644c5 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -18,12 +18,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{
internal class TaikoBeatmapConverter : BeatmapConverter
{
- ///
- /// osu! is generally slower than taiko, so a factor is added to increase
- /// speed. This must be used everywhere slider length or beat length is used.
- ///
- public const float LEGACY_VELOCITY_MULTIPLIER = 1.4f;
-
///
/// Because swells are easier in taiko than spinners are in osu!,
/// legacy taiko multiplies a factor when converting the number of required hits.
@@ -52,10 +46,12 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken)
{
- // Rewrite the beatmap info to add the slider velocity multiplier
- original.BeatmapInfo = original.BeatmapInfo.Clone();
- original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone();
- original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= LEGACY_VELOCITY_MULTIPLIER;
+ if (!(original.BeatmapInfo.BaseDifficulty is TaikoMutliplierAppliedDifficulty))
+ {
+ // Rewrite the beatmap info to add the slider velocity multiplier
+ original.BeatmapInfo = original.BeatmapInfo.Clone();
+ original.BeatmapInfo.BaseDifficulty = new TaikoMutliplierAppliedDifficulty(original.BeatmapInfo.BaseDifficulty);
+ }
Beatmap converted = base.ConvertBeatmap(original, cancellationToken);
@@ -155,7 +151,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
int spans = (obj as IHasRepeats)?.SpanCount() ?? 1;
- double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER;
+ double distance = distanceData.Distance * spans * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
@@ -194,5 +190,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
}
protected override Beatmap CreateBeatmap() => new TaikoBeatmap();
+
+ private class TaikoMutliplierAppliedDifficulty : BeatmapDifficulty
+ {
+ public TaikoMutliplierAppliedDifficulty(BeatmapDifficulty difficulty)
+ {
+ difficulty.CopyTo(this);
+ SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index c0377c67a5..b0634295d0 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -6,10 +6,10 @@ using System;
using System.Threading;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Beatmaps.Formats;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Judgements;
using osuTK;
@@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
double IHasDistance.Distance => Duration * Velocity;
SliderPath IHasPath.Path
- => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER);
+ => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER);
#endregion
}
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs
index 795885d4b9..9d35093591 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs
@@ -7,7 +7,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
-using osu.Game.Rulesets.Taiko.Audio;
+using osu.Game.Rulesets.Taiko.Objects;
+using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Skinning;
using osuTK;
@@ -111,7 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
public readonly Sprite Centre;
[Resolved]
- private DrumSampleContainer sampleContainer { get; set; }
+ private DrumSampleTriggerSource sampleTriggerSource { get; set; }
public LegacyHalfDrum(bool flipped)
{
@@ -143,17 +144,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
public bool OnPressed(TaikoAction action)
{
Drawable target = null;
- var drumSample = sampleContainer.SampleAt(Time.Current);
if (action == CentreAction)
{
target = Centre;
- drumSample.Centre?.Play();
+ sampleTriggerSource.Play(HitType.Centre);
}
else if (action == RimAction)
{
target = Rim;
- drumSample.Rim?.Play();
+ sampleTriggerSource.Play(HitType.Rim);
}
if (target != null)
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
index 1ad1e4495c..876fa207bf 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
@@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.UI
{
@@ -16,5 +17,26 @@ namespace osu.Game.Rulesets.Taiko.UI
this.MoveToY(-100, 500);
base.ApplyHitAnimations();
}
+
+ protected override Drawable CreateDefaultJudgement(HitResult result) => new TaikoJudgementPiece(result);
+
+ private class TaikoJudgementPiece : DefaultJudgementPiece
+ {
+ public TaikoJudgementPiece(HitResult result)
+ : base(result)
+ {
+ }
+
+ public override void PlayAnimation()
+ {
+ if (Result != HitResult.Miss)
+ {
+ JudgementText.ScaleTo(0.9f);
+ JudgementText.ScaleTo(1, 500, Easing.OutElastic);
+ }
+
+ base.PlayAnimation();
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
index 650ce1f5a3..6ddbf3c16b 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
@@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
- protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo);
+ protected override Playfield CreatePlayfield() => new TaikoPlayfield();
public override DrawableHitObject CreateDrawableRepresentation(TaikoHitObject h) => null;
diff --git a/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs b/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs
new file mode 100644
index 0000000000..3279d128d3
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs
@@ -0,0 +1,30 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Game.Audio;
+using osu.Game.Rulesets.Taiko.Objects;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Taiko.UI
+{
+ public class DrumSampleTriggerSource : GameplaySampleTriggerSource
+ {
+ public DrumSampleTriggerSource(HitObjectContainer hitObjectContainer)
+ : base(hitObjectContainer)
+ {
+ }
+
+ public void Play(HitType hitType)
+ {
+ var hitObject = GetMostValidObject();
+
+ if (hitObject == null)
+ return;
+
+ PlaySamples(new ISampleInfo[] { hitObject.SampleControlPoint.GetSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL) });
+ }
+
+ public override void Play() => throw new InvalidOperationException(@"Use override with HitType parameter instead");
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
index 1ca1be1bdf..ddfaf64549 100644
--- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
@@ -2,18 +2,18 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
-using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Taiko.Audio;
+using osu.Game.Rulesets.Taiko.Objects;
+using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
+using osuTK;
namespace osu.Game.Rulesets.Taiko.UI
{
@@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Taiko.UI
private const float middle_split = 0.025f;
[Cached]
- private DrumSampleContainer sampleContainer;
+ private DrumSampleTriggerSource sampleTriggerSource;
- public InputDrum(ControlPointInfo controlPoints)
+ public InputDrum(HitObjectContainer hitObjectContainer)
{
- sampleContainer = new DrumSampleContainer(controlPoints);
+ sampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer);
RelativeSizeAxes = Axes.Both;
}
@@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Taiko.UI
}
}
}),
- sampleContainer
+ sampleTriggerSource
};
}
@@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Sprite centreHit;
[Resolved]
- private DrumSampleContainer sampleContainer { get; set; }
+ private DrumSampleTriggerSource sampleTriggerSource { get; set; }
public TaikoHalfDrum(bool flipped)
{
@@ -156,21 +156,19 @@ namespace osu.Game.Rulesets.Taiko.UI
Drawable target = null;
Drawable back = null;
- var drumSample = sampleContainer.SampleAt(Time.Current);
-
if (action == CentreAction)
{
target = centreHit;
back = centre;
- drumSample.Centre?.Play();
+ sampleTriggerSource.Play(HitType.Centre);
}
else if (action == RimAction)
{
target = rimHit;
back = rim;
- drumSample.Rim?.Play();
+ sampleTriggerSource.Play(HitType.Rim);
}
if (target != null)
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 0d9e08b8b7..d650cab729 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling;
-using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Judgements;
@@ -27,8 +26,6 @@ namespace osu.Game.Rulesets.Taiko.UI
{
public class TaikoPlayfield : ScrollingPlayfield
{
- private readonly ControlPointInfo controlPoints;
-
///
/// Default height of a when inside a .
///
@@ -56,11 +53,6 @@ namespace osu.Game.Rulesets.Taiko.UI
private Container hitTargetOffsetContent;
- public TaikoPlayfield(ControlPointInfo controlPoints)
- {
- this.controlPoints = controlPoints;
- }
-
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
@@ -131,7 +123,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Children = new Drawable[]
{
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()),
- new InputDrum(controlPoints)
+ new InputDrum(HitObjectContainer)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
diff --git a/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs b/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs
deleted file mode 100644
index d407c0663f..0000000000
--- a/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using NUnit.Framework;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Osu.Mods;
-
-namespace osu.Game.Tests.Beatmaps
-{
- [TestFixture]
- public class BeatmapDifficultyCacheTest
- {
- [Test]
- public void TestKeyEqualsWithDifferentModInstances()
- {
- var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
- var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
-
- Assert.That(key1, Is.EqualTo(key2));
- }
-
- [Test]
- public void TestKeyEqualsWithDifferentModOrder()
- {
- var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
- var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
-
- Assert.That(key1, Is.EqualTo(key2));
- }
-
- [TestCase(1.3, DifficultyRating.Easy)]
- [TestCase(1.993, DifficultyRating.Easy)]
- [TestCase(1.998, DifficultyRating.Normal)]
- [TestCase(2.4, DifficultyRating.Normal)]
- [TestCase(2.693, DifficultyRating.Normal)]
- [TestCase(2.698, DifficultyRating.Hard)]
- [TestCase(3.5, DifficultyRating.Hard)]
- [TestCase(3.993, DifficultyRating.Hard)]
- [TestCase(3.997, DifficultyRating.Insane)]
- [TestCase(5.0, DifficultyRating.Insane)]
- [TestCase(5.292, DifficultyRating.Insane)]
- [TestCase(5.297, DifficultyRating.Expert)]
- [TestCase(6.2, DifficultyRating.Expert)]
- [TestCase(6.493, DifficultyRating.Expert)]
- [TestCase(6.498, DifficultyRating.ExpertPlus)]
- [TestCase(8.3, DifficultyRating.ExpertPlus)]
- public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket)
- {
- var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating);
-
- Assert.AreEqual(expectedBracket, actualBracket);
- }
- }
-}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 4fe1cf3790..8560a36fb4 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -3,15 +3,12 @@
using System;
using System.IO;
-using NUnit.Framework;
-using osuTK;
-using osuTK.Graphics;
-using osu.Game.Tests.Resources;
using System.Linq;
+using NUnit.Framework;
using osu.Game.Audio;
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.Formats;
+using osu.Game.Beatmaps.Legacy;
using osu.Game.Beatmaps.Timing;
using osu.Game.IO;
using osu.Game.Rulesets.Catch;
@@ -19,9 +16,13 @@ using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
+using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Skinning;
+using osu.Game.Tests.Resources;
+using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Tests.Beatmaps.Formats
{
@@ -58,12 +59,13 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
Assert.AreEqual(164471, metadata.PreviewTime);
- Assert.IsFalse(beatmapInfo.Countdown);
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.IsTrue(beatmapInfo.RulesetID == 0);
Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
Assert.IsFalse(beatmapInfo.SpecialStyle);
Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
+ Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
+ Assert.AreEqual(0, beatmapInfo.CountdownOffset);
}
}
@@ -165,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var stream = new LineBufferedReader(resStream))
{
var beatmap = decoder.Decode(stream);
- var controlPoints = beatmap.ControlPointInfo;
+ var controlPoints = (LegacyControlPointInfo)beatmap.ControlPointInfo;
Assert.AreEqual(4, controlPoints.TimingPoints.Count);
Assert.AreEqual(5, controlPoints.DifficultyPoints.Count);
@@ -239,7 +241,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var resStream = TestResources.OpenResource("overlapping-control-points.osu"))
using (var stream = new LineBufferedReader(resStream))
{
- var controlPoints = decoder.Decode(stream).ControlPointInfo;
+ var controlPoints = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo;
Assert.That(controlPoints.TimingPoints.Count, Is.EqualTo(4));
Assert.That(controlPoints.DifficultyPoints.Count, Is.EqualTo(3));
@@ -665,111 +667,111 @@ namespace osu.Game.Tests.Beatmaps.Formats
// Multi-segment
var first = ((IHasPath)decoded.HitObjects[0]).Path;
- Assert.That(first.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
- Assert.That(first.ControlPoints[0].Type.Value, Is.EqualTo(PathType.PerfectCurve));
- Assert.That(first.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(161, -244)));
- Assert.That(first.ControlPoints[1].Type.Value, Is.EqualTo(null));
+ Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
+ Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve));
+ Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244)));
+ Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null));
- Assert.That(first.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(376, -3)));
- Assert.That(first.ControlPoints[2].Type.Value, Is.EqualTo(PathType.Bezier));
- Assert.That(first.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(68, 15)));
- Assert.That(first.ControlPoints[3].Type.Value, Is.EqualTo(null));
- Assert.That(first.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(259, -132)));
- Assert.That(first.ControlPoints[4].Type.Value, Is.EqualTo(null));
- Assert.That(first.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(92, -107)));
- Assert.That(first.ControlPoints[5].Type.Value, Is.EqualTo(null));
+ Assert.That(first.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3)));
+ Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.Bezier));
+ Assert.That(first.ControlPoints[3].Position, Is.EqualTo(new Vector2(68, 15)));
+ Assert.That(first.ControlPoints[3].Type, Is.EqualTo(null));
+ Assert.That(first.ControlPoints[4].Position, Is.EqualTo(new Vector2(259, -132)));
+ Assert.That(first.ControlPoints[4].Type, Is.EqualTo(null));
+ Assert.That(first.ControlPoints[5].Position, Is.EqualTo(new Vector2(92, -107)));
+ Assert.That(first.ControlPoints[5].Type, Is.EqualTo(null));
// Single-segment
var second = ((IHasPath)decoded.HitObjects[1]).Path;
- Assert.That(second.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
- Assert.That(second.ControlPoints[0].Type.Value, Is.EqualTo(PathType.PerfectCurve));
- Assert.That(second.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(161, -244)));
- Assert.That(second.ControlPoints[1].Type.Value, Is.EqualTo(null));
- Assert.That(second.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(376, -3)));
- Assert.That(second.ControlPoints[2].Type.Value, Is.EqualTo(null));
+ Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
+ Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve));
+ Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244)));
+ Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null));
+ Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3)));
+ Assert.That(second.ControlPoints[2].Type, Is.EqualTo(null));
// Implicit multi-segment
var third = ((IHasPath)decoded.HitObjects[2]).Path;
- Assert.That(third.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
- Assert.That(third.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier));
- Assert.That(third.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(0, 192)));
- Assert.That(third.ControlPoints[1].Type.Value, Is.EqualTo(null));
- Assert.That(third.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(224, 192)));
- Assert.That(third.ControlPoints[2].Type.Value, Is.EqualTo(null));
+ Assert.That(third.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
+ Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
+ Assert.That(third.ControlPoints[1].Position, Is.EqualTo(new Vector2(0, 192)));
+ Assert.That(third.ControlPoints[1].Type, Is.EqualTo(null));
+ Assert.That(third.ControlPoints[2].Position, Is.EqualTo(new Vector2(224, 192)));
+ Assert.That(third.ControlPoints[2].Type, Is.EqualTo(null));
- Assert.That(third.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(224, 0)));
- Assert.That(third.ControlPoints[3].Type.Value, Is.EqualTo(PathType.Bezier));
- Assert.That(third.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(224, -192)));
- Assert.That(third.ControlPoints[4].Type.Value, Is.EqualTo(null));
- Assert.That(third.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(480, -192)));
- Assert.That(third.ControlPoints[5].Type.Value, Is.EqualTo(null));
- Assert.That(third.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(480, 0)));
- Assert.That(third.ControlPoints[6].Type.Value, Is.EqualTo(null));
+ Assert.That(third.ControlPoints[3].Position, Is.EqualTo(new Vector2(224, 0)));
+ Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.Bezier));
+ Assert.That(third.ControlPoints[4].Position, Is.EqualTo(new Vector2(224, -192)));
+ Assert.That(third.ControlPoints[4].Type, Is.EqualTo(null));
+ Assert.That(third.ControlPoints[5].Position, Is.EqualTo(new Vector2(480, -192)));
+ Assert.That(third.ControlPoints[5].Type, Is.EqualTo(null));
+ Assert.That(third.ControlPoints[6].Position, Is.EqualTo(new Vector2(480, 0)));
+ Assert.That(third.ControlPoints[6].Type, Is.EqualTo(null));
// Last control point duplicated
var fourth = ((IHasPath)decoded.HitObjects[3]).Path;
- Assert.That(fourth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
- Assert.That(fourth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier));
- Assert.That(fourth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1)));
- Assert.That(fourth.ControlPoints[1].Type.Value, Is.EqualTo(null));
- Assert.That(fourth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2)));
- Assert.That(fourth.ControlPoints[2].Type.Value, Is.EqualTo(null));
- Assert.That(fourth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3)));
- Assert.That(fourth.ControlPoints[3].Type.Value, Is.EqualTo(null));
- Assert.That(fourth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3)));
- Assert.That(fourth.ControlPoints[4].Type.Value, Is.EqualTo(null));
+ Assert.That(fourth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
+ Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
+ Assert.That(fourth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1)));
+ Assert.That(fourth.ControlPoints[1].Type, Is.EqualTo(null));
+ Assert.That(fourth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2)));
+ Assert.That(fourth.ControlPoints[2].Type, Is.EqualTo(null));
+ Assert.That(fourth.ControlPoints[3].Position, Is.EqualTo(new Vector2(3, 3)));
+ Assert.That(fourth.ControlPoints[3].Type, Is.EqualTo(null));
+ Assert.That(fourth.ControlPoints[4].Position, Is.EqualTo(new Vector2(3, 3)));
+ Assert.That(fourth.ControlPoints[4].Type, Is.EqualTo(null));
// Last control point in segment duplicated
var fifth = ((IHasPath)decoded.HitObjects[4]).Path;
- Assert.That(fifth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
- Assert.That(fifth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier));
- Assert.That(fifth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1)));
- Assert.That(fifth.ControlPoints[1].Type.Value, Is.EqualTo(null));
- Assert.That(fifth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2)));
- Assert.That(fifth.ControlPoints[2].Type.Value, Is.EqualTo(null));
- Assert.That(fifth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3)));
- Assert.That(fifth.ControlPoints[3].Type.Value, Is.EqualTo(null));
- Assert.That(fifth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3)));
- Assert.That(fifth.ControlPoints[4].Type.Value, Is.EqualTo(null));
+ Assert.That(fifth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
+ Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
+ Assert.That(fifth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1)));
+ Assert.That(fifth.ControlPoints[1].Type, Is.EqualTo(null));
+ Assert.That(fifth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2)));
+ Assert.That(fifth.ControlPoints[2].Type, Is.EqualTo(null));
+ Assert.That(fifth.ControlPoints[3].Position, Is.EqualTo(new Vector2(3, 3)));
+ Assert.That(fifth.ControlPoints[3].Type, Is.EqualTo(null));
+ Assert.That(fifth.ControlPoints[4].Position, Is.EqualTo(new Vector2(3, 3)));
+ Assert.That(fifth.ControlPoints[4].Type, Is.EqualTo(null));
- Assert.That(fifth.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(4, 4)));
- Assert.That(fifth.ControlPoints[5].Type.Value, Is.EqualTo(PathType.Bezier));
- Assert.That(fifth.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(5, 5)));
- Assert.That(fifth.ControlPoints[6].Type.Value, Is.EqualTo(null));
+ Assert.That(fifth.ControlPoints[5].Position, Is.EqualTo(new Vector2(4, 4)));
+ Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.Bezier));
+ Assert.That(fifth.ControlPoints[6].Position, Is.EqualTo(new Vector2(5, 5)));
+ Assert.That(fifth.ControlPoints[6].Type, Is.EqualTo(null));
// Implicit perfect-curve multi-segment(Should convert to bezier to match stable)
var sixth = ((IHasPath)decoded.HitObjects[5]).Path;
- Assert.That(sixth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
- Assert.That(sixth.ControlPoints[0].Type.Value == PathType.Bezier);
- Assert.That(sixth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145)));
- Assert.That(sixth.ControlPoints[1].Type.Value == null);
- Assert.That(sixth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75)));
+ Assert.That(sixth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
+ Assert.That(sixth.ControlPoints[0].Type == PathType.Bezier);
+ Assert.That(sixth.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145)));
+ Assert.That(sixth.ControlPoints[1].Type == null);
+ Assert.That(sixth.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75)));
- Assert.That(sixth.ControlPoints[2].Type.Value == PathType.Bezier);
- Assert.That(sixth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145)));
- Assert.That(sixth.ControlPoints[3].Type.Value == null);
- Assert.That(sixth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20)));
- Assert.That(sixth.ControlPoints[4].Type.Value == null);
+ Assert.That(sixth.ControlPoints[2].Type == PathType.Bezier);
+ Assert.That(sixth.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145)));
+ Assert.That(sixth.ControlPoints[3].Type == null);
+ Assert.That(sixth.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20)));
+ Assert.That(sixth.ControlPoints[4].Type == null);
// Explicit perfect-curve multi-segment(Should not convert to bezier)
var seventh = ((IHasPath)decoded.HitObjects[6]).Path;
- Assert.That(seventh.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
- Assert.That(seventh.ControlPoints[0].Type.Value == PathType.PerfectCurve);
- Assert.That(seventh.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145)));
- Assert.That(seventh.ControlPoints[1].Type.Value == null);
- Assert.That(seventh.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75)));
+ Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
+ Assert.That(seventh.ControlPoints[0].Type == PathType.PerfectCurve);
+ Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145)));
+ Assert.That(seventh.ControlPoints[1].Type == null);
+ Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75)));
- Assert.That(seventh.ControlPoints[2].Type.Value == PathType.PerfectCurve);
- Assert.That(seventh.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145)));
- Assert.That(seventh.ControlPoints[3].Type.Value == null);
- Assert.That(seventh.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20)));
- Assert.That(seventh.ControlPoints[4].Type.Value == null);
+ Assert.That(seventh.ControlPoints[2].Type == PathType.PerfectCurve);
+ Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145)));
+ Assert.That(seventh.ControlPoints[3].Type == null);
+ Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20)));
+ Assert.That(seventh.ControlPoints[4].Type == null);
}
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
index 855a75117d..896aa53f82 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
@@ -14,6 +14,7 @@ using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Formats;
+using osu.Game.Beatmaps.Legacy;
using osu.Game.IO;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets.Catch;
@@ -49,6 +50,63 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration));
}
+ [TestCaseSource(nameof(allBeatmaps))]
+ public void TestEncodeDecodeStabilityDoubleConvert(string name)
+ {
+ var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
+ var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name);
+
+ // run an extra convert. this is expected to be stable.
+ decodedAfterEncode.beatmap = convert(decodedAfterEncode.beatmap);
+
+ sort(decoded.beatmap);
+ sort(decodedAfterEncode.beatmap);
+
+ Assert.That(decodedAfterEncode.beatmap.Serialize(), Is.EqualTo(decoded.beatmap.Serialize()));
+ Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration));
+ }
+
+ [TestCaseSource(nameof(allBeatmaps))]
+ public void TestEncodeDecodeStabilityWithNonLegacyControlPoints(string name)
+ {
+ var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
+
+ // we are testing that the transfer of relevant data to hitobjects (from legacy control points) sticks through encode/decode.
+ // before the encode step, the legacy information is removed here.
+ decoded.beatmap.ControlPointInfo = removeLegacyControlPointTypes(decoded.beatmap.ControlPointInfo);
+
+ var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name);
+
+ // in this process, we may lose some detail in the control points section.
+ // let's focus on only the hitobjects.
+ var originalHitObjects = decoded.beatmap.HitObjects.Serialize();
+ var newHitObjects = decodedAfterEncode.beatmap.HitObjects.Serialize();
+
+ Assert.That(newHitObjects, Is.EqualTo(originalHitObjects));
+
+ ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo)
+ {
+ // emulate non-legacy control points by cloning the non-legacy portion.
+ // the assertion is that the encoder can recreate this losslessly from hitobject data.
+ Assert.IsInstanceOf(controlPointInfo);
+
+ var newControlPoints = new ControlPointInfo();
+
+ foreach (var point in controlPointInfo.AllControlPoints)
+ {
+ // completely ignore "legacy" types, which have been moved to HitObjects.
+ // even though these would mostly be ignored by the Add call, they will still be available in groups,
+ // which isn't what we want to be testing here.
+ if (point is SampleControlPoint)
+ continue;
+
+ newControlPoints.Add(point.Time, point.DeepClone());
+ }
+
+ return newControlPoints;
+ }
+ }
+
[Test]
public void TestEncodeMultiSegmentSliderWithFloatingPointError()
{
@@ -116,7 +174,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
- private Stream encodeToLegacy((IBeatmap beatmap, ISkin beatmapSkin) fullBeatmap)
+ private MemoryStream encodeToLegacy((IBeatmap beatmap, ISkin beatmapSkin) fullBeatmap)
{
var (beatmap, beatmapSkin) = fullBeatmap;
var stream = new MemoryStream();
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index e97c83e2c2..1fc3abef9a 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -50,12 +50,13 @@ namespace osu.Game.Tests.Beatmaps.Formats
var beatmap = decodeAsJson(normal);
var beatmapInfo = beatmap.BeatmapInfo;
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
- Assert.AreEqual(false, beatmapInfo.Countdown);
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
Assert.IsTrue(beatmapInfo.RulesetID == 0);
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
+ Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
+ Assert.AreEqual(0, beatmapInfo.CountdownOffset);
}
[Test]
diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs
new file mode 100644
index 0000000000..dcfeea5db8
--- /dev/null
+++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs
@@ -0,0 +1,173 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Tests.Beatmaps.IO;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Tests.Beatmaps
+{
+ [HeadlessTest]
+ public class TestSceneBeatmapDifficultyCache : OsuTestScene
+ {
+ public const double BASE_STARS = 5.55;
+
+ private BeatmapSetInfo importedSet;
+
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
+
+ private TestBeatmapDifficultyCache difficultyCache;
+
+ private IBindable starDifficultyBindable;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase osu)
+ {
+ importedSet = ImportBeatmapTest.LoadQuickOszIntoOsu(osu).Result;
+ }
+
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("setup difficulty cache", () =>
+ {
+ SelectedMods.Value = Array.Empty();
+
+ Child = difficultyCache = new TestBeatmapDifficultyCache();
+
+ starDifficultyBindable = difficultyCache.GetBindableDifficulty(importedSet.Beatmaps.First());
+ });
+
+ AddUntilStep($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value?.Stars == BASE_STARS);
+ }
+
+ [Test]
+ public void TestStarDifficultyChangesOnModSettings()
+ {
+ OsuModDoubleTime dt = null;
+
+ AddStep("set computation function", () => difficultyCache.ComputeDifficulty = lookup =>
+ {
+ var modRateAdjust = (ModRateAdjust)lookup.OrderedMods.SingleOrDefault(mod => mod is ModRateAdjust);
+ return new StarDifficulty(BASE_STARS + modRateAdjust?.SpeedChange.Value ?? 0, 0);
+ });
+
+ AddStep("change selected mod to DT", () => SelectedMods.Value = new[] { dt = new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } });
+ AddUntilStep($"star difficulty -> {BASE_STARS + 1.5}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.5);
+
+ AddStep("change DT speed to 1.25", () => dt.SpeedChange.Value = 1.25);
+ AddUntilStep($"star difficulty -> {BASE_STARS + 1.25}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.25);
+
+ AddStep("change selected mod to NC", () => SelectedMods.Value = new[] { new OsuModNightcore { SpeedChange = { Value = 1.75 } } });
+ AddUntilStep($"star difficulty -> {BASE_STARS + 1.75}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.75);
+ }
+
+ [Test]
+ public void TestStarDifficultyAdjustHashCodeConflict()
+ {
+ OsuModDifficultyAdjust difficultyAdjust = null;
+
+ AddStep("set computation function", () => difficultyCache.ComputeDifficulty = lookup =>
+ {
+ var modDifficultyAdjust = (ModDifficultyAdjust)lookup.OrderedMods.SingleOrDefault(mod => mod is ModDifficultyAdjust);
+ return new StarDifficulty(BASE_STARS * (modDifficultyAdjust?.OverallDifficulty.Value ?? 1), 0);
+ });
+
+ AddStep("change selected mod to DA", () => SelectedMods.Value = new[] { difficultyAdjust = new OsuModDifficultyAdjust() });
+ AddUntilStep($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value?.Stars == BASE_STARS);
+
+ AddStep("change DA difficulty to 0.5", () => difficultyAdjust.OverallDifficulty.Value = 0.5f);
+ AddUntilStep($"star difficulty -> {BASE_STARS * 0.5f}", () => starDifficultyBindable.Value?.Stars == BASE_STARS / 2);
+
+ // hash code of 0 (the value) conflicts with the hash code of null (the initial/default value).
+ // it's important that the mod reference and its underlying bindable references stay the same to demonstrate this failure.
+ AddStep("change DA difficulty to 0", () => difficultyAdjust.OverallDifficulty.Value = 0);
+ AddUntilStep("star difficulty -> 0", () => starDifficultyBindable.Value?.Stars == 0);
+ }
+
+ [Test]
+ public void TestKeyEqualsWithDifferentModInstances()
+ {
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+
+ Assert.That(key1, Is.EqualTo(key2));
+ Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
+ }
+
+ [Test]
+ public void TestKeyEqualsWithDifferentModOrder()
+ {
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
+
+ Assert.That(key1, Is.EqualTo(key2));
+ Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
+ }
+
+ [Test]
+ public void TestKeyDoesntEqualWithDifferentModSettings()
+ {
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
+
+ Assert.That(key1, Is.Not.EqualTo(key2));
+ Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode()));
+ }
+
+ [Test]
+ public void TestKeyEqualWithMatchingModSettings()
+ {
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
+
+ Assert.That(key1, Is.EqualTo(key2));
+ Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
+ }
+
+ [TestCase(1.3, DifficultyRating.Easy)]
+ [TestCase(1.993, DifficultyRating.Easy)]
+ [TestCase(1.998, DifficultyRating.Normal)]
+ [TestCase(2.4, DifficultyRating.Normal)]
+ [TestCase(2.693, DifficultyRating.Normal)]
+ [TestCase(2.698, DifficultyRating.Hard)]
+ [TestCase(3.5, DifficultyRating.Hard)]
+ [TestCase(3.993, DifficultyRating.Hard)]
+ [TestCase(3.997, DifficultyRating.Insane)]
+ [TestCase(5.0, DifficultyRating.Insane)]
+ [TestCase(5.292, DifficultyRating.Insane)]
+ [TestCase(5.297, DifficultyRating.Expert)]
+ [TestCase(6.2, DifficultyRating.Expert)]
+ [TestCase(6.493, DifficultyRating.Expert)]
+ [TestCase(6.498, DifficultyRating.ExpertPlus)]
+ [TestCase(8.3, DifficultyRating.ExpertPlus)]
+ public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket)
+ {
+ var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating);
+
+ Assert.AreEqual(expectedBracket, actualBracket);
+ }
+
+ private class TestBeatmapDifficultyCache : BeatmapDifficultyCache
+ {
+ public Func ComputeDifficulty { get; set; }
+
+ protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default)
+ {
+ return Task.FromResult(ComputeDifficulty?.Invoke(lookup) ?? new StarDifficulty(BASE_STARS, 0));
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs
index 8f5ebf53bd..d87ac29d75 100644
--- a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs
+++ b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs
@@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Platform;
+using osu.Framework.Testing;
using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Collections.IO
@@ -127,7 +128,7 @@ namespace osu.Game.Tests.Collections.IO
[Test]
public async Task TestSaveAndReload()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
+ using (HeadlessGameHost host = new TestRunHeadlessGameHost("TestSaveAndReload", bypassCleanup: true))
{
try
{
@@ -148,7 +149,7 @@ namespace osu.Game.Tests.Collections.IO
}
}
- using (HeadlessGameHost host = new HeadlessGameHost("TestSaveAndReload"))
+ using (HeadlessGameHost host = new TestRunHeadlessGameHost("TestSaveAndReload"))
{
try
{
diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
index 642ecf00b8..8be74f1a7c 100644
--- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
+++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
@@ -11,6 +11,7 @@ using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Input;
using osu.Game.Input.Bindings;
+using osu.Game.Rulesets;
using Realms;
namespace osu.Game.Tests.Database
@@ -42,7 +43,7 @@ namespace osu.Game.Tests.Database
KeyBindingContainer testContainer = new TestKeyBindingContainer();
- keyBindingStore.Register(testContainer);
+ keyBindingStore.Register(testContainer, Enumerable.Empty());
Assert.That(queryCount(), Is.EqualTo(3));
@@ -66,7 +67,7 @@ namespace osu.Game.Tests.Database
{
KeyBindingContainer testContainer = new TestKeyBindingContainer();
- keyBindingStore.Register(testContainer);
+ keyBindingStore.Register(testContainer, Enumerable.Empty());
using (var primaryUsage = realmContextFactory.GetForRead())
{
diff --git a/osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs b/osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs
index 41a8f72305..4ab6e5cef6 100644
--- a/osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs
+++ b/osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs
@@ -7,6 +7,7 @@ using NUnit.Framework;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects;
@@ -30,7 +31,7 @@ namespace osu.Game.Tests.Editing.Checks
{
check = new CheckMutedObjects();
- cpi = new ControlPointInfo();
+ cpi = new LegacyControlPointInfo();
cpi.Add(0, new SampleControlPoint { SampleVolume = volume_regular });
cpi.Add(1000, new SampleControlPoint { SampleVolume = volume_low });
cpi.Add(2000, new SampleControlPoint { SampleVolume = volume_muted });
diff --git a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs
index 27cece42e8..b612899d79 100644
--- a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs
+++ b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs
@@ -8,7 +8,7 @@ using osu.Framework.Input;
using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.Input;
-using osu.Game.Tests.Visual.Navigation;
+using osu.Game.Tests.Visual;
namespace osu.Game.Tests.Input
{
diff --git a/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs b/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs
index 7a5789f01a..ce6b3a68a5 100644
--- a/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs
+++ b/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Game.Online.API;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
namespace osu.Game.Tests.Mods
@@ -11,26 +12,42 @@ namespace osu.Game.Tests.Mods
public class ModSettingsEqualityComparison
{
[Test]
- public void Test()
+ public void TestAPIMod()
{
+ var apiMod1 = new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 1.25 } });
+ var apiMod2 = new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 1.26 } });
+ var apiMod3 = new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 1.26 } });
+
+ Assert.That(apiMod1, Is.Not.EqualTo(apiMod2));
+ Assert.That(apiMod2, Is.EqualTo(apiMod2));
+ Assert.That(apiMod2, Is.EqualTo(apiMod3));
+ Assert.That(apiMod3, Is.EqualTo(apiMod2));
+ }
+
+ [Test]
+ public void TestMod()
+ {
+ var ruleset = new OsuRuleset();
+
var mod1 = new OsuModDoubleTime { SpeedChange = { Value = 1.25 } };
var mod2 = new OsuModDoubleTime { SpeedChange = { Value = 1.26 } };
var mod3 = new OsuModDoubleTime { SpeedChange = { Value = 1.26 } };
- var apiMod1 = new APIMod(mod1);
- var apiMod2 = new APIMod(mod2);
- var apiMod3 = new APIMod(mod3);
+
+ var doubleConvertedMod1 = new APIMod(mod1).ToMod(ruleset);
+ var doulbeConvertedMod2 = new APIMod(mod2).ToMod(ruleset);
+ var doulbeConvertedMod3 = new APIMod(mod3).ToMod(ruleset);
Assert.That(mod1, Is.Not.EqualTo(mod2));
- Assert.That(apiMod1, Is.Not.EqualTo(apiMod2));
+ Assert.That(doubleConvertedMod1, Is.Not.EqualTo(doulbeConvertedMod2));
Assert.That(mod2, Is.EqualTo(mod2));
- Assert.That(apiMod2, Is.EqualTo(apiMod2));
+ Assert.That(doulbeConvertedMod2, Is.EqualTo(doulbeConvertedMod2));
Assert.That(mod2, Is.EqualTo(mod3));
- Assert.That(apiMod2, Is.EqualTo(apiMod3));
+ Assert.That(doulbeConvertedMod2, Is.EqualTo(doulbeConvertedMod3));
Assert.That(mod3, Is.EqualTo(mod2));
- Assert.That(apiMod3, Is.EqualTo(apiMod2));
+ Assert.That(doulbeConvertedMod3, Is.EqualTo(doulbeConvertedMod2));
}
}
}
diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs
index 240ae4a90c..fabb016d5f 100644
--- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs
+++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs
@@ -4,6 +4,7 @@
using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Tests.NonVisual
{
@@ -64,7 +65,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestAddRedundantSample()
{
- var cpi = new ControlPointInfo();
+ var cpi = new LegacyControlPointInfo();
cpi.Add(0, new SampleControlPoint()); // is *not* redundant, special exception for first sample point
cpi.Add(1000, new SampleControlPoint()); // is redundant
@@ -142,7 +143,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestRemoveGroupAlsoRemovedControlPoints()
{
- var cpi = new ControlPointInfo();
+ var cpi = new LegacyControlPointInfo();
var group = cpi.GroupAt(1000, true);
diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs
index 4c44e2ec72..5e14af5c27 100644
--- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs
+++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs
@@ -6,10 +6,10 @@ using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using NUnit.Framework;
-using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Platform;
+using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.IO;
@@ -278,7 +278,7 @@ namespace osu.Game.Tests.NonVisual
private static string getDefaultLocationFor(string testTypeName)
{
- string path = Path.Combine(RuntimeInfo.StartupDirectory, "headless", testTypeName);
+ string path = Path.Combine(TestRunHeadlessGameHost.TemporaryTestDirectory, testTypeName);
if (Directory.Exists(path))
Directory.Delete(path, true);
@@ -288,7 +288,7 @@ namespace osu.Game.Tests.NonVisual
private string prepareCustomPath(string suffix = "")
{
- string path = Path.Combine(RuntimeInfo.StartupDirectory, $"custom-path{suffix}");
+ string path = Path.Combine(TestRunHeadlessGameHost.TemporaryTestDirectory, $"custom-path{suffix}");
if (Directory.Exists(path))
Directory.Delete(path, true);
@@ -308,6 +308,19 @@ namespace osu.Game.Tests.NonVisual
InitialStorage = new DesktopStorage(defaultStorageLocation, this);
InitialStorage.DeleteDirectory(string.Empty);
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ try
+ {
+ // the storage may have changed from the initial location.
+ // this handles cleanup of the initial location.
+ InitialStorage.DeleteDirectory(string.Empty);
+ }
+ catch { }
+ }
}
}
}
diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs
index e45b8f7dc5..785f31386d 100644
--- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs
+++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs
@@ -40,10 +40,10 @@ namespace osu.Game.Tests.NonVisual.Skinning
assertPlaybackPosition(0);
AddStep("set start time to 1000", () => animationTimeReference.AnimationStartTime.Value = 1000);
- assertPlaybackPosition(-1000);
+ assertPlaybackPosition(0);
AddStep("set current time to 500", () => animationTimeReference.ManualClock.CurrentTime = 500);
- assertPlaybackPosition(-500);
+ assertPlaybackPosition(0);
}
private void assertPlaybackPosition(double expectedPosition)
diff --git a/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs b/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs
new file mode 100644
index 0000000000..5491774e26
--- /dev/null
+++ b/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs
@@ -0,0 +1,72 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using MessagePack;
+using NUnit.Framework;
+using osu.Game.Online;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
+
+namespace osu.Game.Tests.Online
+{
+ [TestFixture]
+ public class TestMultiplayerMessagePackSerialization
+ {
+ [Test]
+ public void TestSerialiseRoom()
+ {
+ var room = new MultiplayerRoom(1)
+ {
+ MatchState = new TeamVersusRoomState()
+ };
+
+ var serialized = MessagePackSerializer.Serialize(room);
+
+ var deserialized = MessagePackSerializer.Deserialize(serialized);
+
+ Assert.IsTrue(deserialized.MatchState is TeamVersusRoomState);
+ }
+
+ [Test]
+ public void TestSerialiseUserStateExpected()
+ {
+ var state = new TeamVersusUserState();
+
+ var serialized = MessagePackSerializer.Serialize(typeof(MatchUserState), state);
+ var deserialized = MessagePackSerializer.Deserialize(serialized);
+
+ Assert.IsTrue(deserialized is TeamVersusUserState);
+ }
+
+ [Test]
+ public void TestSerialiseUnionFailsWithSingalR()
+ {
+ var state = new TeamVersusUserState();
+
+ // SignalR serialises using the actual type, rather than a base specification.
+ var serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state);
+
+ // works with explicit type specified.
+ MessagePackSerializer.Deserialize(serialized);
+
+ // fails with base (union) type.
+ Assert.Throws(() => MessagePackSerializer.Deserialize(serialized));
+ }
+
+ [Test]
+ public void TestSerialiseUnionSucceedsWithWorkaround()
+ {
+ var state = new TeamVersusUserState();
+
+ // SignalR serialises using the actual type, rather than a base specification.
+ var serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state, SignalRUnionWorkaroundResolver.OPTIONS);
+
+ // works with explicit type specified.
+ MessagePackSerializer.Deserialize(serialized);
+
+ // works with custom resolver.
+ var deserialized = MessagePackSerializer.Deserialize(serialized, SignalRUnionWorkaroundResolver.OPTIONS);
+ Assert.IsTrue(deserialized is TeamVersusUserState);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs
index cef0532f9d..7170a76b8b 100644
--- a/osu.Game.Tests/Resources/TestResources.cs
+++ b/osu.Game.Tests/Resources/TestResources.cs
@@ -9,6 +9,8 @@ namespace osu.Game.Tests.Resources
{
public static class TestResources
{
+ public const double QUICK_BEATMAP_LENGTH = 10000;
+
public static DllResourceStore GetStore() => new DllResourceStore(typeof(TestResources).Assembly);
public static Stream OpenResource(string name) => GetStore().GetStream($"Resources/{name}");
diff --git a/osu.Game.Tests/Resources/countdown-settings.osu b/osu.Game.Tests/Resources/countdown-settings.osu
new file mode 100644
index 0000000000..333e48150d
--- /dev/null
+++ b/osu.Game.Tests/Resources/countdown-settings.osu
@@ -0,0 +1,5 @@
+osu file format v14
+
+[General]
+Countdown: 2
+CountdownOffset: 3
diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
index 184a94912a..f0d9ece06f 100644
--- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
+++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
@@ -36,9 +36,9 @@ namespace osu.Game.Tests.Rulesets.Scoring
[TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)]
[TestCase(ScoringMode.Standardised, HitResult.Ok, 800_000)]
[TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)]
- [TestCase(ScoringMode.Classic, HitResult.Meh, 50)]
- [TestCase(ScoringMode.Classic, HitResult.Ok, 100)]
- [TestCase(ScoringMode.Classic, HitResult.Great, 300)]
+ [TestCase(ScoringMode.Classic, HitResult.Meh, 41)]
+ [TestCase(ScoringMode.Classic, HitResult.Ok, 46)]
+ [TestCase(ScoringMode.Classic, HitResult.Great, 72)]
public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
scoreProcessor.Mode.Value = scoringMode;
@@ -85,19 +85,18 @@ namespace osu.Game.Tests.Rulesets.Scoring
[TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 575_000)] // (3 * 30) / (4 * 30) * 300_000 + (0 / 4) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points)
[TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points)
- [TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)] // (0 * 4 * 300) * (1 + 0 / 25)
- [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 156)] // (((3 * 50) / (4 * 300)) * 4 * 300) * (1 + 1 / 25)
- [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 312)] // (((3 * 100) / (4 * 300)) * 4 * 300) * (1 + 1 / 25)
- [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 594)] // (((3 * 200) / (4 * 350)) * 4 * 300) * (1 + 1 / 25)
- [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 936)] // (((3 * 300) / (4 * 300)) * 4 * 300) * (1 + 1 / 25)
- [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 936)] // (((3 * 350) / (4 * 350)) * 4 * 300) * (1 + 1 / 25)
- [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 0)] // (0 * 1 * 300) * (1 + 0 / 25)
- [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 225)] // (((3 * 10) / (4 * 10)) * 1 * 300) * (1 + 0 / 25)
- [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (0 * 4 * 300) * (1 + 0 / 25)
- [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 936)] // (((3 * 50) / (4 * 50)) * 4 * 300) * (1 + 1 / 25)
- // TODO: The following two cases don't match expectations currently (a single hit is registered in acc portion when it shouldn't be). See https://github.com/ppy/osu/issues/12604.
- [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 330)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points)
- [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 450)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 50 (bonus points)
+ [TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)]
+ [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 68)]
+ [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 81)]
+ [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 109)]
+ [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 149)]
+ [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 149)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 9)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 15)]
+ [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)]
+ [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 149)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 18)]
+ [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 18)]
public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore)
{
var minResult = new TestJudgement(hitResult).MinResult;
@@ -129,8 +128,8 @@ namespace osu.Game.Tests.Rulesets.Scoring
///
[TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, 978_571)] // (3 * 10 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, 914_286)] // (3 * 0 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000
- [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 279)] // (((3 * 10 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
- [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 214)] // (((3 * 0 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 69)] // (((3 * 10 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 60)] // (((3 * 0 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
public void TestSmallTicksAccuracy(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
IEnumerable hitObjects = Enumerable
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index bab8dfc983..7a9fc20426 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -138,16 +138,42 @@ namespace osu.Game.Tests.Skins.IO
}
}
- private MemoryStream createOsk(string name, string author)
+ [Test]
+ public async Task TestSameMetadataNameDifferentFolderName()
+ {
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest)))
+ {
+ try
+ {
+ var osu = LoadOsuIntoHost(host);
+
+ var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 1"));
+ Assert.That(imported.Name, Is.EqualTo("name 1 [my custom skin 1]"));
+ Assert.That(imported.Creator, Is.EqualTo("author 1"));
+
+ var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 2"));
+ Assert.That(imported2.Name, Is.EqualTo("name 1 [my custom skin 2]"));
+ Assert.That(imported2.Creator, Is.EqualTo("author 1"));
+
+ Assert.That(imported2.Hash, Is.Not.EqualTo(imported.Hash));
+ }
+ finally
+ {
+ host.Exit();
+ }
+ }
+ }
+
+ private MemoryStream createOsk(string name, string author, bool makeUnique = true)
{
var zipStream = new MemoryStream();
using var zip = ZipArchive.Create();
- zip.AddEntry("skin.ini", generateSkinIni(name, author));
+ zip.AddEntry("skin.ini", generateSkinIni(name, author, makeUnique));
zip.SaveTo(zipStream);
return zipStream;
}
- private MemoryStream generateSkinIni(string name, string author)
+ private MemoryStream generateSkinIni(string name, string author, bool makeUnique = true)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
@@ -155,8 +181,12 @@ namespace osu.Game.Tests.Skins.IO
writer.WriteLine("[General]");
writer.WriteLine($"Name: {name}");
writer.WriteLine($"Author: {author}");
- writer.WriteLine();
- writer.WriteLine($"# unique {Guid.NewGuid()}");
+
+ if (makeUnique)
+ {
+ writer.WriteLine();
+ writer.WriteLine($"# unique {Guid.NewGuid()}");
+ }
writer.Flush();
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs
index 6f5655006e..4813598c9d 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
@@ -30,7 +31,10 @@ namespace osu.Game.Tests.Visual.Editing
{
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
- Child = new ComposeScreen();
+ Child = new ComposeScreen
+ {
+ State = { Value = Visibility.Visible },
+ };
}
}
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs
new file mode 100644
index 0000000000..00f2979691
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs
@@ -0,0 +1,100 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Globalization;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Screens.Edit;
+using osu.Game.Screens.Edit.Setup;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ public class TestSceneDesignSection : OsuManualInputManagerTestScene
+ {
+ private TestDesignSection designSection;
+ private EditorBeatmap editorBeatmap { get; set; }
+
+ [SetUpSteps]
+ public void SetUp()
+ {
+ AddStep("create blank beatmap", () => editorBeatmap = new EditorBeatmap(new Beatmap()));
+ AddStep("create section", () => Child = new DependencyProvidingContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ CachedDependencies = new (Type, object)[]
+ {
+ (typeof(EditorBeatmap), editorBeatmap)
+ },
+ Child = designSection = new TestDesignSection()
+ });
+ }
+
+ [Test]
+ public void TestCountdownOff()
+ {
+ AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false);
+
+ AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.None);
+ AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent);
+ }
+
+ [Test]
+ public void TestCountdownOn()
+ {
+ AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
+
+ AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
+ AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent);
+
+ AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed);
+
+ AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.DoubleSpeed);
+ AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent);
+ }
+
+ [Test]
+ public void TestCountdownOffset()
+ {
+ AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
+
+ AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
+
+ checkOffsetAfter("1", 1);
+ checkOffsetAfter(string.Empty, 0);
+ checkOffsetAfter("123", 123);
+ checkOffsetAfter("0", 0);
+ }
+
+ private void checkOffsetAfter(string userInput, int expectedFinalValue)
+ {
+ AddStep("click text box", () =>
+ {
+ var textBox = designSection.CountdownOffset.ChildrenOfType().Single();
+ InputManager.MoveMouseTo(textBox);
+ InputManager.Click(MouseButton.Left);
+ });
+ AddStep("set offset text", () => designSection.CountdownOffset.Current.Value = userInput);
+ AddStep("commit text", () => InputManager.Key(Key.Enter));
+
+ AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture));
+ AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.BeatmapInfo.CountdownOffset == expectedFinalValue);
+ }
+
+ private class TestDesignSection : DesignSection
+ {
+ public new LabelledSwitchButton EnableCountdown => base.EnableCountdown;
+
+ public new FillFlowContainer CountdownSettings => base.CountdownSettings;
+ public new LabelledEnumDropdown CountdownSpeed => base.CountdownSpeed;
+ public new LabelledNumberBox CountdownOffset => base.CountdownOffset;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs
new file mode 100644
index 0000000000..0f3d413a7d
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs
@@ -0,0 +1,170 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Dialog;
+using osu.Game.Screens.Edit;
+using osu.Game.Screens.Edit.Components.Menus;
+using osu.Game.Screens.Menu;
+using osu.Game.Tests.Beatmaps.IO;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ public class TestSceneDifficultySwitching : ScreenTestScene
+ {
+ private BeatmapSetInfo importedBeatmapSet;
+ private Editor editor;
+
+ // required for screen transitions to work properly
+ // (see comment in EditorLoader.LogoArriving).
+ [Cached]
+ private OsuLogo logo = new OsuLogo
+ {
+ Alpha = 0
+ };
+
+ [Resolved]
+ private OsuGameBase game { get; set; }
+
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
+
+ [BackgroundDependencyLoader]
+ private void load() => Add(logo);
+
+ [SetUpSteps]
+ public void SetUp()
+ {
+ AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result);
+
+ AddStep("set current beatmap", () => Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First()));
+ AddStep("push loader", () => Stack.Push(new EditorLoader()));
+
+ AddUntilStep("wait for editor push", () => Stack.CurrentScreen is Editor);
+ AddStep("store editor", () => editor = (Editor)Stack.CurrentScreen);
+ AddUntilStep("wait for editor to load", () => editor.IsLoaded);
+ }
+
+ [Test]
+ public void TestBasicSwitch()
+ {
+ BeatmapInfo targetDifficulty = null;
+
+ AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo)));
+ switchToDifficulty(() => targetDifficulty);
+ confirmEditingBeatmap(() => targetDifficulty);
+
+ AddStep("exit editor", () => Stack.Exit());
+ // ensure editor loader didn't resume.
+ AddAssert("stack empty", () => Stack.CurrentScreen == null);
+ }
+
+ [Test]
+ public void TestPreventSwitchDueToUnsavedChanges()
+ {
+ BeatmapInfo targetDifficulty = null;
+ PromptForSaveDialog saveDialog = null;
+
+ AddStep("remove first hitobject", () =>
+ {
+ var editorBeatmap = editor.ChildrenOfType().Single();
+ editorBeatmap.RemoveAt(0);
+ });
+
+ AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo)));
+ switchToDifficulty(() => targetDifficulty);
+
+ AddUntilStep("prompt for save dialog shown", () =>
+ {
+ saveDialog = this.ChildrenOfType().Single();
+ return saveDialog != null;
+ });
+ AddStep("continue editing", () =>
+ {
+ var continueButton = saveDialog.ChildrenOfType().Last();
+ continueButton.TriggerClick();
+ });
+
+ confirmEditingBeatmap(() => importedBeatmapSet.Beatmaps.First());
+
+ AddRepeatStep("exit editor forcefully", () => Stack.Exit(), 2);
+ // ensure editor loader didn't resume.
+ AddAssert("stack empty", () => Stack.CurrentScreen == null);
+ }
+
+ [Test]
+ public void TestAllowSwitchAfterDiscardingUnsavedChanges()
+ {
+ BeatmapInfo targetDifficulty = null;
+ PromptForSaveDialog saveDialog = null;
+
+ AddStep("remove first hitobject", () =>
+ {
+ var editorBeatmap = editor.ChildrenOfType().Single();
+ editorBeatmap.RemoveAt(0);
+ });
+
+ AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo)));
+ switchToDifficulty(() => targetDifficulty);
+
+ AddUntilStep("prompt for save dialog shown", () =>
+ {
+ saveDialog = this.ChildrenOfType().Single();
+ return saveDialog != null;
+ });
+ AddStep("discard changes", () =>
+ {
+ var continueButton = saveDialog.ChildrenOfType().Single();
+ continueButton.TriggerClick();
+ });
+
+ confirmEditingBeatmap(() => targetDifficulty);
+
+ AddStep("exit editor forcefully", () => Stack.Exit());
+ // ensure editor loader didn't resume.
+ AddAssert("stack empty", () => Stack.CurrentScreen == null);
+ }
+
+ private void switchToDifficulty(Func difficulty)
+ {
+ AddUntilStep("wait for menubar to load", () => editor.ChildrenOfType().Any());
+ AddStep("open file menu", () =>
+ {
+ var menuBar = editor.ChildrenOfType().Single();
+ var fileMenu = menuBar.ChildrenOfType().First();
+ InputManager.MoveMouseTo(fileMenu);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ AddStep("open difficulty menu", () =>
+ {
+ var difficultySelector =
+ editor.ChildrenOfType().Single(item => item.Item.Text.Value.ToString().Contains("Change difficulty"));
+ InputManager.MoveMouseTo(difficultySelector);
+ });
+ AddWaitStep("wait for open", 3);
+
+ AddStep("switch to target difficulty", () =>
+ {
+ var difficultyMenuItem =
+ editor.ChildrenOfType()
+ .Last(item => item.Item is DifficultyMenuItem difficultyItem && difficultyItem.Beatmap.Equals(difficulty.Invoke()));
+ InputManager.MoveMouseTo(difficultyMenuItem);
+ InputManager.Click(MouseButton.Left);
+ });
+ }
+
+ private void confirmEditingBeatmap(Func targetDifficulty)
+ {
+ AddUntilStep("current beatmap is correct", () => Beatmap.Value.BeatmapInfo.Equals(targetDifficulty.Invoke()));
+ AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs
index 0b1617b6a6..0abf0c47f8 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs
@@ -78,6 +78,24 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500);
}
+ [Test]
+ public void TestClampWhenSeekOutsideBeatmapBounds()
+ {
+ AddStep("stop clock", Clock.Stop);
+
+ AddStep("seek before start time", () => Clock.Seek(-1000));
+ AddAssert("time is clamped to 0", () => Clock.CurrentTime == 0);
+
+ AddStep("seek beyond track length", () => Clock.Seek(Clock.TrackLength + 1000));
+ AddAssert("time is clamped to track length", () => Clock.CurrentTime == Clock.TrackLength);
+
+ AddStep("seek smoothly before start time", () => Clock.SeekSmoothlyTo(-1000));
+ AddAssert("time is clamped to 0", () => Clock.CurrentTime == 0);
+
+ AddStep("seek smoothly beyond track length", () => Clock.SeekSmoothlyTo(Clock.TrackLength + 1000));
+ AddAssert("time is clamped to track length", () => Clock.CurrentTime == Clock.TrackLength);
+ }
+
protected override void Dispose(bool isDisposing)
{
Beatmap.Disabled = false;
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorScreenModes.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorScreenModes.cs
new file mode 100644
index 0000000000..98d8a41674
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorScreenModes.cs
@@ -0,0 +1,29 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Testing;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.Edit;
+using osu.Game.Screens.Edit.Components.Menus;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ public class TestSceneEditorScreenModes : EditorTestScene
+ {
+ protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
+
+ [Test]
+ public void TestSwitchScreensInstantaneously()
+ {
+ AddStep("switch between all screens at once", () =>
+ {
+ foreach (var screen in Enum.GetValues(typeof(EditorScreenMode)).Cast())
+ Editor.ChildrenOfType().Single().Mode.Value = screen;
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs
index 96ce418851..ff741a8ed5 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Editing
beatmap.BeatmapInfo.BeatDivisor = 1;
- beatmap.ControlPointInfo = new ControlPointInfo();
+ beatmap.ControlPointInfo.Clear();
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 });
beatmap.ControlPointInfo.Add(2000, new TimingControlPoint { BeatLength = 500 });
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
index 62e12158ab..c3c803ff23 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
@@ -3,8 +3,14 @@
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
+using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Setup;
@@ -22,11 +28,31 @@ namespace osu.Game.Tests.Visual.Editing
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
}
- [BackgroundDependencyLoader]
- private void load()
+ [Test]
+ public void TestOsu() => runForRuleset(new OsuRuleset().RulesetInfo);
+
+ [Test]
+ public void TestTaiko() => runForRuleset(new TaikoRuleset().RulesetInfo);
+
+ [Test]
+ public void TestCatch() => runForRuleset(new CatchRuleset().RulesetInfo);
+
+ [Test]
+ public void TestMania() => runForRuleset(new ManiaRuleset().RulesetInfo);
+
+ private void runForRuleset(RulesetInfo rulesetInfo)
{
- Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
- Child = new SetupScreen();
+ AddStep("create screen", () =>
+ {
+ editorBeatmap.BeatmapInfo.Ruleset = rulesetInfo;
+
+ Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
+
+ Child = new SetupScreen
+ {
+ State = { Value = Visibility.Visible },
+ };
+ });
}
}
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
index b82e776164..f961fff1e5 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit;
@@ -30,7 +31,10 @@ namespace osu.Game.Tests.Visual.Editing
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
Beatmap.Disabled = true;
- Child = new TimingScreen();
+ Child = new TimingScreen
+ {
+ State = { Value = Visibility.Visible },
+ };
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs
index b7dcad3825..00b5c38e20 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Configuration;
@@ -71,7 +70,7 @@ namespace osu.Game.Tests.Visual.Gameplay
var working = CreateWorkingBeatmap(rulesetInfo);
Beatmap.Value = working;
- SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };
+ SelectedMods.Value = new[] { ruleset.CreateMod() };
Player = CreatePlayer(ruleset);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs
new file mode 100644
index 0000000000..fccc1a377c
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs
@@ -0,0 +1,135 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Game.Audio;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.UI;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ public class TestSceneGameplaySampleTriggerSource : PlayerTestScene
+ {
+ private TestGameplaySampleTriggerSource sampleTriggerSource;
+ protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
+
+ private Beatmap beatmap;
+
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
+ {
+ beatmap = new Beatmap
+ {
+ BeatmapInfo = new BeatmapInfo
+ {
+ BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
+ Ruleset = ruleset
+ }
+ };
+
+ const double start_offset = 8000;
+ const double spacing = 2000;
+
+ double t = start_offset;
+ beatmap.HitObjects.AddRange(new[]
+ {
+ new HitCircle
+ {
+ // intentionally start objects a bit late so we can test the case of no alive objects.
+ StartTime = t += spacing,
+ Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
+ },
+ new HitCircle
+ {
+ StartTime = t += spacing,
+ Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) }
+ },
+ new HitCircle
+ {
+ StartTime = t += spacing,
+ Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) },
+ SampleControlPoint = new SampleControlPoint { SampleBank = "soft" },
+ },
+ new HitCircle
+ {
+ StartTime = t + spacing,
+ Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) },
+ SampleControlPoint = new SampleControlPoint { SampleBank = "soft" },
+ },
+ });
+
+ return beatmap;
+ }
+
+ public override void SetUpSteps()
+ {
+ base.SetUpSteps();
+
+ AddStep("Add trigger source", () => Player.HUDOverlay.Add(sampleTriggerSource = new TestGameplaySampleTriggerSource(Player.DrawableRuleset.Playfield.HitObjectContainer)));
+ }
+
+ [Test]
+ public void TestCorrectHitObject()
+ {
+ HitObjectLifetimeEntry nextObjectEntry = null;
+
+ AddAssert("no alive objects", () => getNextAliveObject() == null);
+
+ AddAssert("check initially correct object", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[0]);
+
+ AddUntilStep("get next object", () =>
+ {
+ var nextDrawableObject = getNextAliveObject();
+
+ if (nextDrawableObject != null)
+ {
+ nextObjectEntry = nextDrawableObject.Entry;
+ InputManager.MoveMouseTo(nextDrawableObject.ScreenSpaceDrawQuad.Centre);
+ return true;
+ }
+
+ return false;
+ });
+
+ AddUntilStep("hit first hitobject", () =>
+ {
+ InputManager.Click(MouseButton.Left);
+ return nextObjectEntry.Result.HasResult;
+ });
+
+ AddAssert("check correct object after hit", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[1]);
+
+ AddUntilStep("check correct object after miss", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[2]);
+ AddUntilStep("check correct object after miss", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[3]);
+
+ AddUntilStep("no alive objects", () => getNextAliveObject() == null);
+ AddAssert("check correct object after none alive", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[3]);
+ }
+
+ private DrawableHitObject getNextAliveObject() =>
+ Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.FirstOrDefault();
+
+ [Test]
+ public void TestSampleTriggering()
+ {
+ AddRepeatStep("trigger sample", () => sampleTriggerSource.Play(), 10);
+ }
+
+ public class TestGameplaySampleTriggerSource : GameplaySampleTriggerSource
+ {
+ public TestGameplaySampleTriggerSource(HitObjectContainer hitObjectContainer)
+ : base(hitObjectContainer)
+ {
+ }
+
+ public new HitObject GetMostValidObject() => base.GetMostValidObject();
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
index 290ba3317b..4b54cd3510 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
@@ -142,7 +142,11 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
createNew();
+
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
+ AddUntilStep("wait for components to be hidden", () => !hudOverlay.ChildrenOfType().Single().IsPresent);
+
+ AddStep("reload components", () => hudOverlay.ChildrenOfType().Single().Reload());
AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType().Single().ComponentsLoaded);
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs
index 4fa4c00981..b308f3d7d8 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs
@@ -3,7 +3,6 @@
using System.Linq;
using NUnit.Framework;
-using osu.Framework.Bindables;
using osu.Game.Overlays;
using osu.Game.Rulesets;
@@ -66,7 +65,6 @@ namespace osu.Game.Tests.Visual.Gameplay
protected class OverlayTestPlayer : TestPlayer
{
public new OverlayActivation OverlayActivationMode => base.OverlayActivationMode.Value;
- public new Bindable LocalUserPlaying => base.LocalUserPlaying;
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs
index 606395c289..9750838433 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs
@@ -74,14 +74,14 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestAddControlPoint()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100))));
- AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = { Value = new Vector2(100) } }));
+ AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = new Vector2(100) }));
}
[Test]
public void TestInsertControlPoint()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100))));
- AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = { Value = new Vector2(0, 100) } }));
+ AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = new Vector2(0, 100) }));
}
[Test]
@@ -95,14 +95,14 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestChangePathType()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
- AddStep("change type to bezier", () => path.ControlPoints[0].Type.Value = PathType.Bezier);
+ AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.Bezier);
}
[Test]
public void TestAddSegmentByChangingType()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))));
- AddStep("change second point type to bezier", () => path.ControlPoints[1].Type.Value = PathType.Bezier);
+ AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.Bezier);
}
[Test]
@@ -111,10 +111,10 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("create path", () =>
{
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
- path.ControlPoints[1].Type.Value = PathType.Bezier;
+ path.ControlPoints[1].Type = PathType.Bezier;
});
- AddStep("change second point type to null", () => path.ControlPoints[1].Type.Value = null);
+ AddStep("change second point type to null", () => path.ControlPoints[1].Type = null);
}
[Test]
@@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("create path", () =>
{
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
- path.ControlPoints[1].Type.Value = PathType.Bezier;
+ path.ControlPoints[1].Type = PathType.Bezier;
});
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
@@ -185,8 +185,8 @@ namespace osu.Game.Tests.Visual.Gameplay
private List createSegment(PathType type, params Vector2[] controlPoints)
{
- var points = controlPoints.Select(p => new PathControlPoint { Position = { Value = p } }).ToList();
- points[0].Type.Value = type;
+ var points = controlPoints.Select(p => new PathControlPoint { Position = p }).ToList();
+ points[0].Type = type;
return points;
}
}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
index aaf3323432..9037338e23 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
@@ -10,7 +10,6 @@ using osu.Game.Database;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
using osu.Game.Tests.Resources;
-using osu.Game.Tests.Visual.Navigation;
namespace osu.Game.Tests.Visual.Menus
{
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs
index e58f85b0b3..e34ec6c46a 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs
@@ -1,10 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
+using osu.Framework.Utils;
+using osu.Game.Configuration;
using osu.Game.Overlays;
-using osu.Game.Tests.Visual.Navigation;
namespace osu.Game.Tests.Visual.Menus
{
@@ -22,21 +25,48 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestScreenOffsettingOnSettingsOverlay()
{
- AddStep("open settings", () => Game.Settings.Show());
- AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO);
+ foreach (var scalingMode in Enum.GetValues(typeof(ScalingMode)).Cast())
+ {
+ AddStep($"set scaling mode to {scalingMode}", () =>
+ {
+ Game.LocalConfig.SetValue(OsuSetting.Scaling, scalingMode);
- AddStep("hide settings", () => Game.Settings.Hide());
- AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f);
+ if (scalingMode != ScalingMode.Off)
+ {
+ Game.LocalConfig.SetValue(OsuSetting.ScalingSizeX, 0.5f);
+ Game.LocalConfig.SetValue(OsuSetting.ScalingSizeY, 0.5f);
+ }
+ });
+
+ AddStep("open settings", () => Game.Settings.Show());
+ AddUntilStep("right screen offset applied", () => Precision.AlmostEquals(Game.ScreenOffsetContainer.X, SettingsPanel.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO));
+
+ AddStep("hide settings", () => Game.Settings.Hide());
+ AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f);
+ }
}
[Test]
public void TestScreenOffsettingOnNotificationOverlay()
{
- AddStep("open notifications", () => Game.Notifications.Show());
- AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == -NotificationOverlay.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO);
+ foreach (var scalingMode in Enum.GetValues(typeof(ScalingMode)).Cast())
+ {
+ if (scalingMode != ScalingMode.Off)
+ {
+ AddStep($"set scaling mode to {scalingMode}", () =>
+ {
+ Game.LocalConfig.SetValue(OsuSetting.Scaling, scalingMode);
+ Game.LocalConfig.SetValue(OsuSetting.ScalingSizeX, 0.5f);
+ Game.LocalConfig.SetValue(OsuSetting.ScalingSizeY, 0.5f);
+ });
+ }
- AddStep("hide notifications", () => Game.Notifications.Hide());
- AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f);
+ AddStep("open notifications", () => Game.Notifications.Show());
+ AddUntilStep("right screen offset applied", () => Precision.AlmostEquals(Game.ScreenOffsetContainer.X, -NotificationOverlay.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO));
+
+ AddStep("hide notifications", () => Game.Notifications.Hide());
+ AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f);
+ }
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
index 299bbacf08..b1f5781f6f 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
@@ -10,11 +10,11 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms;
using osu.Game.Online.Rooms.RoomStatuses;
using osu.Game.Overlays;
using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
@@ -24,12 +24,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneDrawableRoom : OsuTestScene
{
- [Cached]
- private readonly Bindable selectedRoom = new Bindable();
-
[Cached]
protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
+ private readonly Bindable selectedRoom = new Bindable();
+
[Test]
public void TestMultipleStatuses()
{
@@ -108,12 +107,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
EndDate = { Value = DateTimeOffset.Now },
}),
createDrawableRoom(new Room
- {
- Name = { Value = "Room 4 (realtime)" },
- Status = { Value = new RoomStatusOpen() },
- Category = { Value = RoomCategory.Realtime },
- }),
- createDrawableRoom(new Room
{
Name = { Value = "Room 4 (spotlight)" },
Status = { Value = new RoomStatusOpen() },
@@ -134,9 +127,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
Name = { Value = "Room with password" },
Status = { Value = new RoomStatusOpen() },
- Category = { Value = RoomCategory.Realtime },
+ Type = { Value = MatchType.HeadToHead },
}));
+ AddUntilStep("wait for panel load", () => drawableRoom.ChildrenOfType().Any());
+
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha));
AddStep("set password", () => room.Password.Value = "password");
@@ -159,10 +154,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
}));
}
- var drawableRoom = new DrawableRoom(room) { MatchingFilter = true };
- drawableRoom.Action = () => drawableRoom.State = drawableRoom.State == SelectionState.Selected ? SelectionState.NotSelected : SelectionState.Selected;
-
- return drawableRoom;
+ return new DrawableLoungeRoom(room)
+ {
+ MatchingFilter = true,
+ SelectedRoom = { BindTarget = selectedRoom }
+ };
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs
new file mode 100644
index 0000000000..a3a1cacb0d
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs
@@ -0,0 +1,131 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using Moq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Testing;
+using osu.Game.Screens.OnlinePlay.Multiplayer;
+using osu.Game.Screens.Play;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ public class TestSceneGameplayChatDisplay : MultiplayerTestScene
+ {
+ private GameplayChatDisplay chatDisplay;
+
+ [Cached(typeof(ILocalUserPlayInfo))]
+ private ILocalUserPlayInfo localUserInfo;
+
+ private readonly Bindable localUserPlaying = new Bindable();
+
+ private TextBox textBox => chatDisplay.ChildrenOfType().First();
+
+ public TestSceneGameplayChatDisplay()
+ {
+ var mockLocalUserInfo = new Mock();
+ mockLocalUserInfo.SetupGet(i => i.IsPlaying).Returns(localUserPlaying);
+
+ localUserInfo = mockLocalUserInfo.Object;
+ }
+
+ [SetUpSteps]
+ public override void SetUpSteps()
+ {
+ base.SetUpSteps();
+
+ AddStep("load chat display", () => Child = chatDisplay = new GameplayChatDisplay(SelectedRoom.Value)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Width = 0.5f,
+ });
+
+ AddStep("expand", () => chatDisplay.Expanded.Value = true);
+ }
+
+ [Test]
+ public void TestCantClickWhenPlaying()
+ {
+ setLocalUserPlaying(true);
+
+ AddStep("attempt focus chat", () =>
+ {
+ InputManager.MoveMouseTo(textBox);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ assertChatFocused(false);
+ }
+
+ [Test]
+ public void TestFocusDroppedWhenPlaying()
+ {
+ assertChatFocused(false);
+
+ AddStep("focus chat", () =>
+ {
+ InputManager.MoveMouseTo(textBox);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ setLocalUserPlaying(true);
+ assertChatFocused(false);
+
+ // should still stay non-focused even after entering a new break section.
+ setLocalUserPlaying(false);
+ assertChatFocused(false);
+ }
+
+ [Test]
+ public void TestFocusOnTabKeyWhenExpanded()
+ {
+ setLocalUserPlaying(true);
+
+ assertChatFocused(false);
+ AddStep("press tab", () => InputManager.Key(Key.Tab));
+ assertChatFocused(true);
+ }
+
+ [Test]
+ public void TestFocusOnTabKeyWhenNotExpanded()
+ {
+ AddStep("set not expanded", () => chatDisplay.Expanded.Value = false);
+ AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
+
+ AddStep("press tab", () => InputManager.Key(Key.Tab));
+ assertChatFocused(true);
+ AddUntilStep("is visible", () => chatDisplay.IsPresent);
+
+ AddStep("press enter", () => InputManager.Key(Key.Enter));
+ assertChatFocused(false);
+ AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
+ }
+
+ [Test]
+ public void TestFocusToggleViaAction()
+ {
+ AddStep("set not expanded", () => chatDisplay.Expanded.Value = false);
+ AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
+
+ AddStep("press tab", () => InputManager.Key(Key.Tab));
+ assertChatFocused(true);
+ AddUntilStep("is visible", () => chatDisplay.IsPresent);
+
+ AddStep("press tab", () => InputManager.Key(Key.Tab));
+ assertChatFocused(false);
+ AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
+ }
+
+ private void assertChatFocused(bool isFocused) =>
+ AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused);
+
+ private void setLocalUserPlaying(bool playing) =>
+ AddStep($"local user {(playing ? "playing" : "not playing")}", () => localUserPlaying.Value = playing);
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index 7e822f898e..99b530c2a2 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -9,6 +9,7 @@ using osu.Framework.Testing;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input;
@@ -17,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneLoungeRoomsContainer : OnlinePlayTestScene
{
- protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
+ protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
private RoomsContainer container;
@@ -29,6 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 0.5f,
+ SelectedRoom = { BindTarget = SelectedRoom }
};
});
@@ -38,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add rooms", () => RoomManager.AddRooms(3));
AddAssert("has 3 rooms", () => container.Rooms.Count == 3);
- AddStep("remove first room", () => RoomManager.Rooms.Remove(RoomManager.Rooms.FirstOrDefault()));
+ AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.FirstOrDefault()));
AddAssert("has 2 rooms", () => container.Rooms.Count == 2);
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
@@ -150,6 +152,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
private bool checkRoomSelected(Room room) => SelectedRoom.Value == room;
private Room getRoomInFlow(int index) =>
- (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
+ (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs
deleted file mode 100644
index 71ba5db481..0000000000
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using NUnit.Framework;
-using osu.Game.Beatmaps;
-using osu.Game.Online.Rooms;
-using osu.Game.Rulesets.Osu;
-using osu.Game.Rulesets.Osu.Mods;
-using osu.Game.Screens.OnlinePlay.Match.Components;
-using osu.Game.Tests.Visual.OnlinePlay;
-using osu.Game.Users;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- public class TestSceneMatchHeader : OnlinePlayTestScene
- {
- [SetUp]
- public new void Setup() => Schedule(() =>
- {
- SelectedRoom.Value = new Room
- {
- Name = { Value = "A very awesome room" },
- Host = { Value = new User { Id = 2, Username = "peppy" } },
- Playlist =
- {
- new PlaylistItem
- {
- Beatmap =
- {
- Value = new BeatmapInfo
- {
- Metadata = new BeatmapMetadata
- {
- Title = "Title",
- Artist = "Artist",
- AuthorString = "Author",
- },
- Version = "Version",
- Ruleset = new OsuRuleset().RulesetInfo
- }
- },
- RequiredMods =
- {
- new OsuModDoubleTime(),
- new OsuModNoFail(),
- new OsuModRelax(),
- }
- }
- }
- };
-
- Child = new Header();
- });
- }
-}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
index 18e4a6c575..bfcb55ce33 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
@@ -19,6 +20,7 @@ using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Tests.Beatmaps.IO;
using osu.Game.Users;
+using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Multiplayer
{
@@ -78,7 +80,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestGeneral()
{
- int[] userIds = Enumerable.Range(0, 4).Select(i => PLAYER_1_ID + i).ToArray();
+ int[] userIds = getPlayerIds(4);
start(userIds);
loadSpectateScreen();
@@ -314,6 +316,36 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("player 2 playing from correct point in time", () => getPlayer(PLAYER_2_ID).ChildrenOfType().Single().FrameStableClock.CurrentTime > 30000);
}
+ [Test]
+ public void TestPlayersLeaveWhileSpectating()
+ {
+ start(getPlayerIds(4));
+ sendFrames(getPlayerIds(4), 300);
+
+ loadSpectateScreen();
+
+ for (int count = 3; count >= 0; count--)
+ {
+ var id = PLAYER_1_ID + count;
+
+ end(id);
+ AddUntilStep($"{id} area grayed", () => getInstance(id).Colour != Color4.White);
+ AddUntilStep($"{id} score quit set", () => getLeaderboardScore(id).HasQuit.Value);
+ sendFrames(getPlayerIds(count), 300);
+ }
+
+ Player player = null;
+
+ AddStep($"get {PLAYER_1_ID} player instance", () => player = getInstance(PLAYER_1_ID).ChildrenOfType().Single());
+
+ start(new[] { PLAYER_1_ID });
+ sendFrames(PLAYER_1_ID, 300);
+
+ AddAssert($"{PLAYER_1_ID} player instance still same", () => getInstance(PLAYER_1_ID).ChildrenOfType().Single() == player);
+ AddAssert($"{PLAYER_1_ID} area still grayed", () => getInstance(PLAYER_1_ID).Colour != Color4.White);
+ AddAssert($"{PLAYER_1_ID} score quit still set", () => getLeaderboardScore(PLAYER_1_ID).HasQuit.Value);
+ }
+
private void loadSpectateScreen(bool waitForPlayerLoad = true)
{
AddStep("load screen", () =>
@@ -333,14 +365,32 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
foreach (int id in userIds)
{
- OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true);
+ var user = new MultiplayerRoomUser(id)
+ {
+ User = new User { Id = id },
+ };
+ OnlinePlayDependencies.Client.AddUser(user.User, true);
SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId);
- playingUsers.Add(new MultiplayerRoomUser(id));
+
+ playingUsers.Add(user);
}
});
}
+ private void end(int userId)
+ {
+ AddStep($"end play for {userId}", () =>
+ {
+ var user = playingUsers.Single(u => u.UserID == userId);
+
+ OnlinePlayDependencies.Client.RemoveUser(user.User.AsNonNull());
+ SpectatorClient.EndPlay(userId);
+
+ playingUsers.Remove(user);
+ });
+ }
+
private void sendFrames(int userId, int count = 10) => sendFrames(new[] { userId }, count);
private void sendFrames(int[] userIds, int count = 10)
@@ -374,5 +424,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
private Player getPlayer(int userId) => getInstance(userId).ChildrenOfType().Single();
private PlayerArea getInstance(int userId) => spectatorScreen.ChildrenOfType().Single(p => p.UserId == userId);
+
+ private GameplayLeaderboardScore getLeaderboardScore(int userId) => spectatorScreen.ChildrenOfType().Single(s => s.User?.Id == userId);
+
+ private int[] getPlayerIds(int count) => Enumerable.Range(PLAYER_1_ID, count).ToArray();
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
index e618b28f40..0b70703870 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Diagnostics;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
@@ -12,6 +13,7 @@ using osu.Framework.Input;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
+using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
@@ -28,6 +30,8 @@ using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
+using osu.Game.Screens.Play;
+using osu.Game.Screens.Ranking;
using osu.Game.Tests.Resources;
using osu.Game.Users;
using osuTK.Input;
@@ -92,6 +96,120 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("empty step", () => { });
}
+ [Test]
+ public void TestLobbyEvents()
+ {
+ createRoom(() => new Room
+ {
+ Name = { Value = "Test Room" },
+ Playlist =
+ {
+ new PlaylistItem
+ {
+ Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
+ Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ }
+ }
+ });
+
+ AddRepeatStep("random stuff happens", performRandomAction, 30);
+
+ // ensure we have a handful of players so the ready-up sounds good :9
+ AddRepeatStep("player joins", addRandomPlayer, 5);
+
+ // all ready
+ AddUntilStep("all players ready", () =>
+ {
+ var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
+ if (nextUnready != null)
+ client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
+
+ return client.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true;
+ });
+
+ AddStep("unready all players at once", () =>
+ {
+ Debug.Assert(client.Room != null);
+
+ foreach (var u in client.Room.Users) client.ChangeUserState(u.UserID, MultiplayerUserState.Idle);
+ });
+
+ AddStep("ready all players at once", () =>
+ {
+ Debug.Assert(client.Room != null);
+
+ foreach (var u in client.Room.Users) client.ChangeUserState(u.UserID, MultiplayerUserState.Ready);
+ });
+ }
+
+ private void addRandomPlayer()
+ {
+ int randomUser = RNG.Next(200000, 500000);
+ client.AddUser(new User { Id = randomUser, Username = $"user {randomUser}" });
+ }
+
+ private void removeLastUser()
+ {
+ User lastUser = client.Room?.Users.Last().User;
+
+ if (lastUser == null || lastUser == client.LocalUser?.User)
+ return;
+
+ client.RemoveUser(lastUser);
+ }
+
+ private void kickLastUser()
+ {
+ User lastUser = client.Room?.Users.Last().User;
+
+ if (lastUser == null || lastUser == client.LocalUser?.User)
+ return;
+
+ client.KickUser(lastUser.Id);
+ }
+
+ private void markNextPlayerReady()
+ {
+ var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
+ if (nextUnready != null)
+ client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
+ }
+
+ private void markNextPlayerIdle()
+ {
+ var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready);
+ if (nextUnready != null)
+ client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle);
+ }
+
+ private void performRandomAction()
+ {
+ int eventToPerform = RNG.Next(1, 6);
+
+ switch (eventToPerform)
+ {
+ case 1:
+ addRandomPlayer();
+ break;
+
+ case 2:
+ removeLastUser();
+ break;
+
+ case 3:
+ kickLastUser();
+ break;
+
+ case 4:
+ markNextPlayerReady();
+ break;
+
+ case 5:
+ markNextPlayerIdle();
+ break;
+ }
+ }
+
[Test]
public void TestCreateRoomViaKeyboard()
{
@@ -236,8 +354,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter));
- DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
- AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
+ DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
+ AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick());
@@ -411,7 +529,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("invoke on back button", () => multiplayerScreen.OnBackButton());
- AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden);
+ AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden);
AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden);
@@ -430,6 +548,40 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
}
+ [Test]
+ public void TestGameplayFlow()
+ {
+ createRoom(() => new Room
+ {
+ Name = { Value = "Test Room" },
+ Playlist =
+ {
+ new PlaylistItem
+ {
+ Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
+ Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ }
+ }
+ });
+
+ AddRepeatStep("click spectate button", () =>
+ {
+ InputManager.MoveMouseTo(this.ChildrenOfType().Single());
+ InputManager.Click(MouseButton.Left);
+ }, 2);
+
+ AddUntilStep("wait for player", () => Stack.CurrentScreen is Player);
+
+ // Gameplay runs in real-time, so we need to incrementally check if gameplay has finished in order to not time out.
+ for (double i = 1000; i < TestResources.QUICK_BEATMAP_LENGTH; i += 1000)
+ {
+ var time = i;
+ AddUntilStep($"wait for time > {i}", () => this.ChildrenOfType().SingleOrDefault()?.GameplayClock.CurrentTime > time);
+ }
+
+ AddUntilStep("wait for results", () => Stack.CurrentScreen is ResultsScreen);
+ }
+
private void createRoom(Func room)
{
AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
index c66d5429d6..61565c88f4 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -9,7 +9,6 @@ using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge;
-using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input;
@@ -18,7 +17,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene
{
- protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
+ protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
private LoungeSubScreen loungeScreen;
@@ -52,6 +51,24 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("room join password correct", () => lastJoinedPassword == null);
}
+ [Test]
+ public void TestPopoverHidesOnBackButton()
+ {
+ AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ AddStep("select room", () => InputManager.Key(Key.Down));
+ AddStep("attempt join room", () => InputManager.Key(Key.Enter));
+
+ AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any());
+
+ AddAssert("textbox has focus", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
+
+ AddStep("hit escape", () => InputManager.Key(Key.Escape));
+ AddAssert("textbox lost focus", () => InputManager.FocusedDrawable is SearchTextBox);
+
+ AddStep("hit escape", () => InputManager.Key(Key.Escape));
+ AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any());
+ }
+
[Test]
public void TestPopoverHidesOnLeavingScreen()
{
@@ -59,20 +76,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
- AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any());
+ AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any());
AddStep("exit screen", () => Stack.Exit());
- AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any());
+ AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any());
}
[Test]
public void TestJoinRoomWithPassword()
{
- DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
+ DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
- AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
+ AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick());
@@ -83,12 +100,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestJoinRoomWithPasswordViaKeyboardOnly()
{
- DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
+ DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
- AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
+ AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press enter", () => InputManager.Key(Key.Enter));
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
index 8bcb9cebbc..0d6b428cce 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
@@ -15,6 +15,7 @@ using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
+using osu.Game.Online.Rooms;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
@@ -89,7 +90,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedMods.SetDefault();
});
- AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect()));
+ AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value)));
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen());
}
@@ -168,6 +169,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
public new Bindable> FreeMods => base.FreeMods;
public new BeatmapCarousel Carousel => base.Carousel;
+
+ public TestMultiplayerMatchSongSelect(Room room, WorkingBeatmap beatmap = null, RulesetInfo ruleset = null)
+ : base(room, beatmap, ruleset)
+ {
+ }
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
index ea10fc1b8b..21364fe154 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
@@ -59,23 +59,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for load", () => screen.IsCurrentScreen());
}
- [Test]
- public void TestSettingValidity()
- {
- AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value);
-
- AddStep("set playlist", () =>
- {
- SelectedRoom.Value.Playlist.Add(new PlaylistItem
- {
- Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- });
- });
-
- AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value);
- }
-
[Test]
public void TestCreatedRoom()
{
@@ -97,6 +80,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for join", () => Client.Room != null);
}
+ [Test]
+ public void TestSettingValidity()
+ {
+ AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value);
+
+ AddStep("set playlist", () =>
+ {
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem
+ {
+ Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
+ Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ });
+ });
+
+ AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value);
+ }
+
[Test]
public void TestStartMatchWhileSpectating()
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
new file mode 100644
index 0000000000..a1543f99e1
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
@@ -0,0 +1,38 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Testing;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.OnlinePlay.Multiplayer;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ public class TestSceneMultiplayerPlayer : MultiplayerTestScene
+ {
+ private MultiplayerPlayer player;
+
+ [SetUpSteps]
+ public override void SetUpSteps()
+ {
+ base.SetUpSteps();
+
+ AddStep("set beatmap", () =>
+ {
+ Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
+ });
+
+ AddStep("initialise gameplay", () =>
+ {
+ Stack.Push(player = new MultiplayerPlayer(Client.APIRoom, Client.CurrentMatchPlayingItem.Value, Client.Room?.Users.ToArray()));
+ });
+ }
+
+ [Test]
+ public void TestGameplay()
+ {
+ AddUntilStep("wait for gameplay start", () => player.LocalUserPlaying.Value);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
index e4bf9b36ed..ba30315663 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
@@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedMods.Value = Array.Empty();
});
- AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect()));
+ AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value)));
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen());
}
@@ -183,6 +183,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
private class TestPlaylistsSongSelect : PlaylistsSongSelect
{
public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails;
+
+ public TestPlaylistsSongSelect(Room room)
+ : base(room)
+ {
+ }
}
}
}
diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs
index 26641214b1..b8232837b5 100644
--- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs
@@ -10,6 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures;
using osu.Framework.Platform;
+using osu.Framework.Testing;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
@@ -88,21 +89,27 @@ namespace osu.Game.Tests.Visual.Navigation
[Resolved]
private OsuGameBase gameBase { get; set; }
- [BackgroundDependencyLoader]
- private void load(GameHost host)
- {
- game = new OsuGame();
- game.SetHost(host);
+ [Resolved]
+ private GameHost host { get; set; }
- Children = new Drawable[]
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("create game", () =>
{
- new Box
+ game = new OsuGame();
+ game.SetHost(host);
+
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- game
- };
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ game
+ };
+ });
AddUntilStep("wait for load", () => game.IsLoaded);
}
diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
index 3c65f46c79..cc64d37116 100644
--- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
@@ -15,6 +15,7 @@ using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Toolbar;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.Play;
@@ -323,6 +324,84 @@ namespace osu.Game.Tests.Visual.Navigation
AddWaitStep("wait two frames", 2);
}
+ [Test]
+ public void TestOverlayClosing()
+ {
+ // use now playing overlay for "overlay -> background" drag case
+ // since most overlays use a scroll container that absorbs on mouse down
+ NowPlayingOverlay nowPlayingOverlay = null;
+
+ AddStep("enter menu", () => InputManager.Key(Key.Enter));
+
+ AddStep("get and press now playing hotkey", () =>
+ {
+ nowPlayingOverlay = Game.ChildrenOfType().Single();
+ InputManager.Key(Key.F6);
+ });
+
+ // drag tests
+
+ // background -> toolbar
+ AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight));
+ AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left));
+ AddStep("move cursor to toolbar", () => InputManager.MoveMouseTo(Game.Toolbar.ScreenSpaceDrawQuad.Centre));
+ AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
+ AddAssert("now playing is hidden", () => nowPlayingOverlay.State.Value == Visibility.Hidden);
+
+ AddStep("press now playing hotkey", () => InputManager.Key(Key.F6));
+
+ // toolbar -> background
+ AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left));
+ AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight));
+ AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
+ AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible);
+
+ // background -> overlay
+ AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left));
+ AddStep("move cursor to now playing overlay", () => InputManager.MoveMouseTo(nowPlayingOverlay.ScreenSpaceDrawQuad.Centre));
+ AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
+ AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible);
+
+ // overlay -> background
+ AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left));
+ AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight));
+ AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
+ AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible);
+
+ // background -> background
+ AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left));
+ AddStep("move cursor to left", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomLeft));
+ AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
+ AddAssert("now playing is hidden", () => nowPlayingOverlay.State.Value == Visibility.Hidden);
+
+ AddStep("press now playing hotkey", () => InputManager.Key(Key.F6));
+
+ // click tests
+
+ // toolbar
+ AddStep("move cursor to toolbar", () => InputManager.MoveMouseTo(Game.Toolbar.ScreenSpaceDrawQuad.Centre));
+ AddStep("click left mouse button", () => InputManager.Click(MouseButton.Left));
+ AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible);
+
+ // background
+ AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight));
+ AddStep("click left mouse button", () => InputManager.Click(MouseButton.Left));
+ AddAssert("now playing is hidden", () => nowPlayingOverlay.State.Value == Visibility.Hidden);
+ }
+
+ [Test]
+ public void TestExitGameFromSongSelect()
+ {
+ PushAndConfirm(() => new TestPlaySongSelect());
+ exitViaEscapeAndConfirm();
+
+ pushEscape(); // returns to osu! logo
+
+ AddStep("Hold escape", () => InputManager.PressKey(Key.Escape));
+ AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles);
+ AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null);
+ }
+
private void pushEscape() =>
AddStep("Press escape", () => InputManager.Key(Key.Escape));
diff --git a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs
index c1c968e862..7e3d8290be 100644
--- a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs
@@ -9,9 +9,12 @@ namespace osu.Game.Tests.Visual.Navigation
{
public class TestSettingsMigration : OsuGameTestScene
{
- public override void RecycleLocalStorage()
+ public override void RecycleLocalStorage(bool isDisposing)
{
- base.RecycleLocalStorage();
+ base.RecycleLocalStorage(isDisposing);
+
+ if (isDisposing)
+ return;
using (var config = new OsuConfigManager(LocalStorage))
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs
index 5bfb676f81..963809ebe1 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs
@@ -5,10 +5,10 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
-using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
@@ -17,10 +17,11 @@ using osu.Game.Overlays.BeatmapListing;
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Users;
+using osuTK.Input;
namespace osu.Game.Tests.Visual.Online
{
- public class TestSceneBeatmapListingOverlay : OsuTestScene
+ public class TestSceneBeatmapListingOverlay : OsuManualInputManagerTestScene
{
private readonly List setsForResponse = new List();
@@ -28,27 +29,33 @@ namespace osu.Game.Tests.Visual.Online
private BeatmapListingSearchControl searchControl => overlay.ChildrenOfType().Single();
- [BackgroundDependencyLoader]
- private void load()
+ [SetUpSteps]
+ public void SetUpSteps()
{
- Child = overlay = new BeatmapListingOverlay { State = { Value = Visibility.Visible } };
-
- ((DummyAPIAccess)API).HandleRequest = req =>
+ AddStep("setup overlay", () =>
{
- if (!(req is SearchBeatmapSetsRequest searchBeatmapSetsRequest)) return false;
-
- searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse
- {
- BeatmapSets = setsForResponse,
- });
-
- return true;
- };
+ Child = overlay = new BeatmapListingOverlay { State = { Value = Visibility.Visible } };
+ setsForResponse.Clear();
+ });
AddStep("initialize dummy", () =>
{
+ var api = (DummyAPIAccess)API;
+
+ api.HandleRequest = req =>
+ {
+ if (!(req is SearchBeatmapSetsRequest searchBeatmapSetsRequest)) return false;
+
+ searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse
+ {
+ BeatmapSets = setsForResponse,
+ });
+
+ return true;
+ };
+
// non-supporter user
- ((DummyAPIAccess)API).LocalUser.Value = new User
+ api.LocalUser.Value = new User
{
Username = "TestBot",
Id = API.LocalUser.Value.Id + 1,
@@ -56,6 +63,51 @@ namespace osu.Game.Tests.Visual.Online
});
}
+ [Test]
+ public void TestHideViaBack()
+ {
+ AddAssert("is visible", () => overlay.State.Value == Visibility.Visible);
+ AddStep("hide", () => InputManager.Key(Key.Escape));
+ AddUntilStep("is hidden", () => overlay.State.Value == Visibility.Hidden);
+ }
+
+ [Test]
+ public void TestHideViaBackWithSearch()
+ {
+ AddAssert("is visible", () => overlay.State.Value == Visibility.Visible);
+
+ AddStep("search something", () => overlay.ChildrenOfType().First().Text = "search");
+
+ AddStep("kill search", () => InputManager.Key(Key.Escape));
+
+ AddAssert("search textbox empty", () => string.IsNullOrEmpty(overlay.ChildrenOfType().First().Text));
+ AddAssert("is visible", () => overlay.State.Value == Visibility.Visible);
+
+ AddStep("hide", () => InputManager.Key(Key.Escape));
+ AddUntilStep("is hidden", () => overlay.State.Value == Visibility.Hidden);
+ }
+
+ [Test]
+ public void TestHideViaBackWithScrolledSearch()
+ {
+ AddAssert("is visible", () => overlay.State.Value == Visibility.Visible);
+
+ AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, 100).ToArray()));
+
+ AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent));
+
+ AddStep("scroll to bottom", () => overlay.ChildrenOfType().First().ScrollToEnd());
+
+ AddStep("kill search", () => InputManager.Key(Key.Escape));
+
+ AddUntilStep("search textbox empty", () => string.IsNullOrEmpty(overlay.ChildrenOfType().First().Text));
+ AddUntilStep("is scrolled to top", () => overlay.ChildrenOfType().First().Current == 0);
+ AddAssert("is visible", () => overlay.State.Value == Visibility.Visible);
+
+ AddStep("hide", () => InputManager.Key(Key.Escape));
+ AddUntilStep("is hidden", () => overlay.State.Value == Visibility.Hidden);
+ }
+
[Test]
public void TestNoBeatmapsPlaceholder()
{
@@ -63,7 +115,7 @@ namespace osu.Game.Tests.Visual.Online
AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true);
AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet));
- AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any());
+ AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent));
AddStep("fetch for 0 beatmaps", () => fetchFor());
AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true);
@@ -193,13 +245,15 @@ namespace osu.Game.Tests.Visual.Online
noPlaceholderShown();
}
+ private static int searchCount;
+
private void fetchFor(params BeatmapSetInfo[] beatmaps)
{
setsForResponse.Clear();
setsForResponse.AddRange(beatmaps.Select(b => new TestAPIBeatmapSet(b)));
// trigger arbitrary change for fetching.
- searchControl.Query.TriggerChange();
+ searchControl.Query.Value = $"search {searchCount++}";
}
private void setRankAchievedFilter(ScoreRank[] ranks)
@@ -229,8 +283,8 @@ namespace osu.Game.Tests.Visual.Online
private void noPlaceholderShown()
{
AddUntilStep("no placeholder shown", () =>
- !overlay.ChildrenOfType().Any()
- && !overlay.ChildrenOfType().Any());
+ !overlay.ChildrenOfType().Any(d => d.IsPresent)
+ && !overlay.ChildrenOfType().Any(d => d.IsPresent));
}
private class TestAPIBeatmapSet : APIBeatmapSet
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index edc1696456..f420ad976b 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -21,6 +21,8 @@ namespace osu.Game.Tests.Visual.Online
protected override bool UseOnlineAPI => true;
+ private int nextBeatmapSetId = 1;
+
public TestSceneBeatmapSetOverlay()
{
Add(overlay = new TestBeatmapSetOverlay());
@@ -240,12 +242,23 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("show explicit map", () =>
{
- var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
+ var beatmapSet = getBeatmapSet();
beatmapSet.OnlineInfo.HasExplicitContent = true;
overlay.ShowBeatmapSet(beatmapSet);
});
}
+ [Test]
+ public void TestFeaturedBeatmap()
+ {
+ AddStep("show featured map", () =>
+ {
+ var beatmapSet = getBeatmapSet();
+ beatmapSet.OnlineInfo.TrackId = 1;
+ overlay.ShowBeatmapSet(beatmapSet);
+ });
+ }
+
[Test]
public void TestHide()
{
@@ -308,6 +321,14 @@ namespace osu.Game.Tests.Visual.Online
};
}
+ private BeatmapSetInfo getBeatmapSet()
+ {
+ var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
+ // Make sure the overlay is reloaded (see `BeatmapSetInfo.Equals`).
+ beatmapSet.OnlineBeatmapSetID = nextBeatmapSetId++;
+ return beatmapSet;
+ }
+
private void downloadAssert(bool shown)
{
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown);
diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
index cd22bb2513..31e5a9b86c 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
@@ -42,19 +42,21 @@ namespace osu.Game.Tests.Visual.Online
() => commentsContainer.ChildrenOfType().Single().IsLoading);
}
- [Test]
- public void TestSingleCommentsPage()
+ [TestCase(false)]
+ [TestCase(true)]
+ public void TestSingleCommentsPage(bool withPinned)
{
- setUpCommentsResponse(exampleComments);
+ setUpCommentsResponse(getExampleComments(withPinned));
AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
AddUntilStep("show more button hidden",
() => commentsContainer.ChildrenOfType().Single().Alpha == 0);
}
- [Test]
- public void TestMultipleCommentPages()
+ [TestCase(false)]
+ [TestCase(true)]
+ public void TestMultipleCommentPages(bool withPinned)
{
- var comments = exampleComments;
+ var comments = getExampleComments(withPinned);
comments.HasMore = true;
comments.TopLevelCount = 10;
@@ -64,11 +66,12 @@ namespace osu.Game.Tests.Visual.Online
() => commentsContainer.ChildrenOfType().Single().Alpha == 1);
}
- [Test]
- public void TestMultipleLoads()
+ [TestCase(false)]
+ [TestCase(true)]
+ public void TestMultipleLoads(bool withPinned)
{
- var comments = exampleComments;
- int topLevelCommentCount = exampleComments.Comments.Count;
+ var comments = getExampleComments(withPinned);
+ int topLevelCommentCount = comments.Comments.Count;
AddStep("hide container", () => commentsContainer.Hide());
setUpCommentsResponse(comments);
@@ -79,6 +82,48 @@ namespace osu.Game.Tests.Visual.Online
() => commentsContainer.ChildrenOfType().Count() == topLevelCommentCount);
}
+ [Test]
+ public void TestNoComment()
+ {
+ var comments = getExampleComments();
+ comments.Comments.Clear();
+
+ setUpCommentsResponse(comments);
+ AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
+ AddAssert("no comment shown", () => !commentsContainer.ChildrenOfType().Any());
+ }
+
+ [TestCase(false)]
+ [TestCase(true)]
+ public void TestSingleComment(bool withPinned)
+ {
+ var comment = new Comment
+ {
+ Id = 1,
+ Message = "This is a single comment",
+ LegacyName = "SingleUser",
+ CreatedAt = DateTimeOffset.Now,
+ VotesCount = 0,
+ Pinned = withPinned,
+ };
+
+ var bundle = new CommentBundle
+ {
+ Comments = new List { comment },
+ IncludedComments = new List(),
+ PinnedComments = new List(),
+ };
+
+ if (withPinned)
+ bundle.PinnedComments.Add(comment);
+
+ setUpCommentsResponse(bundle);
+ AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
+ AddUntilStep("wait comment load", () => commentsContainer.ChildrenOfType().Any());
+ AddAssert("only one comment shown", () =>
+ commentsContainer.ChildrenOfType().Count(d => d.Comment.Pinned == withPinned) == 1);
+ }
+
private void setUpCommentsResponse(CommentBundle commentBundle)
=> AddStep("set up response", () =>
{
@@ -92,38 +137,71 @@ namespace osu.Game.Tests.Visual.Online
};
});
- private CommentBundle exampleComments => new CommentBundle
+ private CommentBundle getExampleComments(bool withPinned = false)
{
- Comments = new List
+ var bundle = new CommentBundle
{
- new Comment
+ Comments = new List
{
- Id = 1,
- Message = "This is a comment",
- LegacyName = "FirstUser",
- CreatedAt = DateTimeOffset.Now,
- VotesCount = 19,
- RepliesCount = 1
+ new Comment
+ {
+ Id = 1,
+ Message = "This is a comment",
+ LegacyName = "FirstUser",
+ CreatedAt = DateTimeOffset.Now,
+ VotesCount = 19,
+ RepliesCount = 1
+ },
+ new Comment
+ {
+ Id = 5,
+ ParentId = 1,
+ Message = "This is a child comment",
+ LegacyName = "SecondUser",
+ CreatedAt = DateTimeOffset.Now,
+ VotesCount = 4,
+ },
+ new Comment
+ {
+ Id = 10,
+ Message = "This is another comment",
+ LegacyName = "ThirdUser",
+ CreatedAt = DateTimeOffset.Now,
+ VotesCount = 0
+ },
},
- new Comment
+ IncludedComments = new List(),
+ PinnedComments = new List(),
+ };
+
+ if (withPinned)
+ {
+ var pinnedComment = new Comment
{
- Id = 5,
- ParentId = 1,
- Message = "This is a child comment",
- LegacyName = "SecondUser",
+ Id = 15,
+ Message = "This is pinned comment",
+ LegacyName = "PinnedUser",
CreatedAt = DateTimeOffset.Now,
- VotesCount = 4,
- },
- new Comment
+ VotesCount = 999,
+ Pinned = true,
+ RepliesCount = 1,
+ };
+
+ bundle.Comments.Add(pinnedComment);
+ bundle.PinnedComments.Add(pinnedComment);
+
+ bundle.Comments.Add(new Comment
{
- Id = 10,
- Message = "This is another comment",
- LegacyName = "ThirdUser",
+ Id = 20,
+ Message = "Reply to pinned comment",
+ LegacyName = "AbandonedUser",
CreatedAt = DateTimeOffset.Now,
- VotesCount = 0
- },
- },
- IncludedComments = new List(),
- };
+ VotesCount = 0,
+ ParentId = 15,
+ });
+ }
+
+ return bundle;
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
index fd5f306e07..722010ace2 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
@@ -99,16 +99,23 @@ namespace osu.Game.Tests.Visual.Online
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
- var normal = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
+ var normal = getBeatmapSet();
normal.OnlineInfo.HasVideo = true;
normal.OnlineInfo.HasStoryboard = true;
var undownloadable = getUndownloadableBeatmapSet();
var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets);
- var explicitMap = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
+ var explicitMap = getBeatmapSet();
explicitMap.OnlineInfo.HasExplicitContent = true;
+ var featuredMap = getBeatmapSet();
+ featuredMap.OnlineInfo.TrackId = 1;
+
+ var explicitFeaturedMap = getBeatmapSet();
+ explicitFeaturedMap.OnlineInfo.HasExplicitContent = true;
+ explicitFeaturedMap.OnlineInfo.TrackId = 2;
+
Child = new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
@@ -125,13 +132,19 @@ namespace osu.Game.Tests.Visual.Online
new GridBeatmapPanel(undownloadable),
new GridBeatmapPanel(manyDifficulties),
new GridBeatmapPanel(explicitMap),
+ new GridBeatmapPanel(featuredMap),
+ new GridBeatmapPanel(explicitFeaturedMap),
new ListBeatmapPanel(normal),
new ListBeatmapPanel(undownloadable),
new ListBeatmapPanel(manyDifficulties),
- new ListBeatmapPanel(explicitMap)
+ new ListBeatmapPanel(explicitMap),
+ new ListBeatmapPanel(featuredMap),
+ new ListBeatmapPanel(explicitFeaturedMap)
},
},
};
+
+ BeatmapSetInfo getBeatmapSet() => CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs
index 7b741accbb..b26850feb2 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs
@@ -43,6 +43,7 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep(description, () =>
{
+ comment.Pinned = description == "Pinned";
comment.Message = text;
container.Add(new DrawableComment(comment));
});
@@ -59,6 +60,7 @@ namespace osu.Game.Tests.Visual.Online
private static object[] comments =
{
new[] { "Plain", "This is plain comment" },
+ new[] { "Pinned", "This is pinned comment" },
new[] { "Link", "Please visit https://osu.ppy.sh" },
new[]
diff --git a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs
index 64e80e9f02..366fa8a4af 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestPlayActivity()
{
- AddStep("Set activity", () => api.Activity.Value = new UserActivity.SoloGame(new BeatmapInfo(), new RulesetInfo()));
+ AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new RulesetInfo()));
AddStep("Run command", () => Add(new NowPlayingCommand()));
diff --git a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs
index 628ae0971b..4f7947b69c 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs
@@ -149,6 +149,7 @@ namespace osu.Game.Tests.Visual.Online
}
},
IncludedComments = new List(),
+ PinnedComments = new List(),
UserVotes = new List
{
5
@@ -178,6 +179,7 @@ namespace osu.Game.Tests.Visual.Online
},
},
IncludedComments = new List(),
+ PinnedComments = new List(),
};
private class TestCommentsContainer : CommentsContainer
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
index aff510dd95..027f17fff4 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
@@ -1,12 +1,15 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Allocation;
-using osu.Game.Overlays;
using NUnit.Framework;
-using osu.Game.Users;
using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays;
using osu.Game.Overlays.Rankings;
+using osu.Game.Rulesets.Catch;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Users;
namespace osu.Game.Tests.Visual.Online
{
@@ -14,25 +17,29 @@ namespace osu.Game.Tests.Visual.Online
{
protected override bool UseOnlineAPI => true;
- [Cached(typeof(RankingsOverlay))]
- private readonly RankingsOverlay rankingsOverlay;
+ private TestRankingsOverlay rankingsOverlay;
private readonly Bindable countryBindable = new Bindable();
private readonly Bindable scope = new Bindable();
- public TestSceneRankingsOverlay()
- {
- Add(rankingsOverlay = new TestRankingsOverlay
- {
- Country = { BindTarget = countryBindable },
- Header = { Current = { BindTarget = scope } },
- });
- }
+ [SetUp]
+ public void SetUp() => Schedule(loadRankingsOverlay);
[Test]
- public void TestShow()
+ public void TestParentRulesetDecoupledAfterInitialShow()
{
- AddStep("Show", rankingsOverlay.Show);
+ AddStep("enable global ruleset", () => Ruleset.Disabled = false);
+ AddStep("set global ruleset to osu!catch", () => Ruleset.Value = new CatchRuleset().RulesetInfo);
+ AddStep("reload rankings overlay", loadRankingsOverlay);
+ AddAssert("rankings ruleset set to osu!catch", () => rankingsOverlay.Header.Ruleset.Value.ShortName == CatchRuleset.SHORT_NAME);
+
+ AddStep("set global ruleset to osu!", () => Ruleset.Value = new OsuRuleset().RulesetInfo);
+ AddAssert("rankings ruleset still osu!catch", () => rankingsOverlay.Header.Ruleset.Value.ShortName == CatchRuleset.SHORT_NAME);
+
+ AddStep("disable global ruleset", () => Ruleset.Disabled = true);
+ AddAssert("rankings ruleset still enabled", () => rankingsOverlay.Header.Ruleset.Disabled == false);
+ AddStep("set rankings ruleset to osu!mania", () => rankingsOverlay.Header.Ruleset.Value = new ManiaRuleset().RulesetInfo);
+ AddAssert("rankings ruleset set to osu!mania", () => rankingsOverlay.Header.Ruleset.Value.ShortName == ManiaRuleset.SHORT_NAME);
}
[Test]
@@ -50,10 +57,14 @@ namespace osu.Game.Tests.Visual.Online
AddStep("Show US", () => rankingsOverlay.ShowCountry(us_country));
}
- [Test]
- public void TestHide()
+ private void loadRankingsOverlay()
{
- AddStep("Hide", rankingsOverlay.Hide);
+ Child = rankingsOverlay = new TestRankingsOverlay
+ {
+ Country = { BindTarget = countryBindable },
+ Header = { Current = { BindTarget = scope } },
+ State = { Value = Visibility.Visible },
+ };
}
private static readonly Country us_country = new Country
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
index c2e9945c99..a048ae2c54 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
@@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
}
- private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.SoloGame(null, rulesetStore.GetRuleset(rulesetId));
+ private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(null, rulesetStore.GetRuleset(rulesetId));
private class TestUserListPanel : UserListPanel
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
index 03d079261d..70271b0b08 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
@@ -104,6 +104,9 @@ namespace osu.Game.Tests.Visual.Online
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg"
}, api.IsLoggedIn));
+ AddStep("Show ppy from username", () => profile.ShowUser(@"peppy"));
+ AddStep("Show flyte from username", () => profile.ShowUser(@"flyte"));
+
AddStep("Hide", profile.Hide);
AddStep("Show without reload", profile.Show);
}
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
index aff0e7ba4b..5c248163d7 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -3,10 +3,11 @@
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
-using osu.Game.Screens.OnlinePlay.Lounge;
+using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Tests.Visual.OnlinePlay;
@@ -16,27 +17,34 @@ namespace osu.Game.Tests.Visual.Playlists
{
public class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene
{
- protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
+ protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
- private LoungeSubScreen loungeScreen;
+ private TestLoungeSubScreen loungeScreen;
public override void SetUpSteps()
{
base.SetUpSteps();
- AddStep("push screen", () => LoadScreen(loungeScreen = new PlaylistsLoungeSubScreen()));
+ AddStep("push screen", () => LoadScreen(loungeScreen = new TestLoungeSubScreen()));
AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen());
}
private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First();
+ [Test]
+ public void TestManyRooms()
+ {
+ AddStep("add rooms", () => RoomManager.AddRooms(500));
+ }
+
[Test]
public void TestScrollByDraggingRooms()
{
AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left));
AddStep("add rooms", () => RoomManager.AddRooms(30));
+ AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
@@ -53,6 +61,7 @@ namespace osu.Game.Tests.Visual.Playlists
public void TestScrollSelectedIntoView()
{
AddStep("add rooms", () => RoomManager.AddRooms(30));
+ AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
@@ -67,21 +76,26 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("add rooms", () => RoomManager.AddRooms(1));
- AddAssert("selected room is not disabled", () => !OnlinePlayDependencies.SelectedRoom.Disabled);
+ AddAssert("selected room is not disabled", () => !loungeScreen.SelectedRoom.Disabled);
AddStep("select room", () => roomsContainer.Rooms[0].TriggerClick());
- AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null);
+ AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null);
AddStep("enter room", () => roomsContainer.Rooms[0].TriggerClick());
AddUntilStep("wait for match load", () => Stack.CurrentScreen is PlaylistsRoomSubScreen);
- AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null);
- AddAssert("selected room is disabled", () => OnlinePlayDependencies.SelectedRoom.Disabled);
+ AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null);
+ AddAssert("selected room is disabled", () => loungeScreen.SelectedRoom.Disabled);
}
private bool checkRoomVisible(DrawableRoom room) =>
loungeScreen.ChildrenOfType().First().ScreenSpaceDrawQuad
.Contains(room.ScreenSpaceDrawQuad.Centre);
+
+ private class TestLoungeSubScreen : PlaylistsLoungeSubScreen
+ {
+ public new Bindable SelectedRoom => base.SelectedRoom;
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index 98882b659c..48222e6b94 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
SelectedRoom.Value = new Room();
- Child = settings = new TestRoomSettings
+ Child = settings = new TestRoomSettings(SelectedRoom.Value)
{
RelativeSizeAxes = Axes.Both,
State = { Value = Visibility.Visible }
@@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("error not displayed", () => !settings.ErrorText.IsPresent);
}
- private class TestRoomSettings : PlaylistsMatchSettingsOverlay
+ private class TestRoomSettings : PlaylistsRoomSettingsOverlay
{
public TriangleButton ApplyButton => ((MatchSettings)Settings).ApplyButton;
@@ -118,6 +118,11 @@ namespace osu.Game.Tests.Visual.Playlists
public OsuDropdown DurationField => ((MatchSettings)Settings).DurationField;
public OsuSpriteText ErrorText => ((MatchSettings)Settings).ErrorText;
+
+ public TestRoomSettings(Room room)
+ : base(room)
+ {
+ }
}
private class TestDependencies : OnlinePlayTestSceneDependencies
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
index 61d49e4018..4bcc887b9f 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
@@ -160,11 +160,14 @@ namespace osu.Game.Tests.Visual.Playlists
Ruleset = { Value = new OsuRuleset().RulesetInfo }
}));
});
+
+ AddUntilStep("wait for load", () => resultsScreen.ChildrenOfType().FirstOrDefault()?.AllPanelsVisible == true);
}
private void waitForDisplay()
{
AddUntilStep("wait for request to complete", () => requestComplete);
+ AddUntilStep("wait for panels to be visible", () => resultsScreen.ChildrenOfType().FirstOrDefault()?.AllPanelsVisible == true);
AddWaitStep("wait for display", 5);
}
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
index 9fc29049ef..9051c71fc6 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
@@ -11,7 +11,6 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
@@ -19,7 +18,6 @@ using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual.OnlinePlay;
-using osu.Game.Users;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Playlists
@@ -36,18 +34,6 @@ namespace osu.Game.Tests.Visual.Playlists
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
-
- ((DummyAPIAccess)API).HandleRequest = req =>
- {
- switch (req)
- {
- case CreateRoomScoreRequest createRoomScoreRequest:
- createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 });
- return true;
- }
-
- return false;
- };
}
[SetUpSteps]
@@ -66,7 +52,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
SelectedRoom.Value.RoomID.Value = 1;
SelectedRoom.Value.Name.Value = "my awesome room";
- SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
+ SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value);
SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
SelectedRoom.Value.Playlist.Add(new PlaylistItem
@@ -86,7 +72,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("set room properties", () =>
{
SelectedRoom.Value.Name.Value = "my awesome room";
- SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
+ SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
@@ -96,7 +82,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("move mouse to create button", () =>
{
- InputManager.MoveMouseTo(this.ChildrenOfType().Single());
+ InputManager.MoveMouseTo(this.ChildrenOfType().Single());
});
AddStep("click", () => InputManager.Click(MouseButton.Left));
@@ -137,7 +123,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("load room", () =>
{
SelectedRoom.Value.Name.Value = "my awesome room";
- SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
+ SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = importedSet.Beatmaps[0] },
@@ -147,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("create room", () =>
{
- InputManager.MoveMouseTo(match.ChildrenOfType().Single());
+ InputManager.MoveMouseTo(match.ChildrenOfType().Single());
InputManager.Click(MouseButton.Left);
});
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
index ba6b6bd529..631455b727 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
@@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Ranking
TestResultsScreen screen = null;
AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen()));
- AddUntilStep("wait for loaded", () => screen.IsLoaded);
+ AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible);
AddStep("click expanded panel", () =>
{
@@ -138,7 +138,7 @@ namespace osu.Game.Tests.Visual.Ranking
TestResultsScreen screen = null;
AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen()));
- AddUntilStep("wait for loaded", () => screen.IsLoaded);
+ AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible);
AddStep("click expanded panel", () =>
{
@@ -177,7 +177,7 @@ namespace osu.Game.Tests.Visual.Ranking
TestResultsScreen screen = null;
AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen()));
- AddUntilStep("wait for loaded", () => screen.IsLoaded);
+ AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible);
ScorePanel expandedPanel = null;
ScorePanel contractedPanel = null;
@@ -223,6 +223,7 @@ namespace osu.Game.Tests.Visual.Ranking
TestResultsScreen screen = null;
AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen()));
+ AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible);
AddAssert("download button is disabled", () => !screen.ChildrenOfType().Last().Enabled.Value);
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
index e65dcb19b1..6f3b3028be 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
@@ -159,6 +159,9 @@ namespace osu.Game.Tests.Visual.Ranking
var firstScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
var secondScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
+ firstScore.User.Username = "A";
+ secondScore.User.Username = "B";
+
createListStep(() => new ScorePanelList());
AddStep("add scores and select first", () =>
@@ -168,6 +171,8 @@ namespace osu.Game.Tests.Visual.Ranking
list.SelectedScore.Value = firstScore;
});
+ AddUntilStep("wait for load", () => list.AllPanelsVisible);
+
assertScoreState(firstScore, true);
assertScoreState(secondScore, false);
@@ -182,6 +187,87 @@ namespace osu.Game.Tests.Visual.Ranking
assertExpandedPanelCentred();
}
+ [Test]
+ public void TestAddScoreImmediately()
+ {
+ var score = new TestScoreInfo(new OsuRuleset().RulesetInfo);
+
+ createListStep(() =>
+ {
+ var newList = new ScorePanelList { SelectedScore = { Value = score } };
+ newList.AddScore(score);
+ return newList;
+ });
+
+ assertScoreState(score, true);
+ assertExpandedPanelCentred();
+ }
+
+ [Test]
+ public void TestKeyboardNavigation()
+ {
+ var lowestScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { MaxCombo = 100 };
+ var middleScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { MaxCombo = 200 };
+ var highestScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { MaxCombo = 300 };
+
+ createListStep(() => new ScorePanelList());
+
+ AddStep("add scores and select middle", () =>
+ {
+ // order of addition purposefully scrambled.
+ list.AddScore(middleScore);
+ list.AddScore(lowestScore);
+ list.AddScore(highestScore);
+ list.SelectedScore.Value = middleScore;
+ });
+
+ assertScoreState(highestScore, false);
+ assertScoreState(middleScore, true);
+ assertScoreState(lowestScore, false);
+
+ AddStep("press left", () => InputManager.Key(Key.Left));
+
+ assertScoreState(highestScore, true);
+ assertScoreState(middleScore, false);
+ assertScoreState(lowestScore, false);
+ assertExpandedPanelCentred();
+
+ AddStep("press left at start of list", () => InputManager.Key(Key.Left));
+
+ assertScoreState(highestScore, true);
+ assertScoreState(middleScore, false);
+ assertScoreState(lowestScore, false);
+ assertExpandedPanelCentred();
+
+ AddStep("press right", () => InputManager.Key(Key.Right));
+
+ assertScoreState(highestScore, false);
+ assertScoreState(middleScore, true);
+ assertScoreState(lowestScore, false);
+ assertExpandedPanelCentred();
+
+ AddStep("press right again", () => InputManager.Key(Key.Right));
+
+ assertScoreState(highestScore, false);
+ assertScoreState(middleScore, false);
+ assertScoreState(lowestScore, true);
+ assertExpandedPanelCentred();
+
+ AddStep("press right at end of list", () => InputManager.Key(Key.Right));
+
+ assertScoreState(highestScore, false);
+ assertScoreState(middleScore, false);
+ assertScoreState(lowestScore, true);
+ assertExpandedPanelCentred();
+
+ AddStep("press left", () => InputManager.Key(Key.Left));
+
+ assertScoreState(highestScore, false);
+ assertScoreState(middleScore, true);
+ assertScoreState(lowestScore, false);
+ assertExpandedPanelCentred();
+ }
+
private void createListStep(Func creationFunc)
{
AddStep("create list", () => Child = list = creationFunc().With(d =>
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
index fa2c9ecdea..57ba051214 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
@@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Settings
[SetUpSteps]
public void SetUpSteps()
{
+ AddUntilStep("wait for load", () => panel.ChildrenOfType().Any());
AddStep("Scroll to top", () => panel.ChildrenOfType().First().ScrollToTop());
AddWaitStep("wait for scroll", 5);
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs
index df59b9284b..d9cce69ee3 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs
@@ -76,5 +76,23 @@ namespace osu.Game.Tests.Visual.Settings
AddStep("restore default", () => sliderBar.Current.SetDefault());
AddUntilStep("restore button hidden", () => restoreDefaultValueButton.Alpha == 0);
}
+
+ [Test]
+ public void TestWarningTextVisibility()
+ {
+ SettingsNumberBox numberBox = null;
+
+ AddStep("create settings item", () => Child = numberBox = new SettingsNumberBox());
+ AddAssert("warning text not created", () => !numberBox.ChildrenOfType().Any());
+
+ AddStep("set warning text", () => numberBox.WarningText = "this is a warning!");
+ AddAssert("warning text created", () => numberBox.ChildrenOfType().Single().Alpha == 1);
+
+ AddStep("unset warning text", () => numberBox.WarningText = default);
+ AddAssert("warning text hidden", () => numberBox.ChildrenOfType().Single().Alpha == 0);
+
+ AddStep("set warning text again", () => numberBox.WarningText = "another warning!");
+ AddAssert("warning text shown again", () => numberBox.ChildrenOfType().Single().Alpha == 1);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
index 115d2fec7d..0af77b3b5a 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
@@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Settings
@@ -11,27 +12,39 @@ namespace osu.Game.Tests.Visual.Settings
[TestFixture]
public class TestSceneSettingsPanel : OsuTestScene
{
- private readonly SettingsPanel settings;
- private readonly DialogOverlay dialogOverlay;
+ private SettingsPanel settings;
+ private DialogOverlay dialogOverlay;
- public TestSceneSettingsPanel()
+ [SetUpSteps]
+ public void SetUpSteps()
{
- settings = new SettingsOverlay
+ AddStep("create settings", () =>
{
- State = { Value = Visibility.Visible }
- };
- Add(dialogOverlay = new DialogOverlay
- {
- Depth = -1
+ settings?.Expire();
+
+ Add(settings = new SettingsOverlay
+ {
+ State = { Value = Visibility.Visible }
+ });
});
}
+ [Test]
+ public void ToggleVisibility()
+ {
+ AddWaitStep("wait some", 5);
+ AddToggleStep("toggle editor visibility", visible => settings.ToggleVisibility());
+ }
+
[BackgroundDependencyLoader]
private void load()
{
- Dependencies.Cache(dialogOverlay);
+ Add(dialogOverlay = new DialogOverlay
+ {
+ Depth = -1
+ });
- Add(settings);
+ Dependencies.Cache(dialogOverlay);
}
}
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
index da474a64ba..997eac709d 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
@@ -1,14 +1,15 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Linq;
using NUnit.Framework;
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Handlers.Tablet;
-using osu.Framework.Platform;
+using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Overlays;
+using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections.Input;
using osuTK;
@@ -17,22 +18,34 @@ namespace osu.Game.Tests.Visual.Settings
[TestFixture]
public class TestSceneTabletSettings : OsuTestScene
{
- [BackgroundDependencyLoader]
- private void load(GameHost host)
- {
- var tabletHandler = new TestTabletHandler();
+ private TestTabletHandler tabletHandler;
+ private TabletSettings settings;
- AddRange(new Drawable[]
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("create settings", () =>
{
- new TabletSettings(tabletHandler)
+ tabletHandler = new TestTabletHandler();
+
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.None,
- Width = SettingsPanel.PANEL_WIDTH,
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- }
+ settings = new TabletSettings(tabletHandler)
+ {
+ RelativeSizeAxes = Axes.None,
+ Width = SettingsPanel.PANEL_WIDTH,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ }
+ };
});
+ AddStep("set square size", () => tabletHandler.SetTabletSize(new Vector2(100, 100)));
+ }
+
+ [Test]
+ public void TestVariousTabletSizes()
+ {
AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100)));
AddStep("Test with square tablet", () => tabletHandler.SetTabletSize(new Vector2(300, 300)));
AddStep("Test with tall tablet", () => tabletHandler.SetTabletSize(new Vector2(100, 300)));
@@ -40,6 +53,71 @@ namespace osu.Game.Tests.Visual.Settings
AddStep("Test no tablet present", () => tabletHandler.SetTabletSize(Vector2.Zero));
}
+ [Test]
+ public void TestWideAspectRatioValidity()
+ {
+ AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100)));
+
+ AddStep("Reset to full area", () => settings.ChildrenOfType().First().TriggerClick());
+ ensureValid();
+
+ AddStep("rotate 10", () => tabletHandler.Rotation.Value = 10);
+ ensureInvalid();
+
+ AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f);
+ ensureInvalid();
+
+ AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f);
+ ensureInvalid();
+
+ AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f);
+ ensureValid();
+ }
+
+ [Test]
+ public void TestRotationValidity()
+ {
+ AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds);
+
+ AddStep("rotate 90", () => tabletHandler.Rotation.Value = 90);
+ ensureValid();
+
+ AddStep("rotate 180", () => tabletHandler.Rotation.Value = 180);
+
+ ensureValid();
+
+ AddStep("rotate 270", () => tabletHandler.Rotation.Value = 270);
+
+ ensureValid();
+
+ AddStep("rotate 360", () => tabletHandler.Rotation.Value = 360);
+
+ ensureValid();
+
+ AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0);
+ ensureValid();
+
+ AddStep("rotate 45", () => tabletHandler.Rotation.Value = 45);
+ ensureInvalid();
+
+ AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0);
+ ensureValid();
+ }
+
+ [Test]
+ public void TestOffsetValidity()
+ {
+ ensureValid();
+ AddStep("move right", () => tabletHandler.AreaOffset.Value = Vector2.Zero);
+ ensureInvalid();
+ AddStep("move back", () => tabletHandler.AreaOffset.Value = tabletHandler.AreaSize.Value / 2);
+ ensureValid();
+ }
+
+ private void ensureValid() => AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds);
+
+ private void ensureInvalid() => AddAssert("area invalid", () => !settings.AreaSelection.IsWithinBounds);
+
public class TestTabletHandler : ITabletHandler
{
public Bindable AreaOffset { get; } = new Bindable();
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
index 40b2f66d74..dcc2111ad3 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
@@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select EZ mod", () =>
{
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
- SelectedMods.Value = new[] { ruleset.GetAllMods().OfType().Single() };
+ SelectedMods.Value = new[] { ruleset.CreateMod() };
});
AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.FirstValue));
@@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select HR mod", () =>
{
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
- SelectedMods.Value = new[] { ruleset.GetAllMods().OfType().Single() };
+ SelectedMods.Value = new[] { ruleset.CreateMod() };
});
AddAssert("circle size bar is red", () => barIsRed(advancedStats.FirstValue));
@@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select unchanged Difficulty Adjust mod", () =>
{
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
- var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single();
+ var difficultyAdjustMod = ruleset.CreateMod();
difficultyAdjustMod.ReadFromDifficulty(advancedStats.Beatmap.BaseDifficulty);
SelectedMods.Value = new[] { difficultyAdjustMod };
});
@@ -142,7 +142,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select changed Difficulty Adjust mod", () =>
{
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
- var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single();
+ var difficultyAdjustMod = ruleset.CreateMod();
var originalDifficulty = advancedStats.Beatmap.BaseDifficulty;
difficultyAdjustMod.ReadFromDifficulty(originalDifficulty);
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
index a416fd4daf..9fa0eab548 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
@@ -2,14 +2,17 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
@@ -18,6 +21,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
using osuTK;
@@ -65,6 +69,12 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("show", () => { infoWedge.Show(); });
+ AddSliderStep("change star difficulty", 0, 11.9, 5.55, v =>
+ {
+ foreach (var hasCurrentValue in infoWedge.Info.ChildrenOfType>())
+ hasCurrentValue.Current.Value = new StarDifficulty(v, 0);
+ });
+
foreach (var rulesetInfo in rulesets.AvailableRulesets)
{
var instance = rulesetInfo.CreateInstance();
@@ -134,6 +144,29 @@ namespace osu.Game.Tests.Visual.SongSelect
selectBeatmap(createLongMetadata());
}
+ [Test]
+ public void TestBPMUpdates()
+ {
+ const float bpm = 120;
+ IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo);
+ beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / bpm });
+
+ OsuModDoubleTime doubleTime = null;
+
+ selectBeatmap(beatmap);
+ checkDisplayedBPM(bpm);
+
+ AddStep("select DT", () => SelectedMods.Value = new[] { doubleTime = new OsuModDoubleTime() });
+ checkDisplayedBPM(bpm * 1.5f);
+
+ AddStep("change DT rate", () => doubleTime.SpeedChange.Value = 2);
+ checkDisplayedBPM(bpm * 2);
+
+ void checkDisplayedBPM(float target) =>
+ AddUntilStep($"displayed bpm is {target}", () => this.ChildrenOfType().Any(
+ label => label.Statistic.Name == "BPM" && label.Statistic.Content == target.ToString(CultureInfo.InvariantCulture)));
+ }
+
private void selectBeatmap([CanBeNull] IBeatmap b)
{
Container containerBefore = null;
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
index 184a2e59da..29815ce9ff 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect
dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory));
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default));
- dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory));
+ dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler));
return dependencies;
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs
index 66ac700c51..f91d3f595b 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs
@@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
AddStep("setup display", () =>
{
- var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList();
+ var randomMods = Ruleset.Value.CreateInstance().CreateAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList();
OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) };
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
index 5e2d5eba5d..53cb628bb3 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
@@ -14,7 +14,6 @@ using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
-using osu.Game.Tests.Visual.Navigation;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.SongSelect
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
index abd1baf0ac..008d91f649 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
+using Humanizer;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -73,7 +74,7 @@ namespace osu.Game.Tests.Visual.UserInterface
};
control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
- control.General.BindCollectionChanged((u, v) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().ToLowerInvariant())) : "")}", true);
+ control.General.BindCollectionChanged((u, v) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().Underscore())) : "")}", true);
control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true);
control.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true);
control.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true);
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
index 3f9e0048dd..2e30ed9827 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private BeatmapManager beatmapManager;
private ScoreManager scoreManager;
- private readonly List scores = new List();
+ private readonly List importedScores = new List();
private BeatmapInfo beatmap;
[Cached]
@@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.UserInterface
dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory));
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default));
- dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory));
+ dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler));
beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0];
@@ -100,11 +100,9 @@ namespace osu.Game.Tests.Visual.UserInterface
User = new User { Username = "TestUser" },
};
- scores.Add(scoreManager.Import(score).Result);
+ importedScores.Add(scoreManager.Import(score).Result);
}
- scores.Sort(Comparer.Create((s1, s2) => s2.TotalScore.CompareTo(s1.TotalScore)));
-
return dependencies;
}
@@ -134,9 +132,14 @@ namespace osu.Game.Tests.Visual.UserInterface
[Test]
public void TestDeleteViaRightClick()
{
+ ScoreInfo scoreBeingDeleted = null;
AddStep("open menu for top score", () =>
{
- InputManager.MoveMouseTo(leaderboard.ChildrenOfType().First());
+ var leaderboardScore = leaderboard.ChildrenOfType().First();
+
+ scoreBeingDeleted = leaderboardScore.Score;
+
+ InputManager.MoveMouseTo(leaderboardScore);
InputManager.Click(MouseButton.Right);
});
@@ -158,14 +161,14 @@ namespace osu.Game.Tests.Visual.UserInterface
InputManager.Click(MouseButton.Left);
});
- AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scores[0].OnlineScoreID));
+ AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scoreBeingDeleted.OnlineScoreID));
}
[Test]
public void TestDeleteViaDatabase()
{
- AddStep("delete top score", () => scoreManager.Delete(scores[0]));
- AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scores[0].OnlineScoreID));
+ AddStep("delete top score", () => scoreManager.Delete(importedScores[0]));
+ AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != importedScores[0].OnlineScoreID));
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs
index 6fafb8f87a..e1ea02ba67 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs
@@ -5,6 +5,7 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Graphics.Cursor;
@@ -55,20 +56,24 @@ namespace osu.Game.Tests.Visual.UserInterface
{
AddStep("create component", () =>
{
- Child = new OsuContextMenuContainer
+ Child = new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
- Child = new Container
+ Child = new OsuContextMenuContainer
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Width = 500,
- AutoSizeAxes = Axes.Y,
- Child = component = new LabelledColourPalette
+ RelativeSizeAxes = Axes.Both,
+ Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- ColourNamePrefix = "My colour #"
+ Width = 500,
+ AutoSizeAxes = Axes.Y,
+ Child = component = new LabelledColourPalette
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ ColourNamePrefix = "My colour #"
+ }
}
}
};
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDropdown.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDropdown.cs
new file mode 100644
index 0000000000..4b74e37ec4
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDropdown.cs
@@ -0,0 +1,34 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics.UserInterfaceV2;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneLabelledDropdown : OsuTestScene
+ {
+ [Test]
+ public void TestLabelledDropdown()
+ => AddStep(@"create dropdown", () => Child = new LabelledDropdown
+ {
+ Label = @"Countdown speed",
+ Items = new[]
+ {
+ @"Half",
+ @"Normal",
+ @"Double"
+ },
+ Description = @"This is a description"
+ });
+
+ [Test]
+ public void TestLabelledEnumDropdown()
+ => AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown
+ {
+ Label = @"Beatmap status",
+ Description = @"This is a description"
+ });
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs
index 8f057c663b..10eab148de 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Width = 200,
Current =
{
- Value = new OsuRuleset().GetAllMods().ToArray(),
+ Value = new OsuRuleset().CreateAllMods().ToArray(),
}
};
});
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs
index e7fa7d9235..513eb2fafc 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs
@@ -1,7 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Linq;
using NUnit.Framework;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.UI;
@@ -17,5 +19,16 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("create mod icon", () => Child = icon = new ModIcon(new OsuModDoubleTime()));
AddStep("change mod", () => icon.Mod = new OsuModEasy());
}
+
+ [Test]
+ public void TestInterfaceModType()
+ {
+ ModIcon icon = null;
+
+ var ruleset = new OsuRuleset();
+
+ AddStep("create mod icon", () => Child = icon = new ModIcon(ruleset.AllMods.First(m => m.Acronym == "DT")));
+ AddStep("change mod", () => icon.Mod = ruleset.AllMods.First(m => m.Acronym == "EZ"));
+ }
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index 32c1d262d5..4f7aec3b67 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -158,8 +158,8 @@ namespace osu.Game.Tests.Visual.UserInterface
var mania = new ManiaRuleset();
testModsWithSameBaseType(
- mania.GetAllMods().Single(m => m.GetType() == typeof(ManiaModFadeIn)),
- mania.GetAllMods().Single(m => m.GetType() == typeof(ManiaModHidden)));
+ mania.CreateMod(),
+ mania.CreateMod());
}
[Test]
@@ -422,7 +422,7 @@ namespace osu.Game.Tests.Visual.UserInterface
};
}
- private class TestModSelectOverlay : LocalPlayerModSelectOverlay
+ private class TestModSelectOverlay : UserModSelectOverlay
{
public new Bindable> SelectedMods => base.SelectedMods;
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
index 84e2ebb6d8..da0fa5d76d 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
@@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddUntilStep("wait for ready", () => modSelect.State.Value == Visibility.Visible && modSelect.ButtonsLoaded);
}
- private class TestModSelectOverlay : LocalPlayerModSelectOverlay
+ private class TestModSelectOverlay : UserModSelectOverlay
{
public new VisibilityContainer ModSettingsContainer => base.ModSettingsContainer;
public new TriangleButton CustomiseButton => base.CustomiseButton;
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuLogo.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuLogo.cs
new file mode 100644
index 0000000000..8b91339479
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuLogo.cs
@@ -0,0 +1,25 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Screens.Menu;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneOsuLogo : OsuTestScene
+ {
+ [Test]
+ public void TestBasic()
+ {
+ AddStep("Add logo", () =>
+ {
+ Child = new OsuLogo
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs
index 5c2e6e457d..2312c57af2 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs
@@ -6,6 +6,7 @@ using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
using osuTK.Graphics;
@@ -61,10 +62,12 @@ namespace osu.Game.Tests.Visual.UserInterface
));
AddStep("scroll up", () => triggerUserScroll(1));
AddStep("scroll down", () => triggerUserScroll(-1));
+ AddStep("scroll up a bit", () => triggerUserScroll(0.1f));
+ AddStep("scroll down a bit", () => triggerUserScroll(-0.1f));
}
[Test]
- public void TestCorrectSectionSelected()
+ public void TestCorrectSelectionAndVisibleTop()
{
const int sections_count = 11;
float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f };
@@ -79,6 +82,12 @@ namespace osu.Game.Tests.Visual.UserInterface
{
AddStep($"scroll to section {scrollIndex + 1}", () => container.ScrollTo(container.Children[scrollIndex]));
AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]);
+ AddUntilStep("section top is visible", () =>
+ {
+ float scrollPosition = container.ChildrenOfType().First().Current;
+ float sectionTop = container.Children[scrollIndex].BoundingBox.Top;
+ return scrollPosition < sectionTop;
+ });
}
for (int i = 1; i < sections_count; i++)
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs
index 052251d5a8..2806e6d347 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs
@@ -3,7 +3,6 @@
using System.Linq;
using NUnit.Framework;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
@@ -50,32 +49,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
StarRatingDisplay starRating = null;
- BindableDouble starRatingNumeric;
-
- AddStep("load display", () =>
- {
- Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Scale = new Vector2(3f),
- };
- });
-
- AddStep("transform over spectrum", () =>
- {
- starRatingNumeric = new BindableDouble();
- starRatingNumeric.BindValueChanged(val => starRating.Current.Value = new StarDifficulty(val.NewValue, 1));
- this.TransformBindableTo(starRatingNumeric, 10, 10000, Easing.OutQuint);
- });
- }
-
- [Test]
- public void TestChangingStarRatingDisplay()
- {
- StarRatingDisplay starRating = null;
-
- AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
+ AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1), animated: true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 161e248d96..696f930467 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -3,7 +3,7 @@
-
+
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs
index b4d9fa4222..47e7ed9b61 100644
--- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs
+++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Tournament.Tests.Components
private void success(APIBeatmap apiBeatmap)
{
beatmap = apiBeatmap.ToBeatmap(rulesets);
- var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods();
+ var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().AllMods;
foreach (var mod in mods)
{
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs
index 2e34c39370..6879a71f1d 100644
--- a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs
@@ -4,8 +4,10 @@
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Tournament.Components;
+using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Screens.Gameplay;
using osu.Game.Tournament.Screens.Gameplay.Components;
@@ -16,16 +18,26 @@ namespace osu.Game.Tournament.Tests.Screens
[Cached]
private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay { Width = 0.5f };
- [BackgroundDependencyLoader]
- private void load()
+ [Test]
+ public void TestStartupState([Values] TourneyState state)
{
- Add(new GameplayScreen());
- Add(chat);
+ AddStep("set state", () => IPCInfo.State.Value = state);
+ createScreen();
+ }
+
+ [Test]
+ public void TestStartupStateNoCurrentMatch([Values] TourneyState state)
+ {
+ AddStep("set null current", () => Ladder.CurrentMatch.Value = null);
+ AddStep("set state", () => IPCInfo.State.Value = state);
+ createScreen();
}
[Test]
public void TestWarmup()
{
+ createScreen();
+
checkScoreVisibility(false);
toggleWarmup();
@@ -35,6 +47,20 @@ namespace osu.Game.Tournament.Tests.Screens
checkScoreVisibility(false);
}
+ private void createScreen()
+ {
+ AddStep("setup screen", () =>
+ {
+ Remove(chat);
+
+ Children = new Drawable[]
+ {
+ new GameplayScreen(),
+ chat,
+ };
+ });
+ }
+
private void checkScoreVisibility(bool visible)
=> AddUntilStep($"scores {(visible ? "shown" : "hidden")}",
() => this.ChildrenOfType().All(score => score.Alpha == (visible ? 1 : 0)));
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs
index 3ca58dcaf4..d07cc4c431 100644
--- a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
-using osu.Framework.Allocation;
+using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Tournament.Screens.TeamWin;
@@ -10,19 +10,22 @@ namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneTeamWinScreen : TournamentTestScene
{
- [BackgroundDependencyLoader]
- private void load()
+ [Test]
+ public void TestBasic()
{
- var match = Ladder.CurrentMatch.Value;
+ AddStep("set up match", () =>
+ {
+ var match = Ladder.CurrentMatch.Value;
- match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
- match.Completed.Value = true;
+ match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
+ match.Completed.Value = true;
+ });
- Add(new TeamWinScreen
+ AddStep("create screen", () => Add(new TeamWinScreen
{
FillMode = FillMode.Fit,
FillAspectRatio = 16 / 9f
- });
+ }));
}
}
}
diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs
index 1fa0ffc8e9..93e1e018a5 100644
--- a/osu.Game.Tournament.Tests/TournamentTestScene.cs
+++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Tournament.Tests
{
public abstract class TournamentTestScene : OsuTestScene
{
+ private TournamentMatch match;
+
[Cached]
protected LadderInfo Ladder { get; private set; } = new LadderInfo();
@@ -33,19 +35,23 @@ namespace osu.Game.Tournament.Tests
{
Ladder.Ruleset.Value ??= rulesetStore.AvailableRulesets.First();
- TournamentMatch match = CreateSampleMatch();
+ match = CreateSampleMatch();
Ladder.Rounds.Add(match.Round.Value);
Ladder.Matches.Add(match);
Ladder.Teams.Add(match.Team1.Value);
Ladder.Teams.Add(match.Team2.Value);
- Ladder.CurrentMatch.Value = match;
-
Ruleset.BindTo(Ladder.Ruleset);
Dependencies.CacheAs(new StableInfo(storage));
}
+ [SetUpSteps]
+ public virtual void SetUpSteps()
+ {
+ AddStep("set current match", () => Ladder.CurrentMatch.Value = match);
+ }
+
public static TournamentMatch CreateSampleMatch() => new TournamentMatch
{
Team1 =
diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
index ba096abd36..2673c9ec9f 100644
--- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
+++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/osu.Game.Tournament/Components/TournamentModIcon.cs b/osu.Game.Tournament/Components/TournamentModIcon.cs
index 43ac92d285..7c4e9c69a2 100644
--- a/osu.Game.Tournament/Components/TournamentModIcon.cs
+++ b/osu.Game.Tournament/Components/TournamentModIcon.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . 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.Graphics;
using osu.Framework.Graphics.Containers;
@@ -49,7 +48,7 @@ namespace osu.Game.Tournament.Components
}
var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0);
- var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == modAcronym);
+ var modIcon = ruleset?.CreateInstance().CreateModFromAcronym(modAcronym);
if (modIcon == null)
return;
diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs
index 87793f7e1b..2f0e4b5e87 100644
--- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs
@@ -20,6 +20,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
}
private void matchChanged(ValueChangedEvent match) =>
- Text.Text = match.NewValue.Round.Value?.Name.Value ?? "Unknown Round";
+ Text.Text = match.NewValue?.Round.Value?.Name.Value ?? "Unknown Round";
}
}
diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
index 540b45eb56..7e7c719152 100644
--- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
@@ -124,9 +124,6 @@ namespace osu.Game.Tournament.Screens.Gameplay
}
});
- State.BindTo(ipc.State);
- State.BindValueChanged(stateChanged, true);
-
ladder.ChromaKeyWidth.BindValueChanged(width => chroma.Width = width.NewValue, true);
warmup.BindValueChanged(w =>
@@ -136,6 +133,14 @@ namespace osu.Game.Tournament.Screens.Gameplay
}, true);
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ State.BindTo(ipc.State);
+ State.BindValueChanged(stateChanged, true);
+ }
+
protected override void CurrentMatchChanged(ValueChangedEvent match)
{
base.CurrentMatchChanged(match);
@@ -159,7 +164,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
{
if (state.NewValue == TourneyState.Ranking)
{
- if (warmup.Value) return;
+ if (warmup.Value || CurrentMatch.Value == null) return;
if (ipc.Score1.Value > ipc.Score2.Value)
CurrentMatch.Value.Team1Score.Value++;
diff --git a/osu.Game.Tournament/Screens/Setup/SetupScreen.cs b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs
index 5d8f0405ca..f6d28c15e0 100644
--- a/osu.Game.Tournament/Screens/Setup/SetupScreen.cs
+++ b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs
@@ -1,14 +1,12 @@
// Copyright (c) ppy Pty Ltd