diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000..0c6b80e97e
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+custom: https://osu.ppy.sh/home/support
diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml b/.idea/.idea.osu/.idea/runConfigurations/CatchRuleset__Tests_.xml
similarity index 73%
rename from .idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml
rename to .idea/.idea.osu/.idea/runConfigurations/CatchRuleset__Tests_.xml
index 2eff16cc91..6463dd6ea5 100644
--- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml
+++ b/.idea/.idea.osu/.idea/runConfigurations/CatchRuleset__Tests_.xml
@@ -1,18 +1,21 @@
-
+
+
-
+
-
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml b/.idea/.idea.osu/.idea/runConfigurations/ManiaRuleset__Tests_.xml
similarity index 73%
rename from .idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml
rename to .idea/.idea.osu/.idea/runConfigurations/ManiaRuleset__Tests_.xml
index cae9754560..0b63b2d966 100644
--- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__mania_.xml
+++ b/.idea/.idea.osu/.idea/runConfigurations/ManiaRuleset__Tests_.xml
@@ -1,18 +1,21 @@
-
+
+
-
+
-
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml b/.idea/.idea.osu/.idea/runConfigurations/OsuRuleset__Tests_.xml
similarity index 72%
rename from .idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml
rename to .idea/.idea.osu/.idea/runConfigurations/OsuRuleset__Tests_.xml
index 49ec93e1b3..750ece648b 100644
--- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__osu__.xml
+++ b/.idea/.idea.osu/.idea/runConfigurations/OsuRuleset__Tests_.xml
@@ -1,18 +1,21 @@
-
+
+
-
+
-
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml b/.idea/.idea.osu/.idea/runConfigurations/TaikoRuleset__Tests_.xml
similarity index 73%
rename from .idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml
rename to .idea/.idea.osu/.idea/runConfigurations/TaikoRuleset__Tests_.xml
index d0964c6f68..7b359a1ca0 100644
--- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__taiko_.xml
+++ b/.idea/.idea.osu/.idea/runConfigurations/TaikoRuleset__Tests_.xml
@@ -1,18 +1,21 @@
-
+
+
-
+
-
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.osu/.idea/runConfigurations/Tournament.xml b/.idea/.idea.osu/.idea/runConfigurations/Tournament.xml
new file mode 100644
index 0000000000..3722f3dc04
--- /dev/null
+++ b/.idea/.idea.osu/.idea/runConfigurations/Tournament.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.osu/.idea/runConfigurations/Tournament__Tests_.xml b/.idea/.idea.osu/.idea/runConfigurations/Tournament__Tests_.xml
new file mode 100644
index 0000000000..e2628a1bb4
--- /dev/null
+++ b/.idea/.idea.osu/.idea/runConfigurations/Tournament__Tests_.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu_.xml b/.idea/.idea.osu/.idea/runConfigurations/osu_.xml
index 2735f4ceb3..7ac6bb745d 100644
--- a/.idea/.idea.osu/.idea/runConfigurations/osu_.xml
+++ b/.idea/.idea.osu/.idea/runConfigurations/osu_.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests.xml b/.idea/.idea.osu/.idea/runConfigurations/osu___Tests_.xml
similarity index 70%
rename from .idea/.idea.osu/.idea/runConfigurations/VisualTests.xml
rename to .idea/.idea.osu/.idea/runConfigurations/osu___Tests_.xml
index 95cb4c0e62..7fcb7c15ea 100644
--- a/.idea/.idea.osu/.idea/runConfigurations/VisualTests.xml
+++ b/.idea/.idea.osu/.idea/runConfigurations/osu___Tests_.xml
@@ -1,17 +1,20 @@
-
+
+
-
-
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index c3306c2db7..57ff3e6b43 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,41 +1,6 @@
{
"version": "0.2.0",
- "configurations": [
- {
- "name": "VisualTests (Debug)",
- "type": "coreclr",
- "request": "launch",
- "program": "dotnet",
- "args": [
- "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tests.dll"
- ],
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build tests (Debug)",
- "linux": {
- "env": {
- "LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
- }
- },
- "console": "internalConsole"
- },
- {
- "name": "VisualTests (Release)",
- "type": "coreclr",
- "request": "launch",
- "program": "dotnet",
- "args": [
- "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.2/osu.Game.Tests.dll"
- ],
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build tests (Release)",
- "linux": {
- "env": {
- "LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
- }
- },
- "console": "internalConsole"
- },
- {
+ "configurations": [{
"name": "osu! (Debug)",
"type": "coreclr",
"request": "launch",
@@ -69,6 +34,111 @@
},
"console": "internalConsole"
},
+ {
+ "name": "osu! (Tests, Debug)",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "dotnet",
+ "args": [
+ "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tests.dll"
+ ],
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "Build tests (Debug)",
+ "linux": {
+ "env": {
+ "LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
+ }
+ },
+ "console": "internalConsole"
+ }, {
+ "name": "osu! (Tests, Release)",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "dotnet",
+ "args": [
+ "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.2/osu.Game.Tests.dll"
+ ],
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "Build tests (Release)",
+ "linux": {
+ "env": {
+ "LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
+ }
+ },
+ "console": "internalConsole"
+ },
+ {
+ "name": "Tournament (Debug)",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "dotnet",
+ "args": [
+ "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2/osu!.dll",
+ "--tournament"
+ ],
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "Build osu! (Debug)",
+ "linux": {
+ "env": {
+ "LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
+ }
+ },
+ "console": "internalConsole"
+ },
+ {
+ "name": "Tournament (Release)",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "dotnet",
+ "args": [
+ "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2/osu!.dll",
+ "--tournament"
+ ],
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "Build osu! (Release)",
+ "linux": {
+ "env": {
+ "LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
+ }
+ },
+ "console": "internalConsole"
+ },
+ {
+ "name": "Tournament (Tests, Debug)",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "dotnet",
+ "args": [
+ "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tournament.Tests.dll",
+ "--tournament"
+ ],
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "Build tournament tests (Debug)",
+ "linux": {
+ "env": {
+ "LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
+ }
+ },
+ "console": "internalConsole"
+ },
+ {
+ "name": "Tournament (Tests, Release)",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "dotnet",
+ "args": [
+ "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tournament.Tests.dll",
+ "--tournament"
+ ],
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "Build tournament tests (Release)",
+ "linux": {
+ "env": {
+ "LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
+ }
+ },
+ "console": "internalConsole"
+ },
{
"name": "Cake: Debug Script",
"type": "coreclr",
@@ -84,4 +154,4 @@
"externalConsole": false
}
]
-}
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index de799a7c03..aba590f466 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -2,8 +2,7 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
- "tasks": [
- {
+ "tasks": [{
"label": "Build osu! (Debug)",
"type": "shell",
"command": "dotnet",
@@ -65,6 +64,36 @@
"group": "build",
"problemMatcher": "$msCompile"
},
+ {
+ "label": "Build tournament tests (Debug)",
+ "type": "shell",
+ "command": "dotnet",
+ "args": [
+ "build",
+ "--no-restore",
+ "osu.Game.Tournament.Tests",
+ "/p:GenerateFullPaths=true",
+ "/m",
+ "/verbosity:m"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ }, {
+ "label": "Build tournament tests (Release)",
+ "type": "shell",
+ "command": "dotnet",
+ "args": [
+ "build",
+ "--no-restore",
+ "osu.Game.Tournament.Tests",
+ "/p:Configuration=Release",
+ "/p:GenerateFullPaths=true",
+ "/m",
+ "/verbosity:m"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
{
"label": "Restore (netcoreapp2.2)",
"type": "shell",
diff --git a/README.md b/README.md
index abddb1faa1..55f2eebec9 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@ This project is still heavily under development, but is in a state where users a
We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below.
+Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh/home/changelog).
+
## Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
@@ -20,17 +22,24 @@ We are accepting bug reports (please report with as much detail as possible). Fe
### Releases
-If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled.
+![](https://puu.sh/DCmvA/f6a74f5fbb.png)
-- Windows (x64) users should download and run `install.exe`.
-- macOS users (10.12 "Sierra" and higher) should download and run `osu.app.zip`.
-- iOS users can join the [TestFlight beta program](https://t.co/xQJmHkfC18).
+If you are not interested in developing the game, you can still consume our [binary releases](https://github.com/ppy/osu/releases).
+
+**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** users are recommended to self-compile until we have official deployment in place.
+- **iOS** users can join the [TestFlight beta program](https://t.co/PasE1zrHhw) (note that due to high demand this is regularly full).
+- **Android** users can self-compile, and expect a public beta soon.
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
### Downloading the source code
-Clone the repository **including submodules**:
+Clone the repository:
```shell
git clone https://github.com/ppy/osu
@@ -45,7 +54,7 @@ git pull
### Building
-Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below.
+Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided [below](#contributing).
> Visual Studio Code users must run the `Restore` task before any build attempt.
@@ -63,7 +72,7 @@ If the build fails, try to restore nuget packages with `dotnet restore`.
On Linux, the environment variable `LD_LIBRARY_PATH` must point to the build directory, located at `osu.Desktop/bin/Debug/$NETCORE_VERSION`.
-`$NETCORE_VERSION` is the version of .NET Core SDK. You can have it with `grep TargetFramework osu.Desktop/osu.Desktop.csproj | sed -r 's/.*>(.*)<\/.*/\1/'`.
+`$NETCORE_VERSION` is the version of the targeted .NET Core SDK. You can check it by running `grep TargetFramework osu.Desktop/osu.Desktop.csproj | sed -r 's/.*>(.*)<\/.*/\1/'`.
For example, you can run osu! with the following command:
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 3f64bcdf19..48c16caf0f 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -35,7 +35,7 @@ platform :ios do
changelog.gsub!('$BUILD_ID', options[:build])
pilot(
- wait_processing_interval: 900,
+ wait_processing_interval: 1800,
changelog: changelog,
ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa'
)
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 00cabbadf7..975b7f9f5a 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -7,7 +7,6 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using osu.Desktop.Overlays;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Game;
using osuTK.Input;
@@ -56,7 +55,7 @@ namespace osu.Desktop
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, v =>
{
Add(v);
- v.State = Visibility.Visible;
+ v.Show();
});
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
@@ -74,13 +73,11 @@ namespace osu.Desktop
{
case Intro _:
case MainMenu _:
- if (versionManager != null)
- versionManager.State = Visibility.Visible;
+ versionManager?.Show();
break;
default:
- if (versionManager != null)
- versionManager.State = Visibility.Hidden;
+ versionManager?.Hide();
break;
}
}
diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index e9c5d06f3c..1f1d2cea5f 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -1,20 +1,18 @@
// 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.Framework.Allocation;
+using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Platform;
using osu.Game;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
-using osu.Game.Utils;
using osuTK;
using osuTK.Graphics;
@@ -25,15 +23,13 @@ namespace osu.Desktop.Overlays
private OsuConfigManager config;
private OsuGameBase game;
private NotificationOverlay notificationOverlay;
- private GameHost host;
[BackgroundDependencyLoader]
- private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host)
+ private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
{
notificationOverlay = notification;
this.config = config;
this.game = game;
- this.host = host;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
@@ -65,7 +61,7 @@ namespace osu.Desktop.Overlays
},
new OsuSpriteText
{
- Colour = DebugUtils.IsDebug ? colours.Red : Color4.White,
+ Colour = DebugUtils.IsDebugBuild ? colours.Red : Color4.White,
Text = game.Version
},
}
@@ -102,27 +98,31 @@ namespace osu.Desktop.Overlays
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
if (!string.IsNullOrEmpty(lastVersion))
- notificationOverlay.Post(new UpdateCompleteNotification(version, host.OpenUrlExternally));
+ notificationOverlay.Post(new UpdateCompleteNotification(version));
}
}
private class UpdateCompleteNotification : SimpleNotification
{
- public UpdateCompleteNotification(string version, Action openUrl = null)
+ private readonly string version;
+
+ public UpdateCompleteNotification(string version)
{
+ this.version = version;
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
- Icon = FontAwesome.Solid.CheckSquare;
- Activated = delegate
- {
- openUrl?.Invoke($"https://osu.ppy.sh/home/changelog/lazer/{version}");
- return true;
- };
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OsuColour colours, ChangelogOverlay changelog)
{
+ Icon = FontAwesome.Solid.CheckSquare;
IconBackgound.Colour = colours.BlueDark;
+
+ Activated = delegate
+ {
+ changelog.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
+ return true;
+ };
}
}
diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs
index 29554df64c..cb488fea52 100644
--- a/osu.Desktop/Program.cs
+++ b/osu.Desktop/Program.cs
@@ -11,6 +11,7 @@ using osu.Framework.Development;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.IPC;
+using osu.Game.Tournament;
namespace osu.Desktop
{
@@ -46,6 +47,10 @@ namespace osu.Desktop
default:
host.Run(new OsuGameDesktop(args));
break;
+
+ case "--tournament":
+ host.Run(new TournamentGame());
+ break;
}
}
diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs
index e2c7a5e892..69064a40cb 100644
--- a/osu.Desktop/Updater/SquirrelUpdateManager.cs
+++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
@@ -25,11 +25,7 @@ namespace osu.Desktop.Updater
private UpdateManager updateManager;
private NotificationOverlay notificationOverlay;
- public void PrepareUpdate()
- {
- // Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here
- UpdateManager.RestartAppWhenExited().Wait();
- }
+ public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited();
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuGameBase game)
@@ -46,7 +42,7 @@ namespace osu.Desktop.Updater
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
{
//should we schedule a retry on completion of this check?
- bool scheduleRetry = true;
+ bool scheduleRecheck = true;
try
{
@@ -86,10 +82,11 @@ namespace osu.Desktop.Updater
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
//try again without deltas.
checkForUpdateAsync(false, notification);
- scheduleRetry = false;
+ scheduleRecheck = false;
}
else
{
+ notification.State = ProgressNotificationState.Cancelled;
Logger.Error(e, @"update failed!");
}
}
@@ -100,11 +97,8 @@ namespace osu.Desktop.Updater
}
finally
{
- if (scheduleRetry)
+ if (scheduleRecheck)
{
- if (notification != null)
- notification.State = ProgressNotificationState.Cancelled;
-
//check again in 30 minutes.
Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30);
}
@@ -134,8 +128,8 @@ namespace osu.Desktop.Updater
Text = @"Update ready to install. Click to restart!",
Activated = () =>
{
- updateManager.PrepareUpdate();
- game.GracefullyExit();
+ updateManager.PrepareUpdateAsync()
+ .ContinueWith(_ => Schedule(() => game.GracefullyExit()));
return true;
}
};
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index aa8848c55f..8c9e1f279f 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -17,6 +17,7 @@
osu.Desktop.Program
+
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
index 9cec0d280d..ab3c040b4e 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
@@ -20,14 +20,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
- protected override IBeatmap CreateBeatmap(Ruleset ruleset)
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
- Ruleset = ruleset.RulesetInfo
+ Ruleset = ruleset
}
};
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
index 035bbe4b4e..0ad72412fc 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
@@ -29,14 +29,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
- protected override IBeatmap CreateBeatmap(Ruleset ruleset)
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
- Ruleset = ruleset.RulesetInfo
+ Ruleset = ruleset
}
};
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs
index 7d7528372a..9ce46ad6ba 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs
@@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
- protected override IBeatmap CreateBeatmap(Ruleset ruleset)
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
- Ruleset = ruleset.RulesetInfo
+ Ruleset = ruleset
}
};
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
new file mode 100644
index 0000000000..33f93cdb4a
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -0,0 +1,105 @@
+// 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.Allocation;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Tests.Visual;
+using System;
+using System.Collections.Generic;
+using osu.Game.Skinning;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osuTK.Graphics;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Graphics.Textures;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ [TestFixture]
+ public class TestSceneCatcher : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(CatcherSprite),
+ };
+
+ private readonly Container container;
+
+ public TestSceneCatcher()
+ {
+ Child = container = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ AddStep("show default catcher implementation", () => { container.Child = new CatcherSprite(); });
+
+ AddStep("show custom catcher implementation", () =>
+ {
+ container.Child = new CatchCustomSkinSourceContainer
+ {
+ Child = new CatcherSprite()
+ };
+ });
+ }
+
+ private class CatcherCustomSkin : Container
+ {
+ public CatcherCustomSkin()
+ {
+ RelativeSizeAxes = Axes.Both;
+
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Blue
+ },
+ new SpriteText
+ {
+ Text = "custom"
+ }
+ };
+ }
+ }
+
+ [Cached(typeof(ISkinSource))]
+ private class CatchCustomSkinSourceContainer : Container, ISkinSource
+ {
+ public event Action SourceChanged
+ {
+ add { }
+ remove { }
+ }
+
+ public Drawable GetDrawableComponent(string componentName)
+ {
+ switch (componentName)
+ {
+ case "Play/Catch/fruit-catcher-idle":
+ return new CatcherCustomSkin();
+ }
+
+ return null;
+ }
+
+ public SampleChannel GetSample(string sampleName) =>
+ throw new NotImplementedException();
+
+ public Texture GetTexture(string componentName) =>
+ throw new NotImplementedException();
+
+ public TValue GetValue(Func query) where TConfiguration : SkinConfiguration =>
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
index 7393f75e5a..a603d96201 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
-using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Tests.Visual;
@@ -17,19 +16,19 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
- [BackgroundDependencyLoader]
- private void load()
+ [Test]
+ public void TestHyperDash()
{
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
}
- protected override IBeatmap CreateBeatmap(Ruleset ruleset)
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo =
{
- Ruleset = ruleset.RulesetInfo,
+ Ruleset = ruleset,
BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f }
}
};
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 3f8b3bf086..265ecb7688 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,8 +2,8 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
index d6a1ed632b..44e1a8e5cc 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
- return new CatchDifficultyAttributes { Mods = mods };
+ return new CatchDifficultyAttributes { Mods = mods, Skills = skills };
// this is the same as osu!, so there's potential to share the implementation... maybe
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
@@ -41,7 +41,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor,
Mods = mods,
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
- MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet))
+ MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)),
+ Skills = skills
};
}
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index a9fd34455a..0952e8981a 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Objects
{
base.CreateNestedHitObjects();
- var tickSamples = Samples.Select(s => new SampleInfo
+ var tickSamples = Samples.Select(s => new HitSampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
@@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public double Distance => Path.Distance;
- public List> NodeSamples { get; set; } = new List>();
+ public List> NodeSamples { get; set; } = new List>();
public double? LegacyLastTickOffset { get; set; }
}
diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
index daa3f61de3..8dd00756f2 100644
--- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
+++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
@@ -43,10 +43,13 @@ namespace osu.Game.Rulesets.Catch.Replays
float positionChange = Math.Abs(lastPosition - h.X);
double timeAvailable = h.StartTime - lastTime;
- //So we can either make it there without a dash or not.
- double speedRequired = positionChange / timeAvailable;
+ // So we can either make it there without a dash or not.
+ // If positionChange is 0, we don't need to move, so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too)
+ // The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour.
+ double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable;
- bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
+ bool dashRequired = speedRequired > movement_speed;
+ bool impossibleJump = speedRequired > movement_speed * 2;
// todo: get correct catcher size, based on difficulty CS.
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
@@ -59,9 +62,8 @@ namespace osu.Game.Rulesets.Catch.Replays
return;
}
- if (h is Banana)
+ if (impossibleJump)
{
- // auto bananas unrealistically warp to catch 100% combo.
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
else if (h.HyperDash)
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index e7c7fd77df..0b06e958e6 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -6,8 +6,6 @@ using System.Linq;
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.Framework.MathUtils;
using osu.Game.Beatmaps;
@@ -141,7 +139,7 @@ namespace osu.Game.Rulesets.Catch.UI
[BackgroundDependencyLoader]
private void load()
{
- Children = new Drawable[]
+ Children = new[]
{
caughtFruit = new Container
{
@@ -212,7 +210,7 @@ namespace osu.Game.Rulesets.Catch.UI
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
}
- private Sprite createCatcherSprite() => new CatcherSprite();
+ private Drawable createCatcherSprite() => new CatcherSprite();
///
/// Add a caught fruit to the catcher's stack.
@@ -379,8 +377,8 @@ namespace osu.Game.Rulesets.Catch.UI
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1);
// Correct overshooting.
- if (hyperDashDirection > 0 && hyperDashTargetPosition < X ||
- hyperDashDirection < 0 && hyperDashTargetPosition > X)
+ if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
+ (hyperDashDirection < 0 && hyperDashTargetPosition > X))
{
X = hyperDashTargetPosition;
SetHyperDashState();
@@ -444,23 +442,6 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.Expire();
}
-
- private class CatcherSprite : Sprite
- {
- public CatcherSprite()
- {
- Size = new Vector2(CATCHER_SIZE);
-
- // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
- OriginPosition = new Vector2(-0.02f, 0.06f) * CATCHER_SIZE;
- }
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- Texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
- }
- }
}
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
new file mode 100644
index 0000000000..c0c1952064
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
@@ -0,0 +1,33 @@
+// 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.Framework.Graphics.Containers;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.UI
+{
+ public class CatcherSprite : CompositeDrawable
+ {
+ public CatcherSprite()
+ {
+ Size = new Vector2(CatcherArea.CATCHER_SIZE);
+
+ // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
+ OriginPosition = new Vector2(-0.02f, 0.06f) * CatcherArea.CATCHER_SIZE;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ InternalChild = new SkinnableSprite(@"Play/Catch/fruit-catcher-idle")
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
index 0d143198dd..b2613a59d5 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
@@ -11,10 +11,10 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
@@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Tests
content = new Container { RelativeSizeAxes = Axes.Both }
}
},
- new SpriteText
+ new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
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 fd17285a38..dbade6ff8d 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,8 +2,8 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 704deba78b..e10602312e 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
///
/// The time to retrieve the sample info list from.
///
- private List sampleInfoListAt(double time)
+ private List sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 1b6ff16388..ea418eedb4 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
break;
}
- bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP || sample.Name == SampleInfo.HIT_FINISH;
+ bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH;
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
@@ -443,7 +443,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
noteCount = 0;
noteCount = Math.Min(TotalColumns - 1, noteCount);
- bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
+ bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == HitSampleInfo.HIT_WHISTLE || s.Name == HitSampleInfo.HIT_FINISH || s.Name == HitSampleInfo.HIT_CLAP);
var rowPattern = new Pattern();
@@ -472,7 +472,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
/// The time to retrieve the sample info list from.
///
- private List sampleInfoListAt(double time)
+ private List sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 9e95be35fa..b3be08e1f7 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
switch (TotalColumns)
{
- case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
+ case 8 when HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
addToPattern(pattern, 0, generateHold);
break;
@@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
};
if (hold.Head.Samples == null)
- hold.Head.Samples = new List();
+ hold.Head.Samples = new List();
- hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL });
+ hold.Head.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
hold.Tail.Samples = HitObject.Samples;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index d13b21183b..decd159ee9 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (!convertType.HasFlag(PatternType.KeepSingle))
{
- if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
+ if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && TotalColumns != 8)
convertType |= PatternType.Mirror;
- else if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
+ else if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP))
convertType |= PatternType.Gathered;
}
}
@@ -263,7 +263,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
/// Whether this hit object can generate a note in the special column.
///
- private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
+ private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
///
/// Generates a random pattern.
@@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
break;
}
- if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
+ if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP))
p2 = 1;
return GetRandomNoteCount(p2, p3, p4, p5);
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
index 59fed1031f..4a9c22d339 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
- return new ManiaDifficultyAttributes { Mods = mods };
+ return new ManiaDifficultyAttributes { Mods = mods, Skills = skills };
return new ManiaDifficultyAttributes
{
@@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
+ Skills = skills
};
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
index 921246751c..399cf22599 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
@@ -17,14 +17,14 @@ namespace osu.Game.Rulesets.Osu.Tests
{
}
- protected override IBeatmap CreateBeatmap(Ruleset ruleset)
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
- Ruleset = ruleset.RulesetInfo
+ Ruleset = ruleset
}
};
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs
index 12a3a8d27e..8e73d6152f 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs
@@ -46,11 +46,11 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("move mouse away", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.TopLeft));
AddStep("click", () => osuInputManager.GameClick());
- AddAssert("not dismissed", () => !resumeFired && resume.State == Visibility.Visible);
+ AddAssert("not dismissed", () => !resumeFired && resume.State.Value == Visibility.Visible);
AddStep("move mouse back", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
AddStep("click", () => osuInputManager.GameClick());
- AddAssert("dismissed", () => resumeFired && resume.State == Visibility.Hidden);
+ AddAssert("dismissed", () => resumeFired && resume.State.Value == Visibility.Hidden);
}
private class ManualOsuInputManager : OsuInputManager
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index 1ba6d107be..c5a27205d6 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -248,9 +248,9 @@ namespace osu.Game.Rulesets.Osu.Tests
private void createCatmull(int repeats = 0)
{
- var repeatSamples = new List>();
+ var repeatSamples = new List>();
for (int i = 0; i < repeats; i++)
- repeatSamples.Add(new List());
+ repeatSamples.Add(new List());
var slider = new Slider
{
@@ -270,11 +270,11 @@ namespace osu.Game.Rulesets.Osu.Tests
addSlider(slider, 3, 1);
}
- private List> createEmptySamples(int repeats)
+ private List> createEmptySamples(int repeats)
{
- var repeatSamples = new List>();
+ var repeatSamples = new List>();
for (int i = 0; i < repeats; i++)
- repeatSamples.Add(new List());
+ repeatSamples.Add(new List());
return repeatSamples;
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
index 193cfe9c94..2eb783233a 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
@@ -20,7 +20,6 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
-using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual;
using osuTK;
@@ -299,7 +298,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep("load player", () =>
{
- Beatmap.Value = new TestWorkingBeatmap(new Beatmap
+ Beatmap.Value = CreateWorkingBeatmap(new Beatmap
{
HitObjects =
{
@@ -323,7 +322,7 @@ namespace osu.Game.Rulesets.Osu.Tests
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 },
Ruleset = new OsuRuleset().RulesetInfo
},
- }, Clock);
+ });
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
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 8c31db9a7d..a99a93c3e9 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,8 +2,8 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
index b2beda18f4..7bb1f42802 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
break;
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance
- || stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
+ || (stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance))
{
stackBaseIndex = n;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index e2a1542574..c197933233 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
- return new OsuDifficultyAttributes { Mods = mods };
+ return new OsuDifficultyAttributes { Mods = mods, Skills = skills };
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
@@ -50,7 +50,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
SpeedStrain = speedRating,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
OverallDifficulty = (80 - hitWindowGreat) / 6,
- MaxCombo = maxCombo
+ MaxCombo = maxCombo,
+ Skills = skills
};
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
index a2da2bbf53..8072dc09c1 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
@@ -2,8 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
@@ -11,7 +14,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
- internal class OsuModGrow : Mod, IApplicableToDrawableHitObjects
+ internal class OsuModGrow : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
{
public override string Name => "Grow";
@@ -25,9 +28,16 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1;
+ private Bindable increaseFirstObjectVisibility = new Bindable();
+
+ public void ReadFromConfig(OsuConfigManager config)
+ {
+ increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
+ }
+
public void ApplyToDrawableHitObjects(IEnumerable drawables)
{
- foreach (var drawable in drawables)
+ foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
{
switch (drawable)
{
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
index ec23570f54..bc5d02258f 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
@@ -37,11 +37,11 @@ namespace osu.Game.Rulesets.Osu.Mods
if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
- if (osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime || osuHit.IsHit)
+ if ((osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime) || osuHit.IsHit)
continue;
requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime);
- requiresHold |= osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered) || osuHit is DrawableSpinner;
+ requiresHold |= (osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered)) || osuHit is DrawableSpinner;
}
if (requiresHit)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 10b37af957..f372cb65ce 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -18,6 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
private readonly ShakeContainer shakeContainer;
+ // Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects.
+ public override bool HandlePositionalInput => true;
+
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
index 8ee065aaea..9981585f9e 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
@@ -4,7 +4,6 @@
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.Game.Skinning;
@@ -25,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
- Child = new SkinnableDrawable("Play/osu/approachcircle", name => new Sprite { Texture = textures.Get(name) });
+ Child = new SkinnableSprite("Play/osu/approachcircle");
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index 25e1aebd18..33b3667c4f 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -2,13 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
-using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
-using osu.Framework.Graphics.Primitives;
using osuTK;
using osuTK.Graphics;
-using osuTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -19,8 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private readonly SliderPath path;
protected Path Path => path;
- private readonly BufferedContainer container;
-
public float PathRadius
{
get => path.PathRadius;
@@ -44,8 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.AccentColour = value;
-
- container.ForceRedraw();
}
}
@@ -61,8 +54,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.BorderColour = value;
-
- container.ForceRedraw();
}
}
@@ -78,23 +69,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.BorderSize = value;
-
- container.ForceRedraw();
}
}
- public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
-
protected SliderBody()
{
- InternalChild = container = new BufferedContainer
- {
- RelativeSizeAxes = Axes.Both,
- CacheDrawnFrameBuffer = true,
- Child = path = new SliderPath { Blending = BlendingMode.None }
- };
-
- container.Attach(RenderbufferInternalFormat.DepthComponent16);
+ InternalChild = path = new SliderPath();
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
@@ -103,11 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
/// Sets the vertices of the path which should be drawn by this .
///
/// The vertices
- protected void SetVertices(IReadOnlyList vertices)
- {
- path.Vertices = vertices;
- container.ForceRedraw();
- }
+ protected void SetVertices(IReadOnlyList vertices) => path.Vertices = vertices;
private class SliderPath : SmoothPath
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index a8aec005d1..a4638c31f2 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects
///
internal float LazyTravelDistance;
- public List> NodeSamples { get; set; } = new List>();
+ public List> NodeSamples { get; set; } = new List>();
private int repeatCount;
@@ -157,12 +157,12 @@ namespace osu.Game.Rulesets.Osu.Objects
foreach (var e in
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
{
- var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL)
+ var firstSample = Samples.Find(s => s.Name == HitSampleInfo.HIT_NORMAL)
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
- var sampleList = new List();
+ var sampleList = new List();
if (firstSample != null)
- sampleList.Add(new SampleInfo
+ sampleList.Add(new HitSampleInfo
{
Bank = firstSample.Bank,
Volume = firstSample.Volume,
@@ -225,7 +225,7 @@ namespace osu.Game.Rulesets.Osu.Objects
}
}
- private List getNodeSamples(int nodeIndex) =>
+ private List getNodeSamples(int nodeIndex) =>
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
public override Judgement CreateJudgement() => new OsuJudgement();
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 1b8fa0de01..b986076593 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -6,7 +6,7 @@ using System.Diagnostics;
using System.Runtime.InteropServices;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.OpenGL.Buffers;
+using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.OpenGL.Vertices;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
@@ -54,8 +54,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
for (int i = 0; i < max_sprites; i++)
{
- parts[i].InvalidationID = 0;
- parts[i].WasUpdated = true;
+ // InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is run on the draw node
+ // This is to prevent garbage data from being sent to the vertex shader, resulting in visual issues on some platforms
+ parts[i].InvalidationID = 1;
}
}
@@ -147,7 +148,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
public Vector2 Position;
public float Time;
public long InvalidationID;
- public bool WasUpdated;
}
private class TrailDrawNode : DrawNode
@@ -162,16 +162,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly TrailPart[] parts = new TrailPart[max_sprites];
private Vector2 size;
- private readonly VertexBuffer vertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw);
+ private readonly VertexBatch vertexBatch = new QuadBatch(max_sprites, 1);
public TrailDrawNode(CursorTrail source)
: base(source)
{
for (int i = 0; i < max_sprites; i++)
- {
parts[i].InvalidationID = 0;
- parts[i].WasUpdated = false;
- }
}
public override void ApplyState()
@@ -192,55 +189,29 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
public override void Draw(Action vertexAction)
{
- shader.GetUniform("g_FadeClock").UpdateValue(ref time);
-
- int updateStart = -1, updateEnd = 0;
-
- for (int i = 0; i < parts.Length; ++i)
- {
- if (parts[i].WasUpdated)
- {
- if (updateStart == -1)
- updateStart = i;
- updateEnd = i + 1;
-
- int start = i * 4;
- int end = start;
-
- Vector2 pos = parts[i].Position;
- float localTime = parts[i].Time;
-
- texture.DrawQuad(
- new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
- DrawColourInfo.Colour,
- null,
- v => vertexBuffer.Vertices[end++] = new TexturedTrailVertex
- {
- Position = v.Position,
- TexturePosition = v.TexturePosition,
- Time = localTime + 1,
- Colour = v.Colour,
- });
-
- parts[i].WasUpdated = false;
- }
- else if (updateStart != -1)
- {
- vertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4);
- updateStart = -1;
- }
- }
-
- // Update all remaining vertices that have been changed.
- if (updateStart != -1)
- vertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4);
-
base.Draw(vertexAction);
shader.Bind();
+ shader.GetUniform("g_FadeClock").UpdateValue(ref time);
- texture.TextureGL.Bind();
- vertexBuffer.Draw();
+ for (int i = 0; i < parts.Length; ++i)
+ {
+ Vector2 pos = parts[i].Position;
+ float localTime = parts[i].Time;
+
+ DrawQuad(
+ texture,
+ new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
+ DrawColourInfo.Colour,
+ null,
+ v => vertexBatch.Add(new TexturedTrailVertex
+ {
+ Position = v.Position,
+ TexturePosition = v.TexturePosition,
+ Time = localTime + 1,
+ Colour = v.Colour,
+ }));
+ }
shader.Unbind();
}
@@ -249,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
base.Dispose(isDisposing);
- vertexBuffer.Dispose();
+ vertexBatch.Dispose();
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
index d72c334ed3..893c7875fa 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.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 System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -73,7 +74,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
- if (--downCount == 0)
+ // Todo: Math.Max() is required as a temporary measure to address https://github.com/ppy/osu-framework/issues/2576
+ downCount = Math.Max(0, downCount - 1);
+
+ if (downCount == 0)
updateExpandedState();
break;
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
index 0d4e7edb7b..9e5df0d6b1 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.UI
private GameplayCursorContainer localCursorContainer;
- public override CursorContainer LocalCursor => State == Visibility.Visible ? localCursorContainer : null;
+ public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null;
protected override string Message => "Click the orange cursor to resume";
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
index 02300a5dde..8c1b0c4c62 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
typeof(InputDrum),
typeof(DrumSampleMapping),
- typeof(SampleInfo),
+ typeof(HitSampleInfo),
typeof(SampleControlPoint)
};
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
index 3634ec7d4a..6f9856df83 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
@@ -18,7 +18,6 @@ using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.UI;
-using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual;
using osuTK;
using osu.Game.Rulesets.Scoring;
@@ -64,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
var controlPointInfo = new ControlPointInfo();
controlPointInfo.TimingPoints.Add(new TimingControlPoint());
- WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
+ WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{
HitObjects = new List { new CentreHit() },
BeatmapInfo = new BeatmapInfo
@@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Ruleset = new TaikoRuleset().RulesetInfo
},
ControlPointInfo = controlPointInfo
- }, Clock);
+ });
Add(playfieldContainer = new Container
{
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs
new file mode 100644
index 0000000000..d0db193738
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs
@@ -0,0 +1,68 @@
+// 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.Beatmaps;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Taiko.Beatmaps;
+using osu.Game.Rulesets.Taiko.Mods;
+using osu.Game.Rulesets.Taiko.Objects;
+using osu.Game.Screens.Play;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Taiko.Tests
+{
+ public class TestSceneTaikoSuddenDeath : PlayerTestScene
+ {
+ public TestSceneTaikoSuddenDeath()
+ : base(new TaikoRuleset())
+ {
+ }
+
+ protected override bool AllowFail => true;
+
+ protected override Player CreatePlayer(Ruleset ruleset)
+ {
+ Mods.Value = Mods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray();
+ return new ScoreAccessiblePlayer();
+ }
+
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) =>
+ new TaikoBeatmap
+ {
+ HitObjects =
+ {
+ new Swell { StartTime = 1500 },
+ new Hit { StartTime = 100000 },
+ },
+ BeatmapInfo =
+ {
+ Ruleset = new TaikoRuleset().RulesetInfo
+ }
+ };
+
+ [Test]
+ public void TestSpinnerDoesNotFail()
+ {
+ bool judged = false;
+ AddStep("Setup judgements", () =>
+ {
+ judged = false;
+ ((ScoreAccessiblePlayer)Player).ScoreProcessor.NewJudgement += b => judged = true;
+ });
+ AddUntilStep("swell judged", () => judged);
+ AddAssert("not failed", () => !Player.HasFailed);
+ }
+
+ private class ScoreAccessiblePlayer : TestPlayer
+ {
+ public ScoreAccessiblePlayer()
+ : base(false, false)
+ {
+ }
+
+ public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
+ }
+ }
+}
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 72ce6c947b..216cc0222f 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,8 +2,8 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
index d7fa661e8a..ad2596931d 100644
--- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
+++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Audio
foreach (var s in samplePoints)
{
var centre = s.GetSampleInfo();
- var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP);
+ var rim = s.GetSampleInfo(HitSampleInfo.HIT_CLAP);
// todo: this is ugly
centre.Namespace = "taiko";
@@ -43,9 +43,9 @@ namespace osu.Game.Rulesets.Taiko.Audio
}
}
- private SkinnableSound addSound(SampleInfo sampleInfo)
+ private SkinnableSound addSound(HitSampleInfo hitSampleInfo)
{
- var drawable = new SkinnableSound(sampleInfo);
+ var drawable = new SkinnableSound(hitSampleInfo);
Sounds.Add(drawable);
return drawable;
}
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index f8672037cd..f0cf8d9c7d 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
var curveData = obj as IHasCurve;
// Old osu! used hit sounding to determine various hit type information
- List samples = obj.Samples;
+ List samples = obj.Samples;
- bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
+ bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
if (distanceData != null)
{
@@ -117,15 +117,15 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
- List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples });
+ List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples });
int i = 0;
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
{
- List currentSamples = allSamples[i];
- bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
- strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);
+ List currentSamples = allSamples[i];
+ bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
+ strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
if (isRim)
{
@@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
}
else
{
- bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
+ bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
if (isRim)
{
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
index 685ad9949b..c8f3e18911 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
- return new TaikoDifficultyAttributes { Mods = mods };
+ return new TaikoDifficultyAttributes { Mods = mods, Skills = skills };
return new TaikoDifficultyAttributes
{
@@ -36,6 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
+ Skills = skills
};
}
diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
index 502dd54e9e..a6f902208c 100644
--- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
+++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
@@ -9,5 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
public override string Description => @"Beats fade out before you hit them!";
public override double ScoreMultiplier => 1.06;
+ public override bool HasImplementation => false;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index 119940536e..bd45b52d7b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
// Normal and clap samples are handled by the drum
- protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP);
+ protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP);
protected override string SampleNamespace => "Taiko";
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 3c1f48b7a4..d087251e7e 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -170,27 +170,98 @@ namespace osu.Game.Tests.Beatmaps.Formats
var controlPoints = beatmap.ControlPointInfo;
Assert.AreEqual(4, controlPoints.TimingPoints.Count);
- var timingPoint = controlPoints.TimingPoints[0];
+ Assert.AreEqual(42, controlPoints.DifficultyPoints.Count);
+ Assert.AreEqual(42, controlPoints.SamplePoints.Count);
+ Assert.AreEqual(42, controlPoints.EffectPoints.Count);
+
+ var timingPoint = controlPoints.TimingPointAt(0);
+ Assert.AreEqual(956, timingPoint.Time);
+ Assert.AreEqual(329.67032967033, timingPoint.BeatLength);
+ Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature);
+
+ timingPoint = controlPoints.TimingPointAt(48428);
Assert.AreEqual(956, timingPoint.Time);
Assert.AreEqual(329.67032967033d, timingPoint.BeatLength);
Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature);
- Assert.AreEqual(5, controlPoints.DifficultyPoints.Count);
- var difficultyPoint = controlPoints.DifficultyPoints[0];
- Assert.AreEqual(116999, difficultyPoint.Time);
- Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier);
+ timingPoint = controlPoints.TimingPointAt(119637);
+ Assert.AreEqual(119637, timingPoint.Time);
+ Assert.AreEqual(659.340659340659, timingPoint.BeatLength);
+ Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature);
- Assert.AreEqual(34, controlPoints.SamplePoints.Count);
- var soundPoint = controlPoints.SamplePoints[0];
+ var difficultyPoint = controlPoints.DifficultyPointAt(0);
+ Assert.AreEqual(0, difficultyPoint.Time);
+ Assert.AreEqual(1.0, difficultyPoint.SpeedMultiplier);
+
+ difficultyPoint = controlPoints.DifficultyPointAt(48428);
+ Assert.AreEqual(48428, difficultyPoint.Time);
+ Assert.AreEqual(1.0, difficultyPoint.SpeedMultiplier);
+
+ difficultyPoint = controlPoints.DifficultyPointAt(116999);
+ Assert.AreEqual(116999, difficultyPoint.Time);
+ Assert.AreEqual(0.75, difficultyPoint.SpeedMultiplier, 0.1);
+
+ var soundPoint = controlPoints.SamplePointAt(0);
Assert.AreEqual(956, soundPoint.Time);
Assert.AreEqual("soft", soundPoint.SampleBank);
Assert.AreEqual(60, soundPoint.SampleVolume);
- Assert.AreEqual(8, controlPoints.EffectPoints.Count);
- var effectPoint = controlPoints.EffectPoints[0];
+ soundPoint = controlPoints.SamplePointAt(53373);
+ Assert.AreEqual(53373, soundPoint.Time);
+ Assert.AreEqual("soft", soundPoint.SampleBank);
+ Assert.AreEqual(60, soundPoint.SampleVolume);
+
+ soundPoint = controlPoints.SamplePointAt(119637);
+ Assert.AreEqual(119637, soundPoint.Time);
+ Assert.AreEqual("soft", soundPoint.SampleBank);
+ Assert.AreEqual(80, soundPoint.SampleVolume);
+
+ var effectPoint = controlPoints.EffectPointAt(0);
+ Assert.AreEqual(0, effectPoint.Time);
+ Assert.IsFalse(effectPoint.KiaiMode);
+ Assert.IsFalse(effectPoint.OmitFirstBarLine);
+
+ effectPoint = controlPoints.EffectPointAt(53703);
Assert.AreEqual(53703, effectPoint.Time);
Assert.IsTrue(effectPoint.KiaiMode);
Assert.IsFalse(effectPoint.OmitFirstBarLine);
+
+ effectPoint = controlPoints.EffectPointAt(119637);
+ Assert.AreEqual(119637, effectPoint.Time);
+ Assert.IsFalse(effectPoint.KiaiMode);
+ Assert.IsFalse(effectPoint.OmitFirstBarLine);
+ }
+ }
+
+ [Test]
+ public void TestDecodeOverlappingTimingPoints()
+ {
+ var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
+
+ using (var resStream = TestResources.OpenResource("overlapping-control-points.osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var controlPoints = decoder.Decode(stream).ControlPointInfo;
+
+ Assert.That(controlPoints.DifficultyPointAt(500).SpeedMultiplier, Is.EqualTo(1.5).Within(0.1));
+ Assert.That(controlPoints.DifficultyPointAt(1500).SpeedMultiplier, Is.EqualTo(1.5).Within(0.1));
+ Assert.That(controlPoints.DifficultyPointAt(2500).SpeedMultiplier, Is.EqualTo(0.75).Within(0.1));
+ Assert.That(controlPoints.DifficultyPointAt(3500).SpeedMultiplier, Is.EqualTo(1.5).Within(0.1));
+
+ Assert.That(controlPoints.EffectPointAt(500).KiaiMode, Is.True);
+ Assert.That(controlPoints.EffectPointAt(1500).KiaiMode, Is.True);
+ Assert.That(controlPoints.EffectPointAt(2500).KiaiMode, Is.False);
+ Assert.That(controlPoints.EffectPointAt(3500).KiaiMode, Is.True);
+
+ Assert.That(controlPoints.SamplePointAt(500).SampleBank, Is.EqualTo("drum"));
+ Assert.That(controlPoints.SamplePointAt(1500).SampleBank, Is.EqualTo("drum"));
+ Assert.That(controlPoints.SamplePointAt(2500).SampleBank, Is.EqualTo("normal"));
+ Assert.That(controlPoints.SamplePointAt(3500).SampleBank, Is.EqualTo("drum"));
+
+ Assert.That(controlPoints.TimingPointAt(500).BeatLength, Is.EqualTo(500).Within(0.1));
+ Assert.That(controlPoints.TimingPointAt(1500).BeatLength, Is.EqualTo(500).Within(0.1));
+ Assert.That(controlPoints.TimingPointAt(2500).BeatLength, Is.EqualTo(250).Within(0.1));
+ Assert.That(controlPoints.TimingPointAt(3500).BeatLength, Is.EqualTo(500).Within(0.1));
}
}
@@ -283,14 +354,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsNotNull(curveData);
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
Assert.AreEqual(956, hitObjects[0].StartTime);
- Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
+ Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
positionData = hitObjects[1] as IHasPosition;
Assert.IsNotNull(positionData);
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
Assert.AreEqual(1285, hitObjects[1].StartTime);
- Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
+ Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
}
}
@@ -313,7 +384,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
}
- SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
+ HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
@@ -331,7 +402,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
}
- SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
+ HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
@@ -351,7 +422,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
}
- SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
+ HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
@@ -367,34 +438,34 @@ namespace osu.Game.Tests.Beatmaps.Formats
var slider1 = (ConvertSlider)hitObjects[0];
Assert.AreEqual(1, slider1.NodeSamples[0].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
Assert.AreEqual(1, slider1.NodeSamples[1].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
Assert.AreEqual(1, slider1.NodeSamples[2].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
var slider2 = (ConvertSlider)hitObjects[1];
Assert.AreEqual(2, slider2.NodeSamples[0].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
- Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
Assert.AreEqual(2, slider2.NodeSamples[1].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
- Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
Assert.AreEqual(2, slider2.NodeSamples[2].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
- Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
var slider3 = (ConvertSlider)hitObjects[2];
Assert.AreEqual(2, slider3.NodeSamples[0].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
- Assert.AreEqual(SampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
Assert.AreEqual(1, slider3.NodeSamples[1].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
Assert.AreEqual(2, slider3.NodeSamples[2].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
- Assert.AreEqual(SampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 39b7735a55..a725c58462 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -101,14 +101,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsNotNull(curveData);
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
- Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
+ Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
positionData = beatmap.HitObjects[1] as IHasPosition;
Assert.IsNotNull(positionData);
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
- Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
+ Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
}
[TestCase(normal)]
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index f020c2a805..ad0ed00989 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -11,7 +11,9 @@ using NUnit.Framework;
using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
+using osu.Framework.Logging;
using osu.Game.Beatmaps;
+using osu.Game.IO;
using osu.Game.Tests.Resources;
using SharpCompress.Archives.Zip;
@@ -21,14 +23,14 @@ namespace osu.Game.Tests.Beatmaps.IO
public class ImportBeatmapTest
{
[Test]
- public void TestImportWhenClosed()
+ public async Task TestImportWhenClosed()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenClosed"))
{
try
{
- LoadOszIntoOsu(loadOsu(host));
+ await LoadOszIntoOsu(loadOsu(host));
}
finally
{
@@ -38,7 +40,7 @@ namespace osu.Game.Tests.Beatmaps.IO
}
[Test]
- public void TestImportThenDelete()
+ public async Task TestImportThenDelete()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDelete"))
@@ -47,7 +49,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{
var osu = loadOsu(host);
- var imported = LoadOszIntoOsu(osu);
+ var imported = await LoadOszIntoOsu(osu);
deleteBeatmapSet(imported, osu);
}
@@ -59,7 +61,7 @@ namespace osu.Game.Tests.Beatmaps.IO
}
[Test]
- public void TestImportThenImport()
+ public async Task TestImportThenImport()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport"))
@@ -68,17 +70,15 @@ namespace osu.Game.Tests.Beatmaps.IO
{
var osu = loadOsu(host);
- var imported = LoadOszIntoOsu(osu);
- var importedSecondTime = LoadOszIntoOsu(osu);
+ var imported = await LoadOszIntoOsu(osu);
+ var importedSecondTime = await LoadOszIntoOsu(osu);
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
- var manager = osu.Dependencies.Get();
-
- Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
- Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
+ checkBeatmapSetCount(osu, 1);
+ checkSingleReferencedFileCount(osu, 18);
}
finally
{
@@ -88,30 +88,41 @@ namespace osu.Game.Tests.Beatmaps.IO
}
[Test]
- public void TestRollbackOnFailure()
+ public async Task TestRollbackOnFailure()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestRollbackOnFailure"))
{
try
{
+ int itemAddRemoveFireCount = 0;
+ int loggedExceptionCount = 0;
+
+ Logger.NewEntry += l =>
+ {
+ if (l.Target == LoggingTarget.Database && l.Exception != null)
+ Interlocked.Increment(ref loggedExceptionCount);
+ };
+
var osu = loadOsu(host);
var manager = osu.Dependencies.Get();
- int fireCount = 0;
-
// ReSharper disable once AccessToModifiedClosure
- manager.ItemAdded += (_, __) => fireCount++;
- manager.ItemRemoved += _ => fireCount++;
+ manager.ItemAdded += _ => Interlocked.Increment(ref itemAddRemoveFireCount);
+ manager.ItemRemoved += _ => Interlocked.Increment(ref itemAddRemoveFireCount);
- var imported = LoadOszIntoOsu(osu);
+ var imported = await LoadOszIntoOsu(osu);
- Assert.AreEqual(0, fireCount -= 1);
+ Assert.AreEqual(0, itemAddRemoveFireCount -= 1);
imported.Hash += "-changed";
manager.Update(imported);
- Assert.AreEqual(0, fireCount -= 2);
+ Assert.AreEqual(0, itemAddRemoveFireCount -= 2);
+
+ checkBeatmapSetCount(osu, 1);
+ checkBeatmapCount(osu, 12);
+ checkSingleReferencedFileCount(osu, 18);
var breakTemp = TestResources.GetTestBeatmapForImport();
@@ -127,19 +138,24 @@ namespace osu.Game.Tests.Beatmaps.IO
zip.SaveTo(outStream, SharpCompress.Common.CompressionType.Deflate);
}
- Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
- Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
- Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count);
-
// this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu.
- manager.Import(breakTemp);
+ try
+ {
+ await manager.Import(breakTemp);
+ }
+ catch
+ {
+ }
// no events should be fired in the case of a rollback.
- Assert.AreEqual(0, fireCount);
+ Assert.AreEqual(0, itemAddRemoveFireCount);
- Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
- Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
- Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count);
+ checkBeatmapSetCount(osu, 1);
+ checkBeatmapCount(osu, 12);
+
+ checkSingleReferencedFileCount(osu, 18);
+
+ Assert.AreEqual(1, loggedExceptionCount);
}
finally
{
@@ -149,7 +165,7 @@ namespace osu.Game.Tests.Beatmaps.IO
}
[Test]
- public void TestImportThenImportDifferentHash()
+ public async Task TestImportThenImportDifferentHash()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImportDifferentHash"))
@@ -159,19 +175,18 @@ namespace osu.Game.Tests.Beatmaps.IO
var osu = loadOsu(host);
var manager = osu.Dependencies.Get();
- var imported = LoadOszIntoOsu(osu);
+ var imported = await LoadOszIntoOsu(osu);
imported.Hash += "-changed";
manager.Update(imported);
- var importedSecondTime = LoadOszIntoOsu(osu);
+ var importedSecondTime = await LoadOszIntoOsu(osu);
Assert.IsTrue(imported.ID != importedSecondTime.ID);
Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID);
// only one beatmap will exist as the online set ID matched, causing purging of the first import.
- Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
- Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
+ checkBeatmapSetCount(osu, 1);
}
finally
{
@@ -181,7 +196,7 @@ namespace osu.Game.Tests.Beatmaps.IO
}
[Test]
- public void TestImportThenDeleteThenImport()
+ public async Task TestImportThenDeleteThenImport()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDeleteThenImport"))
@@ -190,11 +205,11 @@ namespace osu.Game.Tests.Beatmaps.IO
{
var osu = loadOsu(host);
- var imported = LoadOszIntoOsu(osu);
+ var imported = await LoadOszIntoOsu(osu);
deleteBeatmapSet(imported, osu);
- var importedSecondTime = LoadOszIntoOsu(osu);
+ var importedSecondTime = await LoadOszIntoOsu(osu);
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
Assert.IsTrue(imported.ID == importedSecondTime.ID);
@@ -209,7 +224,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[TestCase(true)]
[TestCase(false)]
- public void TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set)
+ public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set)
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"TestImportThenDeleteThenImport-{set}"))
@@ -218,7 +233,7 @@ namespace osu.Game.Tests.Beatmaps.IO
{
var osu = loadOsu(host);
- var imported = LoadOszIntoOsu(osu);
+ var imported = await LoadOszIntoOsu(osu);
if (set)
imported.OnlineBeatmapSetID = 1234;
@@ -229,7 +244,7 @@ namespace osu.Game.Tests.Beatmaps.IO
deleteBeatmapSet(imported, osu);
- var importedSecondTime = LoadOszIntoOsu(osu);
+ var importedSecondTime = await LoadOszIntoOsu(osu);
// check the newly "imported" beatmap has been reimported due to mismatch (even though hashes matched)
Assert.IsTrue(imported.ID != importedSecondTime.ID);
@@ -243,7 +258,7 @@ namespace osu.Game.Tests.Beatmaps.IO
}
[Test]
- public void TestImportWithDuplicateBeatmapIDs()
+ public async Task TestImportWithDuplicateBeatmapIDs()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWithDuplicateBeatmapID"))
@@ -284,7 +299,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get();
- var imported = manager.Import(toImport);
+ var imported = await manager.Import(toImport);
Assert.NotNull(imported);
Assert.AreEqual(null, imported.Beatmaps[0].OnlineBeatmapID);
@@ -330,7 +345,7 @@ namespace osu.Game.Tests.Beatmaps.IO
}
[Test]
- public void TestImportWhenFileOpen()
+ public async Task TestImportWhenFileOpen()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenFileOpen"))
{
@@ -339,7 +354,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var osu = loadOsu(host);
var temp = TestResources.GetTestBeatmapForImport();
using (File.OpenRead(temp))
- osu.Dependencies.Get().Import(temp);
+ await osu.Dependencies.Get().Import(temp);
ensureLoaded(osu);
File.Delete(temp);
Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't");
@@ -351,13 +366,13 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
- public static BeatmapSetInfo LoadOszIntoOsu(OsuGameBase osu, string path = null)
+ public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null)
{
var temp = path ?? TestResources.GetTestBeatmapForImport();
var manager = osu.Dependencies.Get();
- manager.Import(temp);
+ await manager.Import(temp);
var imported = manager.GetAllUsableBeatmapSets();
@@ -373,11 +388,32 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get();
manager.Delete(imported);
- Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 0);
- Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
+ checkBeatmapSetCount(osu, 0);
+ checkBeatmapSetCount(osu, 1, true);
+ checkSingleReferencedFileCount(osu, 0);
+
Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending);
}
+ private void checkBeatmapSetCount(OsuGameBase osu, int expected, bool includeDeletePending = false)
+ {
+ var manager = osu.Dependencies.Get();
+
+ Assert.AreEqual(expected, includeDeletePending
+ ? manager.QueryBeatmapSets(_ => true).ToList().Count
+ : manager.GetAllUsableBeatmapSets().Count);
+ }
+
+ private void checkBeatmapCount(OsuGameBase osu, int expected)
+ {
+ Assert.AreEqual(expected, osu.Dependencies.Get().QueryBeatmaps(_ => true).ToList().Count);
+ }
+
+ private void checkSingleReferencedFileCount(OsuGameBase osu, int expected)
+ {
+ Assert.AreEqual(expected, osu.Dependencies.Get().QueryFiles(f => f.ReferenceCount == 1).Count());
+ }
+
private OsuGameBase loadOsu(GameHost host)
{
var osu = new OsuGameBase();
diff --git a/osu.Game.Tests/Resources/overlapping-control-points.osu b/osu.Game.Tests/Resources/overlapping-control-points.osu
new file mode 100644
index 0000000000..31d38a3d01
--- /dev/null
+++ b/osu.Game.Tests/Resources/overlapping-control-points.osu
@@ -0,0 +1,19 @@
+osu file format v14
+
+[TimingPoints]
+
+// Timing then inherited
+0,500,4,2,0,100,1,0
+0,-66.6666666666667,4,3,0,100,0,1
+
+// Inherited then timing (equivalent to previous)
+1000,-66.6666666666667,4,3,0,100,0,1
+1000,500,4,2,0,100,1,0
+
+// Inherited then timing (different to previous)
+2000,-133.333333333333,4,1,0,100,0,0
+2000,250,4,2,0,100,1,0
+
+// Timing then inherited (different to previous)
+3000,500,4,2,0,100,1,0
+3000,-66.6666666666667,4,3,0,100,0,1
diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
index e39f18c3cd..4babb07213 100644
--- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
+++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
@@ -23,13 +23,13 @@ namespace osu.Game.Tests.Scores.IO
public class ImportScoreTest
{
[Test]
- public void TestBasicImport()
+ public async Task TestBasicImport()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestBasicImport"))
{
try
{
- var osu = loadOsu(host);
+ var osu = await loadOsu(host);
var toImport = new ScoreInfo
{
@@ -43,7 +43,7 @@ namespace osu.Game.Tests.Scores.IO
OnlineScoreID = 12345,
};
- var imported = loadIntoOsu(osu, toImport);
+ var imported = await loadIntoOsu(osu, toImport);
Assert.AreEqual(toImport.Rank, imported.Rank);
Assert.AreEqual(toImport.TotalScore, imported.TotalScore);
@@ -62,20 +62,20 @@ namespace osu.Game.Tests.Scores.IO
}
[Test]
- public void TestImportMods()
+ public async Task TestImportMods()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportMods"))
{
try
{
- var osu = loadOsu(host);
+ var osu = await loadOsu(host);
var toImport = new ScoreInfo
{
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
};
- var imported = loadIntoOsu(osu, toImport);
+ var imported = await loadIntoOsu(osu, toImport);
Assert.IsTrue(imported.Mods.Any(m => m is OsuModHardRock));
Assert.IsTrue(imported.Mods.Any(m => m is OsuModDoubleTime));
@@ -88,13 +88,13 @@ namespace osu.Game.Tests.Scores.IO
}
[Test]
- public void TestImportStatistics()
+ public async Task TestImportStatistics()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportStatistics"))
{
try
{
- var osu = loadOsu(host);
+ var osu = await loadOsu(host);
var toImport = new ScoreInfo
{
@@ -105,7 +105,7 @@ namespace osu.Game.Tests.Scores.IO
}
};
- var imported = loadIntoOsu(osu, toImport);
+ var imported = await loadIntoOsu(osu, toImport);
Assert.AreEqual(toImport.Statistics[HitResult.Perfect], imported.Statistics[HitResult.Perfect]);
Assert.AreEqual(toImport.Statistics[HitResult.Miss], imported.Statistics[HitResult.Miss]);
@@ -117,7 +117,7 @@ namespace osu.Game.Tests.Scores.IO
}
}
- private ScoreInfo loadIntoOsu(OsuGameBase osu, ScoreInfo score)
+ private async Task loadIntoOsu(OsuGameBase osu, ScoreInfo score)
{
var beatmapManager = osu.Dependencies.Get();
@@ -125,20 +125,24 @@ namespace osu.Game.Tests.Scores.IO
score.Ruleset = new OsuRuleset().RulesetInfo;
var scoreManager = osu.Dependencies.Get();
- scoreManager.Import(score);
+ await scoreManager.Import(score);
return scoreManager.GetAllUsableScores().First();
}
- private OsuGameBase loadOsu(GameHost host)
+ private async Task loadOsu(GameHost host)
{
var osu = new OsuGameBase();
+
+#pragma warning disable 4014
Task.Run(() => host.Run(osu));
+#pragma warning restore 4014
+
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
var beatmapFile = TestResources.GetTestBeatmapForImport();
var beatmapManager = osu.Dependencies.Get();
- beatmapManager.Import(beatmapFile);
+ await beatmapManager.Import(beatmapFile);
return osu;
}
diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs
index 10e3dc10c8..8b941e4633 100644
--- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs
+++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenBeatmap.cs
@@ -7,9 +7,9 @@ using System.Linq;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Framework.Input.States;
using osu.Framework.Platform;
@@ -19,6 +19,7 @@ using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Scoring;
@@ -54,7 +55,7 @@ namespace osu.Game.Tests.Visual.Background
private RulesetStore rulesets;
[BackgroundDependencyLoader]
- private void load(GameHost host)
+ private void load(GameHost host, AudioManager audio)
{
factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase();
@@ -68,10 +69,10 @@ namespace osu.Game.Tests.Visual.Background
usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory));
- Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, Beatmap.Default));
+ Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(new OsuConfigManager(LocalStorage));
- manager.Import(TestResources.GetTestBeatmapForImport());
+ manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
Beatmap.SetDefault();
}
@@ -240,7 +241,7 @@ namespace osu.Game.Tests.Visual.Background
{
player.StoryboardEnabled.Value = false;
player.ReplacesBackground.Value = false;
- player.CurrentStoryboardContainer.Add(new SpriteText
+ player.CurrentStoryboardContainer.Add(new OsuSpriteText
{
Size = new Vector2(250, 50),
Alpha = 1,
diff --git a/osu.Game.Tests/Visual/Components/TestSceneIdleTracker.cs b/osu.Game.Tests/Visual/Components/TestSceneIdleTracker.cs
index e97983dd8b..55aaeed8bf 100644
--- a/osu.Game.Tests/Visual/Components/TestSceneIdleTracker.cs
+++ b/osu.Game.Tests/Visual/Components/TestSceneIdleTracker.cs
@@ -14,94 +14,132 @@ namespace osu.Game.Tests.Visual.Components
[TestFixture]
public class TestSceneIdleTracker : ManualInputManagerTestScene
{
- private readonly IdleTrackingBox box1;
- private readonly IdleTrackingBox box2;
- private readonly IdleTrackingBox box3;
- private readonly IdleTrackingBox box4;
+ private IdleTrackingBox box1;
+ private IdleTrackingBox box2;
+ private IdleTrackingBox box3;
+ private IdleTrackingBox box4;
- public TestSceneIdleTracker()
+ private IdleTrackingBox[] boxes;
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
- Children = new Drawable[]
+ InputManager.MoveMouseTo(Vector2.Zero);
+
+ Children = boxes = new[]
{
- box1 = new IdleTrackingBox(1000)
+ box1 = new IdleTrackingBox(2000)
{
+ Name = "TopLeft",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Red,
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
},
- box2 = new IdleTrackingBox(2000)
+ box2 = new IdleTrackingBox(4000)
{
+ Name = "TopRight",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Green,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
- box3 = new IdleTrackingBox(3000)
+ box3 = new IdleTrackingBox(6000)
{
+ Name = "BottomLeft",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Blue,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
},
- box4 = new IdleTrackingBox(4000)
+ box4 = new IdleTrackingBox(8000)
{
+ Name = "BottomRight",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Orange,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
},
};
- }
+ });
[Test]
public void TestNudge()
{
- AddStep("move mouse to top left", () => InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.Centre));
+ AddStep("move to top left", () => InputManager.MoveMouseTo(box1));
- AddUntilStep("Wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
+ waitForAllIdle();
AddStep("nudge mouse", () => InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.Centre + new Vector2(1)));
- AddAssert("check not idle", () => !box1.IsIdle);
- AddAssert("check idle", () => box2.IsIdle);
- AddAssert("check idle", () => box3.IsIdle);
- AddAssert("check idle", () => box4.IsIdle);
+ checkIdleStatus(1, false);
+ checkIdleStatus(2, true);
+ checkIdleStatus(3, true);
+ checkIdleStatus(4, true);
}
[Test]
public void TestMovement()
{
- AddStep("move mouse", () => InputManager.MoveMouseTo(box2.ScreenSpaceDrawQuad.Centre));
+ AddStep("move to top right", () => InputManager.MoveMouseTo(box2));
- AddAssert("check not idle", () => box1.IsIdle);
- AddAssert("check not idle", () => !box2.IsIdle);
- AddAssert("check idle", () => box3.IsIdle);
- AddAssert("check idle", () => box4.IsIdle);
+ checkIdleStatus(1, true);
+ checkIdleStatus(2, false);
+ checkIdleStatus(3, true);
+ checkIdleStatus(4, true);
- AddStep("move mouse", () => InputManager.MoveMouseTo(box3.ScreenSpaceDrawQuad.Centre));
- AddStep("move mouse", () => InputManager.MoveMouseTo(box4.ScreenSpaceDrawQuad.Centre));
+ AddStep("move to bottom left", () => InputManager.MoveMouseTo(box3));
+ AddStep("move to bottom right", () => InputManager.MoveMouseTo(box4));
- AddAssert("check not idle", () => box1.IsIdle);
- AddAssert("check not idle", () => !box2.IsIdle);
- AddAssert("check idle", () => !box3.IsIdle);
- AddAssert("check idle", () => !box4.IsIdle);
+ checkIdleStatus(1, true);
+ checkIdleStatus(2, false);
+ checkIdleStatus(3, false);
+ checkIdleStatus(4, false);
- AddUntilStep("Wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
+ waitForAllIdle();
}
[Test]
public void TestTimings()
{
- AddStep("move mouse", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
+ AddStep("move to centre", () => InputManager.MoveMouseTo(Content));
+
+ checkIdleStatus(1, false);
+ checkIdleStatus(2, false);
+ checkIdleStatus(3, false);
+ checkIdleStatus(4, false);
- AddAssert("check not idle", () => !box1.IsIdle && !box2.IsIdle && !box3.IsIdle && !box4.IsIdle);
AddUntilStep("Wait for idle", () => box1.IsIdle);
- AddAssert("check not idle", () => !box2.IsIdle && !box3.IsIdle && !box4.IsIdle);
+
+ checkIdleStatus(1, true);
+ checkIdleStatus(2, false);
+ checkIdleStatus(3, false);
+ checkIdleStatus(4, false);
+
AddUntilStep("Wait for idle", () => box2.IsIdle);
- AddAssert("check not idle", () => !box3.IsIdle && !box4.IsIdle);
+
+ checkIdleStatus(1, true);
+ checkIdleStatus(2, true);
+ checkIdleStatus(3, false);
+ checkIdleStatus(4, false);
+
AddUntilStep("Wait for idle", () => box3.IsIdle);
+ checkIdleStatus(1, true);
+ checkIdleStatus(2, true);
+ checkIdleStatus(3, true);
+ checkIdleStatus(4, false);
+
+ waitForAllIdle();
+ }
+
+ private void checkIdleStatus(int box, bool expectedIdle)
+ {
+ AddAssert($"box {box} is {(expectedIdle ? "idle" : "active")}", () => boxes[box - 1].IsIdle == expectedIdle);
+ }
+
+ private void waitForAllIdle()
+ {
AddUntilStep("Wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
}
diff --git a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs
index 94412455a0..df6740421b 100644
--- a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs
+++ b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs
@@ -111,16 +111,19 @@ namespace osu.Game.Tests.Visual.Components
private class TestPreviewTrackManager : PreviewTrackManager
{
- protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TestPreviewTrack(beatmapSetInfo, trackManager);
+ protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore);
protected class TestPreviewTrack : TrackManagerPreviewTrack
{
- public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager)
+ private readonly ITrackStore trackManager;
+
+ public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager)
: base(beatmapSetInfo, trackManager)
{
+ this.trackManager = trackManager;
}
- protected override Track GetTrack() => new TrackVirtual { Length = 100000 };
+ protected override Track GetTrack() => trackManager.GetVirtual(100000);
}
}
}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorCompose.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorCompose.cs
index b537cb0beb..608df1965e 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneEditorCompose.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorCompose.cs
@@ -7,7 +7,6 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Compose;
-using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Editor
{
@@ -19,7 +18,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
- Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, Clock);
+ Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Child = new ComposeScreen();
}
}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs
index 154c58dd99..a8c2362910 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -30,9 +31,9 @@ namespace osu.Game.Tests.Visual.Editor
};
[BackgroundDependencyLoader]
- private void load()
+ private void load(AudioManager audio)
{
- Beatmap.Value = new WaveformTestBeatmap();
+ Beatmap.Value = new WaveformTestBeatmap(audio);
Children = new Drawable[]
{
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs
index 590fa59107..b997d6aaeb 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorSeekSnapping.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// 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;
@@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Tests.Beatmaps;
using osuTK;
using osuTK.Graphics;
@@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editor
}
};
- Beatmap.Value = new TestWorkingBeatmap(testBeatmap, Clock);
+ Beatmap.Value = CreateWorkingBeatmap(testBeatmap);
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs
index f20c921ff2..2e04eb50ca 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorSummaryTimeline.cs
@@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
-using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@@ -21,7 +20,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
- Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
+ Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Add(new SummaryTimeline
{
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs
index 47aa059b62..7accbe2fa8 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs
@@ -18,7 +18,6 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Compose.Components;
-using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
- Beatmap.Value = new TestWorkingBeatmap(new Beatmap
+ Beatmap.Value = CreateWorkingBeatmap(new Beatmap
{
HitObjects = new List
{
diff --git a/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs b/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs
index 126ab98291..0d4fe4366d 100644
--- a/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs
+++ b/osu.Game.Tests/Visual/Editor/TestScenePlaybackControl.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// 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;
@@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Screens.Edit.Components;
-using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Editor
Size = new Vector2(200, 100)
};
- Beatmap.Value = new TestWorkingBeatmap(new Beatmap(), Clock);
+ Beatmap.Value = CreateWorkingBeatmap(new Beatmap());
Child = playback;
}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs b/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs
index e93789b1d3..e2762f3d5f 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneWaveform.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
@@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets.Osu;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
@@ -20,9 +22,9 @@ namespace osu.Game.Tests.Visual.Editor
private WorkingBeatmap waveformBeatmap;
[BackgroundDependencyLoader]
- private void load()
+ private void load(AudioManager audio)
{
- waveformBeatmap = new WaveformTestBeatmap();
+ waveformBeatmap = new WaveformTestBeatmap(audio);
}
[TestCase(1f)]
@@ -91,7 +93,7 @@ namespace osu.Game.Tests.Visual.Editor
Child = graph = new TestWaveformGraph
{
RelativeSizeAxes = Axes.Both,
- Waveform = new DummyWorkingBeatmap().Waveform,
+ Waveform = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).Waveform,
},
};
});
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
new file mode 100644
index 0000000000..f4e8a68819
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
@@ -0,0 +1,50 @@
+// 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.Collections.Generic;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Screens.Play;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ public class TestSceneFailAnimation : AllPlayersTestScene
+ {
+ protected override Player CreatePlayer(Ruleset ruleset)
+ {
+ Mods.Value = Array.Empty();
+ return new FailPlayer();
+ }
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(AllPlayersTestScene),
+ typeof(TestPlayer),
+ typeof(Player),
+ };
+
+ protected override void AddCheckSteps()
+ {
+ AddUntilStep("wait for fail", () => Player.HasFailed);
+ AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State.Value == Visibility.Visible);
+ }
+
+ private class FailPlayer : TestPlayer
+ {
+ public new FailOverlay FailOverlay => base.FailOverlay;
+
+ public FailPlayer()
+ : base(false, false)
+ {
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ ScoreProcessor.FailConditions += (_, __) => true;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
index ba9c583b08..4727140d99 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
@@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Press select", () => press(GlobalAction.Select));
- AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible);
+ AddAssert("Overlay still open", () => pauseOverlay.State.Value == Visibility.Visible);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
@@ -237,7 +237,7 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddAssert("Action was triggered", () => triggered);
- AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
+ AddAssert("Overlay is closed", () => pauseOverlay.State.Value == Visibility.Hidden);
}
///
@@ -272,7 +272,7 @@ namespace osu.Game.Tests.Visual.Gameplay
return triggered;
});
- AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
+ AddAssert("Overlay is closed", () => pauseOverlay.State.Value == Visibility.Hidden);
}
private void press(Key key)
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
new file mode 100644
index 0000000000..237fee1594
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
@@ -0,0 +1,109 @@
+// 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 NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Track;
+using osu.Framework.MathUtils;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Play;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ public class TestSceneGameplayRewinding : PlayerTestScene
+ {
+ private RulesetExposingPlayer player => (RulesetExposingPlayer)Player;
+
+ [Resolved]
+ private AudioManager audioManager { get; set; }
+
+ public TestSceneGameplayRewinding()
+ : base(new OsuRuleset())
+ {
+ }
+
+ private Track track;
+
+ protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
+ {
+ var working = new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
+ track = working.Track;
+ return working;
+ }
+
+ [Test]
+ public void TestNoJudgementsOnRewind()
+ {
+ AddUntilStep("wait for track to start running", () => track.IsRunning);
+ addSeekStep(3000);
+ AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
+ AddStep("clear results", () => player.AppliedResults.Clear());
+ addSeekStep(0);
+ AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
+ AddAssert("no results triggered", () => player.AppliedResults.Count == 0);
+ }
+
+ private void addSeekStep(double time)
+ {
+ AddStep($"seek to {time}", () => track.Seek(time));
+
+ // Allow a few frames of lenience
+ AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
+ }
+
+ protected override Player CreatePlayer(Ruleset ruleset)
+ {
+ Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
+ return new RulesetExposingPlayer();
+ }
+
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
+ {
+ var beatmap = new Beatmap
+ {
+ BeatmapInfo = { BaseDifficulty = { ApproachRate = 9 } },
+ };
+
+ for (int i = 0; i < 15; i++)
+ {
+ beatmap.HitObjects.Add(new HitCircle
+ {
+ Position = new Vector2(256, 192),
+ StartTime = 1000 + 30 * i
+ });
+ }
+
+ return beatmap;
+ }
+
+ private class RulesetExposingPlayer : Player
+ {
+ public readonly List AppliedResults = new List();
+
+ public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
+
+ public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
+
+ public RulesetExposingPlayer()
+ : base(false, false)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ ScoreProcessor.NewJudgement += r => AppliedResults.Add(r);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
index b6f8638f4a..5808a78056 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
@@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestPauseAfterFail()
{
AddUntilStep("wait for fail", () => Player.HasFailed);
- AddAssert("fail overlay shown", () => Player.FailOverlayVisible);
+ AddUntilStep("fail overlay shown", () => Player.FailOverlayVisible);
confirmClockRunning(false);
@@ -137,6 +137,22 @@ namespace osu.Game.Tests.Visual.Gameplay
exitAndConfirm();
}
+ [Test]
+ public void TestExitViaHoldToExit()
+ {
+ AddStep("exit", () =>
+ {
+ InputManager.MoveMouseTo(Player.HUDOverlay.HoldToQuit.First(c => c is HoldToConfirmContainer));
+ InputManager.PressButton(MouseButton.Left);
+ });
+
+ confirmPaused();
+
+ AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
+
+ exitAndConfirm();
+ }
+
[Test]
public void TestExitFromPause()
{
@@ -189,7 +205,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("pause overlay " + (isShown ? "shown" : "hidden"), () => Player.PauseOverlayVisible == isShown);
private void confirmClockRunning(bool isRunning) =>
- AddAssert("clock " + (isRunning ? "running" : "stopped"), () => Player.GameplayClockContainer.GameplayClock.IsRunning == isRunning);
+ AddUntilStep("clock " + (isRunning ? "running" : "stopped"), () => Player.GameplayClockContainer.GameplayClock.IsRunning == isRunning);
protected override bool AllowFail => true;
@@ -203,9 +219,9 @@ namespace osu.Game.Tests.Visual.Gameplay
public new HUDOverlay HUDOverlay => base.HUDOverlay;
- public bool FailOverlayVisible => FailOverlay.State == Visibility.Visible;
+ public bool FailOverlayVisible => FailOverlay.State.Value == Visibility.Visible;
- public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible;
+ public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible;
public override void OnEntering(IScreen last)
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index 5c26f733ab..daee3a520c 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
@@ -16,7 +16,6 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
-using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Gameplay
{
@@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void Setup() => Schedule(() =>
{
InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
- Beatmap.Value = new TestWorkingBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), Clock);
+ Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
});
[Test]
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs
index d941ad54c0..65b56319e8 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs
@@ -3,7 +3,6 @@
using System;
using osu.Framework.Lists;
-using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Screens.Play;
@@ -24,7 +23,9 @@ namespace osu.Game.Tests.Visual.Gameplay
GC.WaitForPendingFinalizers();
int count = 0;
- workingWeakReferences.ForEachAlive(_ => count++);
+ foreach (var unused in workingWeakReferences)
+ count++;
+
return count == 1;
});
@@ -34,14 +35,16 @@ namespace osu.Game.Tests.Visual.Gameplay
GC.WaitForPendingFinalizers();
int count = 0;
- playerWeakReferences.ForEachAlive(_ => count++);
+ foreach (var unused in playerWeakReferences)
+ count++;
+
return count == 1;
});
}
- protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock clock)
+ protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
{
- var working = base.CreateWorkingBeatmap(beatmap, clock);
+ var working = base.CreateWorkingBeatmap(beatmap);
workingWeakReferences.Add(working);
return working;
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
new file mode 100644
index 0000000000..0dfcda122f
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
@@ -0,0 +1,76 @@
+// 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.Online;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Scoring;
+using osu.Game.Screens.Play;
+using osu.Game.Users;
+using osuTK;
+using System;
+using System.Collections.Generic;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ [TestFixture]
+ public class TestSceneReplayDownloadButton : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ReplayDownloadButton)
+ };
+
+ private TestReplayDownloadButton downloadButton;
+
+ public TestSceneReplayDownloadButton()
+ {
+ createButton(true);
+ AddStep(@"downloading state", () => downloadButton.SetDownloadState(DownloadState.Downloading));
+ AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
+ AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
+ createButton(false);
+ }
+
+ private void createButton(bool withReplay)
+ {
+ AddStep(withReplay ? @"create button with replay" : "create button without replay", () =>
+ {
+ Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(withReplay))
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(80, 40),
+ };
+ });
+ }
+
+ private ScoreInfo getScoreInfo(bool replayAvailable)
+ {
+ return new APILegacyScoreInfo
+ {
+ ID = 1,
+ OnlineScoreID = 2553163309,
+ Ruleset = new OsuRuleset().RulesetInfo,
+ Replay = replayAvailable,
+ User = new User
+ {
+ Id = 39828,
+ Username = @"WubWoofWolf",
+ }
+ };
+ }
+
+ private class TestReplayDownloadButton : ReplayDownloadButton
+ {
+ public void SetDownloadState(DownloadState state) => State.Value = state;
+
+ public TestReplayDownloadButton(ScoreInfo score)
+ : base(score)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs
index 0c9e3fcd73..080a287b48 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs
@@ -5,6 +5,7 @@ using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
+using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play.HUD;
using osuTK;
@@ -53,7 +54,7 @@ namespace osu.Game.Tests.Visual.Gameplay
};
Add(stars);
- SpriteText starsLabel = new SpriteText
+ SpriteText starsLabel = new OsuSpriteText
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs
index 7d6edd0d12..c7a0df6e9f 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinReloadable.cs
@@ -7,9 +7,9 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
using osu.Game.Skinning;
using osuTK.Graphics;
@@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
},
- new SpriteText
+ new OsuSpriteText
{
Font = OsuFont.Default.With(size: 40),
Anchor = Anchor.Centre,
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
index 213cdf5e48..ead7a4b7fc 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
- State = Visibility.Visible,
+ State = { Value = Visibility.Visible },
});
AddStep("Restart", restart);
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs
index 813d4df708..000832b784 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs
@@ -53,38 +53,12 @@ namespace osu.Game.Tests.Visual.Menus
}
[Test]
- public void TestShortLoad()
+ public void TestDelayedLoad()
{
- bool logoVisible = false;
-
AddStep("begin loading", () => LoadScreen(loader = new TestLoader()));
- AddWaitStep("wait", 3);
- AddStep("finish loading", () =>
- {
- logoVisible = loader.Logo?.Alpha > 0;
- loader.AllowLoad.Set();
- });
-
+ AddUntilStep("wait for logo visible", () => loader.Logo?.Alpha > 0);
+ AddStep("finish loading", () => loader.AllowLoad.Set());
AddAssert("loaded", () => loader.Logo != null && loader.ScreenLoaded);
- AddAssert("logo was visible", () => logoVisible);
- AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0);
- }
-
- [Test]
- public void TestLongLoad()
- {
- bool logoVisible = false;
-
- AddStep("begin loading", () => LoadScreen(loader = new TestLoader()));
- AddWaitStep("wait", 10);
- AddStep("finish loading", () =>
- {
- logoVisible = loader.Logo?.Alpha > 0;
- loader.AllowLoad.Set();
- });
-
- AddAssert("loaded", () => loader.Logo != null && loader.ScreenLoaded);
- AddAssert("logo was visible", () => logoVisible);
AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0);
}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs
index 0c789d8cb7..f24589ed35 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs
@@ -17,13 +17,13 @@ namespace osu.Game.Tests.Visual.Menus
{
typeof(ToolbarButton),
typeof(ToolbarRulesetSelector),
- typeof(ToolbarRulesetButton),
+ typeof(ToolbarRulesetTabButton),
typeof(ToolbarNotificationButton),
};
public TestSceneToolbar()
{
- var toolbar = new Toolbar { State = Visibility.Visible };
+ var toolbar = new Toolbar { State = { Value = Visibility.Visible } };
ToolbarNotificationButton notificationButton = null;
AddStep("create toolbar", () =>
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs
index 21b97fe73b..8d842fc865 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs
@@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
@@ -37,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
settings = new TestRoomSettings
{
RelativeSizeAxes = Axes.Both,
- State = Visibility.Visible
+ State = { Value = Visibility.Visible }
};
Child = settings;
@@ -57,7 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set name", () => Room.Name.Value = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
- AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo }));
+ AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo }));
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
AddStep("clear name", () => Room.Name.Value = "");
diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
index a7e725ec3f..35449f5687 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online
api.Logout();
api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
- AddStep("show", () => accountCreation.State = Visibility.Visible);
+ AddStep("show", () => accountCreation.Show());
AddStep("logout", () => api.Logout());
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs
new file mode 100644
index 0000000000..fe94165777
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs
@@ -0,0 +1,95 @@
+// 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.Overlays.BeatmapSet;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ [TestFixture]
+ public class TestSceneBeatmapAvailability : OsuTestScene
+ {
+ private readonly BeatmapAvailability container;
+
+ public TestSceneBeatmapAvailability()
+ {
+ Add(container = new BeatmapAvailability());
+ }
+
+ [Test]
+ public void TestUndownloadableWithLink()
+ {
+ AddStep("set undownloadable beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Availability = new BeatmapSetOnlineAvailability
+ {
+ DownloadDisabled = true,
+ ExternalLink = @"https://osu.ppy.sh",
+ },
+ },
+ });
+
+ visiblityAssert(true);
+ }
+
+ [Test]
+ public void TestUndownloadableNoLink()
+ {
+ AddStep("set undownloadable beatmapset without link", () => container.BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Availability = new BeatmapSetOnlineAvailability
+ {
+ DownloadDisabled = true,
+ },
+ },
+ });
+
+ visiblityAssert(true);
+ }
+
+ [Test]
+ public void TestPartsRemovedWithLink()
+ {
+ AddStep("set parts-removed beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Availability = new BeatmapSetOnlineAvailability
+ {
+ DownloadDisabled = false,
+ ExternalLink = @"https://osu.ppy.sh",
+ },
+ },
+ });
+
+ visiblityAssert(true);
+ }
+
+ [Test]
+ public void TestNormal()
+ {
+ AddStep("set normal beatmapset", () => container.BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Availability = new BeatmapSetOnlineAvailability
+ {
+ DownloadDisabled = false,
+ },
+ },
+ });
+
+ visiblityAssert(false);
+ }
+
+ private void visiblityAssert(bool shown)
+ {
+ AddAssert($"is container {(shown ? "visible" : "hidden")}", () => container.Alpha == (shown ? 1 : 0));
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index 5910da7b88..a9c44c9020 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneBeatmapSetOverlay : OsuTestScene
{
- private readonly BeatmapSetOverlay overlay;
+ private readonly TestBeatmapSetOverlay overlay;
public override IReadOnlyList RequiredTypes => new[]
{
@@ -32,375 +32,204 @@ namespace osu.Game.Tests.Visual.Online
typeof(BasicStats),
typeof(BeatmapPicker),
typeof(Details),
- typeof(DownloadButton),
+ typeof(HeaderDownloadButton),
typeof(FavouriteButton),
typeof(Header),
typeof(HeaderButton),
typeof(Info),
typeof(PreviewButton),
typeof(SuccessRate),
+ typeof(BeatmapAvailability),
};
+ private RulesetInfo taikoRuleset;
+ private RulesetInfo maniaRuleset;
+
public TestSceneBeatmapSetOverlay()
{
- Add(overlay = new BeatmapSetOverlay());
+ Add(overlay = new TestBeatmapSetOverlay());
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
- var mania = rulesets.GetRuleset(3);
- var taiko = rulesets.GetRuleset(1);
+ taikoRuleset = rulesets.GetRuleset(1);
+ maniaRuleset = rulesets.GetRuleset(3);
+ }
+ [Test]
+ public void TestLoading()
+ {
AddStep(@"show loading", () => overlay.ShowBeatmapSet(null));
+ }
+ [Test]
+ public void TestOnline()
+ {
AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55));
+ }
+ [Test]
+ public void TestLocalBeatmaps()
+ {
AddStep(@"show first", () =>
{
overlay.ShowBeatmapSet(new BeatmapSetInfo
{
+ OnlineBeatmapSetID = 1235,
Metadata = new BeatmapMetadata
{
- Title = @"Lachryma ",
- Artist = @"Kaneko Chiharu",
- Source = @"SOUND VOLTEX III GRAVITY WARS",
- Tags = @"sdvx grace the 5th kac original song contest konami bemani",
+ Title = @"an awesome beatmap",
+ Artist = @"naru narusegawa",
+ Source = @"hinata sou",
+ Tags = @"test tag tag more tag",
Author = new User
{
- Username = @"Fresh Chicken",
- Id = 3984370,
+ Username = @"BanchoBot",
+ Id = 3,
},
},
OnlineInfo = new BeatmapSetOnlineInfo
{
- Preview = @"https://b.ppy.sh/preview/415886.mp3",
- PlayCount = 681380,
- FavouriteCount = 356,
- Submitted = new DateTime(2016, 2, 10),
- Ranked = new DateTime(2016, 6, 19),
- Status = BeatmapSetOnlineStatus.Ranked,
- BPM = 236,
+ Preview = @"https://b.ppy.sh/preview/12345.mp3",
+ PlayCount = 123,
+ FavouriteCount = 456,
+ Submitted = DateTime.Now,
+ Ranked = DateTime.Now,
+ BPM = 111,
HasVideo = true,
- Covers = new BeatmapSetOnlineCovers
- {
- Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778",
- },
+ HasStoryboard = true,
+ Covers = new BeatmapSetOnlineCovers(),
},
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
Beatmaps = new List
{
new BeatmapInfo
{
- StarDifficulty = 1.36,
- Version = @"BASIC",
- Ruleset = mania,
+ StarDifficulty = 9.99,
+ Version = @"TEST",
+ Ruleset = maniaRuleset,
BaseDifficulty = new BeatmapDifficulty
{
- CircleSize = 4,
- DrainRate = 6.5f,
- OverallDifficulty = 6.5f,
- ApproachRate = 5,
+ CircleSize = 1,
+ DrainRate = 2.3f,
+ OverallDifficulty = 4.5f,
+ ApproachRate = 6,
},
OnlineInfo = new BeatmapOnlineInfo
{
- Length = 115000,
- CircleCount = 265,
- SliderCount = 71,
- PlayCount = 47906,
- PassCount = 19899,
+ Length = 456000,
+ CircleCount = 111,
+ SliderCount = 12,
+ PlayCount = 222,
+ PassCount = 21,
},
Metrics = new BeatmapMetrics
{
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
- },
- },
- new BeatmapInfo
- {
- StarDifficulty = 2.22,
- Version = @"NOVICE",
- Ruleset = mania,
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 4,
- DrainRate = 7,
- OverallDifficulty = 7,
- ApproachRate = 5,
- },
- OnlineInfo = new BeatmapOnlineInfo
- {
- Length = 118000,
- CircleCount = 592,
- SliderCount = 62,
- PlayCount = 162021,
- PassCount = 72116,
- },
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
- },
- },
- new BeatmapInfo
- {
- StarDifficulty = 3.49,
- Version = @"ADVANCED",
- Ruleset = mania,
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 4,
- DrainRate = 7.5f,
- OverallDifficulty = 7.5f,
- ApproachRate = 5,
- },
- OnlineInfo = new BeatmapOnlineInfo
- {
- Length = 118000,
- CircleCount = 1042,
- SliderCount = 79,
- PlayCount = 225178,
- PassCount = 73001,
- },
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
- },
- },
- new BeatmapInfo
- {
- StarDifficulty = 4.24,
- Version = @"EXHAUST",
- Ruleset = mania,
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 4,
- DrainRate = 8,
- OverallDifficulty = 8,
- ApproachRate = 5,
- },
- OnlineInfo = new BeatmapOnlineInfo
- {
- Length = 118000,
- CircleCount = 1352,
- SliderCount = 69,
- PlayCount = 131545,
- PassCount = 42703,
- },
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
- },
- },
- new BeatmapInfo
- {
- StarDifficulty = 5.26,
- Version = @"GRAVITY",
- Ruleset = mania,
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 4,
- DrainRate = 8.5f,
- OverallDifficulty = 8.5f,
- ApproachRate = 5,
- },
- OnlineInfo = new BeatmapOnlineInfo
- {
- Length = 118000,
- CircleCount = 1730,
- SliderCount = 115,
- PlayCount = 117673,
- PassCount = 24241,
- },
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
},
},
});
});
- AddStep(@"show second", () =>
+ downloadAssert(true);
+ }
+
+ [Test]
+ public void TestAvailability()
+ {
+ AddStep(@"show undownloadable", () =>
{
overlay.ShowBeatmapSet(new BeatmapSetInfo
{
+ OnlineBeatmapSetID = 1234,
Metadata = new BeatmapMetadata
{
- Title = @"Soumatou Labyrinth",
- Artist = @"Yunomi with Momobako&miko",
- Tags = @"mmbk.com yuzu__rinrin charlotte",
+ Title = @"undownloadable beatmap",
+ Artist = @"no one",
+ Source = @"some source",
+ Tags = @"another test tag tag more test tags",
Author = new User
{
- Username = @"komasy",
- Id = 1980256,
+ Username = @"BanchoBot",
+ Id = 3,
},
},
OnlineInfo = new BeatmapSetOnlineInfo
{
- Preview = @"https://b.ppy.sh/preview/625493.mp3",
- PlayCount = 22996,
- FavouriteCount = 58,
- Submitted = new DateTime(2016, 6, 11),
- Ranked = new DateTime(2016, 7, 12),
- Status = BeatmapSetOnlineStatus.Pending,
- BPM = 160,
- HasVideo = false,
- Covers = new BeatmapSetOnlineCovers
+ Availability = new BeatmapSetOnlineAvailability
{
- Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472",
+ DownloadDisabled = true,
+ ExternalLink = "https://osu.ppy.sh",
},
+ Preview = @"https://b.ppy.sh/preview/1234.mp3",
+ PlayCount = 123,
+ FavouriteCount = 456,
+ Submitted = DateTime.Now,
+ Ranked = DateTime.Now,
+ BPM = 111,
+ HasVideo = true,
+ HasStoryboard = true,
+ Covers = new BeatmapSetOnlineCovers(),
},
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
Beatmaps = new List
{
new BeatmapInfo
{
- StarDifficulty = 1.40,
- Version = @"yzrin's Kantan",
- Ruleset = taiko,
+ StarDifficulty = 5.67,
+ Version = @"ANOTHER TEST",
+ Ruleset = taikoRuleset,
BaseDifficulty = new BeatmapDifficulty
{
- CircleSize = 2,
- DrainRate = 7,
- OverallDifficulty = 3,
- ApproachRate = 10,
+ CircleSize = 9,
+ DrainRate = 8,
+ OverallDifficulty = 7,
+ ApproachRate = 6,
},
OnlineInfo = new BeatmapOnlineInfo
{
- Length = 193000,
- CircleCount = 262,
- SliderCount = 0,
- PlayCount = 3952,
- PassCount = 1373,
+ Length = 123000,
+ CircleCount = 123,
+ SliderCount = 45,
+ PlayCount = 567,
+ PassCount = 89,
},
Metrics = new BeatmapMetrics
{
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
- },
- },
- new BeatmapInfo
- {
- StarDifficulty = 2.23,
- Version = @"Futsuu",
- Ruleset = taiko,
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 2,
- DrainRate = 6,
- OverallDifficulty = 4,
- ApproachRate = 10,
- },
- OnlineInfo = new BeatmapOnlineInfo
- {
- Length = 193000,
- CircleCount = 464,
- SliderCount = 0,
- PlayCount = 4833,
- PassCount = 920,
- },
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
- },
- },
- new BeatmapInfo
- {
- StarDifficulty = 3.19,
- Version = @"Muzukashii",
- Ruleset = taiko,
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 2,
- DrainRate = 6,
- OverallDifficulty = 5,
- ApproachRate = 10,
- },
- OnlineInfo = new BeatmapOnlineInfo
- {
- Length = 193000,
- CircleCount = 712,
- SliderCount = 0,
- PlayCount = 4405,
- PassCount = 854,
- },
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
- },
- },
- new BeatmapInfo
- {
- StarDifficulty = 3.97,
- Version = @"Charlotte's Oni",
- Ruleset = taiko,
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 5,
- DrainRate = 6,
- OverallDifficulty = 5.5f,
- ApproachRate = 10,
- },
- OnlineInfo = new BeatmapOnlineInfo
- {
- Length = 193000,
- CircleCount = 943,
- SliderCount = 0,
- PlayCount = 3950,
- PassCount = 693,
- },
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
- },
- },
- new BeatmapInfo
- {
- StarDifficulty = 5.08,
- Version = @"Labyrinth Oni",
- Ruleset = taiko,
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 5,
- DrainRate = 5,
- OverallDifficulty = 6,
- ApproachRate = 10,
- },
- OnlineInfo = new BeatmapOnlineInfo
- {
- Length = 193000,
- CircleCount = 1068,
- SliderCount = 0,
- PlayCount = 5856,
- PassCount = 1207,
- },
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
},
},
});
});
+ downloadAssert(false);
+ }
+
+ [Test]
+ public void TestHide()
+ {
AddStep(@"hide", overlay.Hide);
+ }
+
+ [Test]
+ public void TestShowWithNoReload()
+ {
AddStep(@"show without reload", overlay.Show);
}
+
+ private void downloadAssert(bool shown)
+ {
+ AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.DownloadButtonsVisible == shown);
+ }
+
+ private class TestBeatmapSetOverlay : BeatmapSetOverlay
+ {
+ public bool DownloadButtonsVisible => Header.DownloadButtonsVisible;
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs
new file mode 100644
index 0000000000..2a45e68c0a
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs
@@ -0,0 +1,69 @@
+// 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.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.MathUtils;
+using osu.Game.Beatmaps;
+using osu.Game.Overlays.BeatmapSet;
+using osu.Game.Screens.Select.Details;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneBeatmapSetOverlayDetails : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(Details)
+ };
+
+ private RatingsExposingDetails details;
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ Child = details = new RatingsExposingDetails
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre
+ };
+ });
+
+ [Test]
+ public void TestMetrics()
+ {
+ var firstSet = createSet();
+ var secondSet = createSet();
+
+ AddStep("set first set", () => details.BeatmapSet = firstSet);
+ AddAssert("ratings set", () => details.Ratings.Metrics == firstSet.Metrics);
+
+ AddStep("set second set", () => details.BeatmapSet = secondSet);
+ AddAssert("ratings set", () => details.Ratings.Metrics == secondSet.Metrics);
+
+ BeatmapSetInfo createSet() => new BeatmapSetInfo
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray() },
+ Beatmaps = new List
+ {
+ new BeatmapInfo
+ {
+ Metrics = new BeatmapMetrics
+ {
+ Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
+ }
+ }
+ }
+ };
+ }
+
+ private class RatingsExposingDetails : Details
+ {
+ public new UserRatings Ratings => base.Ratings;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs
new file mode 100644
index 0000000000..05f5c117e4
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs
@@ -0,0 +1,82 @@
+// 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.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.MathUtils;
+using osu.Game.Beatmaps;
+using osu.Game.Overlays.BeatmapSet;
+using osu.Game.Screens.Select.Details;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneBeatmapSetOverlaySuccessRate : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(Details)
+ };
+
+ private GraphExposingSuccessRate successRate;
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(275, 220),
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Gray,
+ },
+ successRate = new GraphExposingSuccessRate
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(275, 220),
+ Padding = new MarginPadding(20)
+ }
+ }
+ };
+ });
+
+ [Test]
+ public void TestMetrics()
+ {
+ var firstBeatmap = createBeatmap();
+ var secondBeatmap = createBeatmap();
+
+ AddStep("set first set", () => successRate.Beatmap = firstBeatmap);
+ AddAssert("ratings set", () => successRate.Graph.Metrics == firstBeatmap.Metrics);
+
+ AddStep("set second set", () => successRate.Beatmap = secondBeatmap);
+ AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics);
+
+ BeatmapInfo createBeatmap() => new BeatmapInfo
+ {
+ Metrics = new BeatmapMetrics
+ {
+ Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
+ }
+ };
+ }
+
+ private class GraphExposingSuccessRate : SuccessRate
+ {
+ public new FailRetryGraph Graph => base.Graph;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
new file mode 100644
index 0000000000..0655611230
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
@@ -0,0 +1,70 @@
+// 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.Collections.Generic;
+using NUnit.Framework;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Changelog;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ [TestFixture]
+ public class TestSceneChangelogOverlay : OsuTestScene
+ {
+ private ChangelogOverlay changelog;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(UpdateStreamBadgeArea),
+ typeof(UpdateStreamBadge),
+ typeof(ChangelogHeader),
+ typeof(ChangelogContent),
+ typeof(ChangelogListing),
+ typeof(ChangelogSingleBuild),
+ typeof(ChangelogBuild),
+ };
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Add(changelog = new ChangelogOverlay());
+ AddStep(@"Show", changelog.Show);
+ AddStep(@"Hide", changelog.Hide);
+
+ AddWaitStep("wait for hide", 3);
+
+ AddStep(@"Show with Lazer 2018.712.0", () =>
+ {
+ changelog.ShowBuild(new APIChangelogBuild
+ {
+ Version = "2018.712.0",
+ DisplayVersion = "2018.712.0",
+ UpdateStream = new APIUpdateStream { Name = OsuGameBase.CLIENT_STREAM_NAME },
+ ChangelogEntries = new List
+ {
+ new APIChangelogEntry
+ {
+ Category = "Test",
+ Title = "Title",
+ MessageHtml = "Message",
+ }
+ }
+ });
+ changelog.Show();
+ });
+
+ AddWaitStep("wait for show", 3);
+ AddStep(@"Hide", changelog.Hide);
+ AddWaitStep("wait for hide", 3);
+
+ AddStep(@"Show with listing", () =>
+ {
+ changelog.ShowListing();
+ changelog.Show();
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs
index d93daba4d4..364c986723 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
+using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using osu.Game.Overlays.Chat.Tabs;
using osu.Game.Users;
@@ -61,7 +62,7 @@ namespace osu.Game.Tests.Visual.Online
Anchor = Anchor.TopLeft,
Children = new Drawable[]
{
- currentText = new SpriteText
+ currentText = new OsuSpriteText
{
Text = "Currently selected channel:"
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatDisplay.cs
deleted file mode 100644
index 634176e65f..0000000000
--- a/osu.Game.Tests/Visual/Online/TestSceneChatDisplay.cs
+++ /dev/null
@@ -1,45 +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;
-using System.Collections.Generic;
-using System.ComponentModel;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Online.Chat;
-using osu.Game.Overlays;
-using osu.Game.Overlays.Chat;
-using osu.Game.Overlays.Chat.Tabs;
-
-namespace osu.Game.Tests.Visual.Online
-{
- [Description("Testing chat api and overlay")]
- public class TestSceneChatDisplay : OsuTestScene
- {
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(ChatOverlay),
- typeof(ChatLine),
- typeof(DrawableChannel),
- typeof(ChannelSelectorTabItem),
- typeof(ChannelTabControl),
- typeof(ChannelTabItem),
- typeof(PrivateChannelTabItem),
- typeof(TabCloseButton)
- };
-
- [Cached]
- private readonly ChannelManager channelManager = new ChannelManager();
-
- [BackgroundDependencyLoader]
- private void load()
- {
- Children = new Drawable[]
- {
- channelManager,
- new ChatOverlay { State = Visibility.Visible }
- };
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
new file mode 100644
index 0000000000..4d3992ce13
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -0,0 +1,157 @@
+// 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.Collections.Generic;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Online.Chat;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Chat;
+using osu.Game.Overlays.Chat.Selection;
+using osu.Game.Overlays.Chat.Tabs;
+using osu.Game.Users;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneChatOverlay : ManualInputManagerTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ChatLine),
+ typeof(DrawableChannel),
+ typeof(ChannelSelectorTabItem),
+ typeof(ChannelTabControl),
+ typeof(ChannelTabItem),
+ typeof(PrivateChannelTabItem),
+ typeof(TabCloseButton)
+ };
+
+ private TestChatOverlay chatOverlay;
+ private ChannelManager channelManager;
+
+ private readonly Channel channel1 = new Channel(new User()) { Name = "test1" };
+ private readonly Channel channel2 = new Channel(new User()) { Name = "test2" };
+
+ [SetUp]
+ public void Setup()
+ {
+ Schedule(() =>
+ {
+ ChannelManagerContainer container;
+
+ Child = container = new ChannelManagerContainer(new List { channel1, channel2 })
+ {
+ RelativeSizeAxes = Axes.Both,
+ };
+
+ chatOverlay = container.ChatOverlay;
+ channelManager = container.ChannelManager;
+ });
+ }
+
+ [Test]
+ public void TestHideOverlay()
+ {
+ AddAssert("Chat overlay is visible", () => chatOverlay.State.Value == Visibility.Visible);
+ AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+
+ AddStep("Close chat overlay", () => chatOverlay.Hide());
+
+ AddAssert("Chat overlay was hidden", () => chatOverlay.State.Value == Visibility.Hidden);
+ AddAssert("Channel selection overlay was hidden", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+ }
+
+ [Test]
+ public void TestSelectingChannelClosesSelector()
+ {
+ AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+
+ AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
+ AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
+
+ AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
+ AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+ }
+
+ [Test]
+ public void TestCloseChannelWhileSelectorClosed()
+ {
+ AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
+ AddStep("Join channel 2", () => channelManager.JoinChannel(channel2));
+
+ AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
+ AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
+
+ AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+ AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
+
+ AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
+
+ AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+ }
+
+ private void clickDrawable(Drawable d)
+ {
+ InputManager.MoveMouseTo(d);
+ InputManager.Click(MouseButton.Left);
+ }
+
+ private class ChannelManagerContainer : Container
+ {
+ public TestChatOverlay ChatOverlay { get; private set; }
+
+ [Cached]
+ public ChannelManager ChannelManager { get; } = new ChannelManager();
+
+ private readonly List channels;
+
+ public ChannelManagerContainer(List channels)
+ {
+ this.channels = channels;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ ((BindableList)ChannelManager.AvailableChannels).AddRange(channels);
+
+ Child = ChatOverlay = new TestChatOverlay { RelativeSizeAxes = Axes.Both, };
+ ChatOverlay.Show();
+ }
+ }
+
+ private class TestChatOverlay : ChatOverlay
+ {
+ public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value;
+
+ public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay;
+
+ protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl();
+
+ public IReadOnlyDictionary> TabMap => ((TestTabControl)ChannelTabControl).TabMap;
+ }
+
+ private class TestTabControl : ChannelTabControl
+ {
+ protected override TabItem CreateTabItem(Channel value) => new TestChannelTabItem(value);
+
+ public new IReadOnlyDictionary> TabMap => base.TabMap;
+ }
+
+ private class TestChannelTabItem : PrivateChannelTabItem
+ {
+ public TestChannelTabItem(Channel channel)
+ : base(channel)
+ {
+ }
+
+ public new ClickableContainer CloseButton => base.CloseButton;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
new file mode 100644
index 0000000000..5b0c2d3c67
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
@@ -0,0 +1,158 @@
+// 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.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Online;
+using osu.Game.Overlays.Direct;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Tests.Resources;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneDirectDownloadButton : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(PanelDownloadButton)
+ };
+
+ private TestDownloadButton downloadButton;
+
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
+
+ [Test]
+ public void TestDownloadableBeatmap()
+ {
+ createButton(true);
+ assertEnabled(true);
+ }
+
+ [Test]
+ public void TestUndownloadableBeatmap()
+ {
+ createButton(false);
+ assertEnabled(false);
+ }
+
+ [Test]
+ public void TestDownloadState()
+ {
+ AddUntilStep("ensure manager loaded", () => beatmaps != null);
+ ensureSoleilyRemoved();
+ createButtonWithBeatmap(createSoleily());
+ AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
+ AddStep("import soleily", () => beatmaps.Import(new[] { TestResources.GetTestBeatmapForImport() }));
+ AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineBeatmapSetID == 241526));
+ createButtonWithBeatmap(createSoleily());
+ AddAssert("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable);
+ ensureSoleilyRemoved();
+ AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
+ }
+
+ private void ensureSoleilyRemoved()
+ {
+ AddStep("remove soleily", () =>
+ {
+ var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == 241526);
+
+ if (beatmap != null) beatmaps.Delete(beatmap);
+ });
+ }
+
+ private void assertEnabled(bool enabled)
+ {
+ AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => downloadButton.DownloadEnabled == enabled);
+ }
+
+ private BeatmapSetInfo createSoleily()
+ {
+ return new BeatmapSetInfo
+ {
+ ID = 1,
+ OnlineBeatmapSetID = 241526,
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Availability = new BeatmapSetOnlineAvailability
+ {
+ DownloadDisabled = false,
+ ExternalLink = string.Empty,
+ },
+ },
+ };
+ }
+
+ private void createButtonWithBeatmap(BeatmapSetInfo beatmap)
+ {
+ AddStep("create button", () =>
+ {
+ Child = downloadButton = new TestDownloadButton(beatmap)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(75, 50),
+ };
+ });
+ }
+
+ private void createButton(bool downloadable)
+ {
+ AddStep("create button", () =>
+ {
+ Child = downloadButton = new TestDownloadButton(downloadable ? getDownloadableBeatmapSet() : getUndownloadableBeatmapSet())
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(75, 50),
+ };
+ });
+ }
+
+ private BeatmapSetInfo getDownloadableBeatmapSet()
+ {
+ var normal = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo;
+ normal.OnlineInfo.HasVideo = true;
+ normal.OnlineInfo.HasStoryboard = true;
+
+ return normal;
+ }
+
+ private BeatmapSetInfo getUndownloadableBeatmapSet()
+ {
+ var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo;
+ beatmap.Metadata.Artist = "test";
+ beatmap.Metadata.Title = "undownloadable";
+ beatmap.Metadata.AuthorString = "test";
+
+ beatmap.OnlineInfo.HasVideo = true;
+ beatmap.OnlineInfo.HasStoryboard = true;
+
+ beatmap.OnlineInfo.Availability = new BeatmapSetOnlineAvailability
+ {
+ DownloadDisabled = true,
+ ExternalLink = "http://osu.ppy.sh",
+ };
+
+ return beatmap;
+ }
+
+ private class TestDownloadButton : PanelDownloadButton
+ {
+ public new bool DownloadEnabled => base.DownloadEnabled;
+
+ public DownloadState DownloadState => State.Value;
+
+ public TestDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
+ : base(beatmapSet, noVideo)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
index a3d932a383..53dbaeddda 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
@@ -6,9 +6,11 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
using osu.Game.Overlays.Direct;
+using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
-using osu.Game.Tests.Beatmaps;
+using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Online
@@ -22,25 +24,74 @@ namespace osu.Game.Tests.Visual.Online
typeof(IconPill)
};
+ private BeatmapSetInfo getUndownloadableBeatmapSet(RulesetInfo ruleset) => new BeatmapSetInfo
+ {
+ OnlineBeatmapSetID = 123,
+ Metadata = new BeatmapMetadata
+ {
+ Title = "undownloadable beatmap",
+ Artist = "test",
+ Source = "more tests",
+ Author = new User
+ {
+ Username = "BanchoBot",
+ Id = 3,
+ },
+ },
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Availability = new BeatmapSetOnlineAvailability
+ {
+ DownloadDisabled = true,
+ },
+ Preview = @"https://b.ppy.sh/preview/12345.mp3",
+ PlayCount = 123,
+ FavouriteCount = 456,
+ BPM = 111,
+ HasVideo = true,
+ HasStoryboard = true,
+ Covers = new BeatmapSetOnlineCovers(),
+ },
+ Beatmaps = new List
+ {
+ new BeatmapInfo
+ {
+ Ruleset = ruleset,
+ Version = "Test",
+ StarDifficulty = 6.42,
+ }
+ }
+ };
+
[BackgroundDependencyLoader]
private void load()
{
- var beatmap = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
- beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true;
- beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true;
+ var ruleset = new OsuRuleset().RulesetInfo;
- Child = new FillFlowContainer
+ var normal = CreateWorkingBeatmap(ruleset).BeatmapSetInfo;
+ normal.OnlineInfo.HasVideo = true;
+ normal.OnlineInfo.HasStoryboard = true;
+
+ var undownloadable = getUndownloadableBeatmapSet(ruleset);
+
+ Child = new BasicScrollContainer
{
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Padding = new MarginPadding(20),
- Spacing = new Vector2(0, 20),
- Children = new Drawable[]
+ RelativeSizeAxes = Axes.Both,
+ Child = new FillFlowContainer
{
- new DirectGridPanel(beatmap.BeatmapSetInfo),
- new DirectListPanel(beatmap.BeatmapSetInfo)
- }
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Padding = new MarginPadding(20),
+ Spacing = new Vector2(0, 20),
+ Children = new Drawable[]
+ {
+ new DirectGridPanel(normal),
+ new DirectListPanel(normal),
+ new DirectGridPanel(undownloadable),
+ new DirectListPanel(undownloadable),
+ },
+ },
};
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
index 6dc3428bff..fe8437be17 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
@@ -18,8 +18,24 @@ namespace osu.Game.Tests.Visual.Online
{
base.LoadComplete();
+ int fireCount = 0;
+
Add(overlay = new TestFullscreenOverlay());
- AddStep(@"toggle", overlay.ToggleVisibility);
+
+ overlay.State.ValueChanged += _ => fireCount++;
+
+ AddStep(@"show", overlay.Show);
+
+ AddAssert("fire count 1", () => fireCount == 1);
+
+ AddStep(@"show again", overlay.Show);
+
+ // this logic is specific to FullscreenOverlay
+ AddAssert("fire count 2", () => fireCount == 2);
+
+ AddStep(@"hide", overlay.Hide);
+
+ AddAssert("fire count 3", () => fireCount == 3);
}
private class TestFullscreenOverlay : FullscreenOverlay
diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
index 455807649a..883f0c5e3f 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
@@ -5,9 +5,9 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Historical;
using osu.Game.Users;
@@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Online
Colour = OsuColour.Gray(0.2f)
});
- Add(new ScrollContainer
+ Add(new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = section = new HistoricalSection(),
diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs
new file mode 100644
index 0000000000..c344cb9598
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs
@@ -0,0 +1,40 @@
+// 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.Graphics;
+using osu.Game.Overlays.Profile.Header.Components;
+using System;
+using System.Collections.Generic;
+using osu.Game.Rulesets.Catch;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Taiko;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneProfileRulesetSelector : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ProfileRulesetSelector),
+ typeof(ProfileRulesetTabItem),
+ };
+
+ public TestSceneProfileRulesetSelector()
+ {
+ ProfileRulesetSelector selector;
+
+ Child = selector = new ProfileRulesetSelector
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+
+ AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo));
+ AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo));
+ AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo));
+ AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo));
+ AddStep("select default ruleset", selector.SelectDefaultRuleset);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs
index c04a4249cc..709e75ab13 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs
@@ -31,6 +31,7 @@ namespace osu.Game.Tests.Visual.Online
var data = new int[89];
var dataWithZeros = new int[89];
var smallData = new int[89];
+ var edgyData = new int[89];
for (int i = 0; i < 89; i++)
data[i] = dataWithZeros[i] = (i + 1) * 1000;
@@ -41,6 +42,14 @@ namespace osu.Game.Tests.Visual.Online
for (int i = 79; i < 89; i++)
smallData[i] = 100000 - i * 1000;
+ bool edge = true;
+
+ for (int i = 0; i < 20; i++)
+ {
+ edgyData[i] = 100000 + (edge ? 1000 : -1000) * (i + 1);
+ edge = !edge;
+ }
+
Add(new Container
{
Anchor = Anchor.Centre,
@@ -120,6 +129,22 @@ namespace osu.Game.Tests.Visual.Online
}
};
});
+
+ AddStep("graph with edges", () =>
+ {
+ graph.User.Value = new User
+ {
+ Statistics = new UserStatistics
+ {
+ Ranks = new UserStatistics.UserRanks { Global = 12000 },
+ PP = 12345,
+ },
+ RankHistory = new User.RankHistoryData
+ {
+ Data = edgyData,
+ }
+ };
+ });
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs
index 6815018be6..06414af865 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -39,8 +40,7 @@ namespace osu.Game.Tests.Visual.Online
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- AutoSizeAxes = Axes.Y,
- RelativeSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Both,
Width = 0.8f,
Children = new Drawable[]
{
@@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.Online
FlagName = @"TH",
},
},
- Rank = ScoreRank.F,
+ Rank = ScoreRank.D,
PP = 160,
MaxCombo = 1234,
TotalScore = 123456,
@@ -173,7 +173,9 @@ namespace osu.Game.Tests.Visual.Online
s.Statistics.Add(HitResult.Miss, RNG.Next(2000));
}
- scoresContainer.Scores = scores;
+ AddStep("Load all scores", () => scoresContainer.Scores = scores);
+ AddStep("Load null scores", () => scoresContainer.Scores = null);
+ AddStep("Load only one score", () => scoresContainer.Scores = new[] { scores.First() });
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs
new file mode 100644
index 0000000000..bccb263600
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs
@@ -0,0 +1,55 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Overlays.Profile.Sections;
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneShowMoreButton : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ShowMoreButton),
+ };
+
+ public TestSceneShowMoreButton()
+ {
+ ShowMoreButton button = null;
+
+ int fireCount = 0;
+
+ Add(button = new ShowMoreButton
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Action = () =>
+ {
+ fireCount++;
+ // ReSharper disable once AccessToModifiedClosure
+ // ReSharper disable once PossibleNullReferenceException
+ Scheduler.AddDelayed(() => button.IsLoading = false, 2000);
+ }
+ });
+
+ AddStep("click button", () => button.Click());
+
+ AddAssert("action fired once", () => fireCount == 1);
+ AddAssert("is in loading state", () => button.IsLoading);
+
+ AddStep("click button", () => button.Click());
+
+ AddAssert("action not fired", () => fireCount == 1);
+ AddAssert("is in loading state", () => button.IsLoading);
+
+ AddUntilStep("wait for loaded", () => !button.IsLoading);
+
+ AddStep("click button", () => button.Click());
+
+ AddAssert("action fired twice", () => fireCount == 2);
+ AddAssert("is in loading state", () => button.IsLoading);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
index fca18a9263..54f06d6ad2 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Users;
@@ -12,10 +13,12 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneUserPanel : OsuTestScene
{
+ private readonly UserPanel peppy;
+
public TestSceneUserPanel()
{
UserPanel flyte;
- UserPanel peppy;
+
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
@@ -44,13 +47,31 @@ namespace osu.Game.Tests.Visual.Online
});
flyte.Status.Value = new UserStatusOnline();
- peppy.Status.Value = new UserStatusSoloGame();
+ peppy.Status.Value = null;
+ }
- AddStep(@"spectating", () => { flyte.Status.Value = new UserStatusSpectating(); });
- AddStep(@"multiplaying", () => { flyte.Status.Value = new UserStatusMultiplayerGame(); });
- AddStep(@"modding", () => { flyte.Status.Value = new UserStatusModding(); });
- AddStep(@"offline", () => { flyte.Status.Value = new UserStatusOffline(); });
- AddStep(@"null status", () => { flyte.Status.Value = null; });
+ [Test]
+ public void UserStatusesTests()
+ {
+ AddStep("online", () => { peppy.Status.Value = new UserStatusOnline(); });
+ AddStep(@"do not disturb", () => { peppy.Status.Value = new UserStatusDoNotDisturb(); });
+ AddStep(@"offline", () => { peppy.Status.Value = new UserStatusOffline(); });
+ AddStep(@"null status", () => { peppy.Status.Value = null; });
+ }
+
+ [Test]
+ public void UserActivitiesTests()
+ {
+ Bindable activity = new Bindable();
+
+ peppy.Activity.BindTo(activity);
+
+ AddStep("idle", () => { activity.Value = null; });
+ AddStep("spectating", () => { activity.Value = new UserActivity.Spectating(); });
+ AddStep("solo", () => { activity.Value = new UserActivity.SoloGame(null, null); });
+ AddStep("choosing", () => { activity.Value = new UserActivity.ChoosingBeatmap(); });
+ AddStep("editing", () => { activity.Value = new UserActivity.Editing(null); });
+ AddStep("modding", () => { activity.Value = new UserActivity.Modding(); });
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
index 14c81558c1..2285c9b799 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
@@ -7,6 +7,7 @@ using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
+using osu.Game.Overlays;
using osu.Game.Overlays.Profile;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Overlays.Profile.Header.Components;
@@ -21,7 +22,7 @@ namespace osu.Game.Tests.Visual.Online
typeof(ProfileHeader),
typeof(RankGraph),
typeof(LineGraph),
- typeof(ProfileHeaderTabControl),
+ typeof(OverlayHeaderTabControl),
typeof(CentreHeaderContainer),
typeof(BottomHeaderContainer),
typeof(DetailHeaderContainer),
@@ -38,13 +39,27 @@ namespace osu.Game.Tests.Visual.Online
header = new ProfileHeader();
Add(header);
- AddStep("Show offline dummy", () => header.User.Value = TestSceneUserProfileOverlay.TEST_USER);
+ AddStep("Show test dummy", () => header.User.Value = TestSceneUserProfileOverlay.TEST_USER);
AddStep("Show null dummy", () => header.User.Value = new User
{
Username = "Null"
});
+ AddStep("Show online dummy", () => header.User.Value = new User
+ {
+ Username = "IAmOnline",
+ LastVisit = DateTimeOffset.Now,
+ IsOnline = true,
+ });
+
+ AddStep("Show offline dummy", () => header.User.Value = new User
+ {
+ Username = "IAmOffline",
+ LastVisit = DateTimeOffset.Now,
+ IsOnline = false,
+ });
+
addOnlineStep("Show ppy", new User
{
Username = @"peppy",
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs
index d60e723102..f022425bf6 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Profile.Sections;
@@ -36,7 +37,7 @@ namespace osu.Game.Tests.Visual.Online
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
- new ScrollContainer
+ new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
index 70118b5ebd..9f0a8c769a 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Users;
@@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
- new ScrollContainer
+ new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = ranks = new RanksSection(),
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyConfiguration.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
similarity index 50%
rename from osu.Game.Tests/Visual/Settings/TestSceneKeyConfiguration.cs
rename to osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
index d06d82ddb5..426ff988c4 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneKeyConfiguration.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
@@ -1,17 +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 System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Overlays;
+using osu.Game.Overlays.KeyBinding;
namespace osu.Game.Tests.Visual.Settings
{
[TestFixture]
- public class TestSceneKeyConfiguration : OsuTestScene
+ public class TestSceneKeyBindingPanel : OsuTestScene
{
private readonly KeyBindingPanel panel;
- public TestSceneKeyConfiguration()
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(KeyBindingRow),
+ typeof(GlobalKeyBindingsSection),
+ typeof(KeyBindingRow),
+ typeof(KeyBindingsSubsection),
+ typeof(RulesetBindingsSection),
+ typeof(VariantBindingsSubsection),
+ };
+
+ public TestSceneKeyBindingPanel()
{
Child = panel = new KeyBindingPanel();
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
similarity index 67%
rename from osu.Game.Tests/Visual/Settings/TestSceneSettings.cs
rename to osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
index 964754f8d0..668fdf2c20 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneSettings.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
@@ -1,24 +1,33 @@
// 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.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
+using osu.Game.Overlays.Settings;
namespace osu.Game.Tests.Visual.Settings
{
[TestFixture]
- public class TestSceneSettings : OsuTestScene
+ public class TestSceneSettingsPanel : OsuTestScene
{
private readonly SettingsPanel settings;
private readonly DialogOverlay dialogOverlay;
- public TestSceneSettings()
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(SettingsFooter),
+ typeof(SettingsOverlay),
+ };
+
+ public TestSceneSettingsPanel()
{
settings = new SettingsOverlay
{
- State = Visibility.Visible
+ State = { Value = Visibility.Visible }
};
Add(dialogOverlay = new DialogOverlay
{
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs
index cf4362ba28..7b97a27732 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
@@ -18,7 +19,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
public override IReadOnlyList RequiredTypes => new[] { typeof(BeatmapDetails) };
- public TestSceneBeatmapDetailArea()
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase game)
{
BeatmapDetailArea detailsArea;
Add(detailsArea = new BeatmapDetailArea
@@ -28,8 +30,12 @@ namespace osu.Game.Tests.Visual.SongSelect
Size = new Vector2(550f, 450f),
});
- AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap
+ AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
+ BeatmapSetInfo =
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
+ },
BeatmapInfo =
{
Version = "All Metrics",
@@ -48,16 +54,19 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
}
}
);
- AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap
+ AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
+ BeatmapSetInfo =
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
+ },
BeatmapInfo =
{
Version = "All Metrics",
@@ -75,15 +84,18 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
}
});
- AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap
+ AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
+ BeatmapSetInfo =
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
+ },
BeatmapInfo =
{
Version = "Only Ratings",
@@ -99,15 +111,11 @@ namespace osu.Game.Tests.Visual.SongSelect
OverallDifficulty = 6,
ApproachRate = 6,
},
- StarDifficulty = 4.8f,
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- },
+ StarDifficulty = 4.8f
}
});
- AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap
+ AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
BeatmapInfo =
{
@@ -127,13 +135,13 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 2.91f,
Metrics = new BeatmapMetrics
{
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
}
});
- AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap
+ AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
BeatmapInfo =
{
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
index acbbd4e18b..acf037198f 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
@@ -1,28 +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.ComponentModel;
using System.Linq;
+using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
namespace osu.Game.Tests.Visual.SongSelect
{
- [Description("PlaySongSelect beatmap details")]
+ [System.ComponentModel.Description("PlaySongSelect beatmap details")]
public class TestSceneBeatmapDetails : OsuTestScene
{
- public TestSceneBeatmapDetails()
+ private BeatmapDetails details;
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
{
- BeatmapDetails details;
- Add(details = new BeatmapDetails
+ Child = details = new BeatmapDetails
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(150),
- });
+ };
+ });
+ [Test]
+ public void TestAllMetrics()
+ {
AddStep("all metrics", () => details.Beatmap = new BeatmapInfo
{
+ BeatmapSet = new BeatmapSetInfo
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
+ },
Version = "All Metrics",
Metadata = new BeatmapMetadata
{
@@ -39,14 +49,21 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
});
+ }
+ [Test]
+ public void TestAllMetricsExceptSource()
+ {
AddStep("all except source", () => details.Beatmap = new BeatmapInfo
{
+ BeatmapSet = new BeatmapSetInfo
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
+ },
Version = "All Metrics",
Metadata = new BeatmapMetadata
{
@@ -62,14 +79,21 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
});
+ }
+ [Test]
+ public void TestOnlyRatings()
+ {
AddStep("ratings", () => details.Beatmap = new BeatmapInfo
{
+ BeatmapSet = new BeatmapSetInfo
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
+ },
Version = "Only Ratings",
Metadata = new BeatmapMetadata
{
@@ -84,12 +108,12 @@ namespace osu.Game.Tests.Visual.SongSelect
ApproachRate = 6,
},
StarDifficulty = 4.8f,
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 11),
- },
});
+ }
+ [Test]
+ public void TestOnlyFailsAndRetries()
+ {
AddStep("fails retries", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Retries and Fails",
@@ -108,11 +132,15 @@ namespace osu.Game.Tests.Visual.SongSelect
StarDifficulty = 2.91f,
Metrics = new BeatmapMetrics
{
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
});
+ }
+ [Test]
+ public void TestNoMetrics()
+ {
AddStep("no metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "No Metrics",
@@ -129,10 +157,22 @@ namespace osu.Game.Tests.Visual.SongSelect
ApproachRate = 6.5f,
},
StarDifficulty = 1.97f,
- Metrics = new BeatmapMetrics(),
});
+ }
+ [Test]
+ public void TestNullBeatmap()
+ {
AddStep("null beatmap", () => details.Beatmap = null);
}
+
+ [Test]
+ public void TestOnlineMetrics()
+ {
+ AddStep("online ratings/retries/fails", () => details.Beatmap = new BeatmapInfo
+ {
+ OnlineBeatmapID = 162,
+ });
+ }
}
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
index b1ed5c46c2..932e114580 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
@@ -7,7 +7,6 @@ using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
@@ -18,7 +17,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
-using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.SongSelect
@@ -49,7 +47,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("show", () =>
{
- infoWedge.State = Visibility.Visible;
+ infoWedge.Show();
infoWedge.Beatmap = Beatmap.Value;
});
@@ -58,11 +56,11 @@ namespace osu.Game.Tests.Visual.SongSelect
AddWaitStep("wait for select", 3);
- AddStep("hide", () => { infoWedge.State = Visibility.Hidden; });
+ AddStep("hide", () => { infoWedge.Hide(); });
AddWaitStep("wait for hide", 3);
- AddStep("show", () => { infoWedge.State = Visibility.Visible; });
+ AddStep("show", () => { infoWedge.Show(); });
foreach (var rulesetInfo in rulesets.AvailableRulesets)
{
@@ -136,7 +134,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () =>
{
infoBefore = infoWedge.Info;
- infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : new TestWorkingBeatmap(b);
+ infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b);
});
AddUntilStep("wait for async load", () => infoWedge.Info != infoBefore);
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs
index 3d75470328..157e572606 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs
@@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.SongSelect
},
new ScoreInfo
{
- Rank = ScoreRank.F,
+ Rank = ScoreRank.D,
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
@@ -206,7 +206,7 @@ namespace osu.Game.Tests.Visual.SongSelect
},
new ScoreInfo
{
- Rank = ScoreRank.F,
+ Rank = ScoreRank.D,
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
@@ -224,7 +224,7 @@ namespace osu.Game.Tests.Visual.SongSelect
},
new ScoreInfo
{
- Rank = ScoreRank.F,
+ Rank = ScoreRank.D,
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
@@ -270,9 +270,8 @@ namespace osu.Game.Tests.Visual.SongSelect
},
Metrics = new BeatmapMetrics
{
- Ratings = Enumerable.Range(0, 11),
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
};
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index 7e962dbc06..962e0fb362 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
@@ -17,6 +18,7 @@ using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
@@ -79,7 +81,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
[BackgroundDependencyLoader]
- private void load(GameHost host)
+ private void load(GameHost host, AudioManager audio)
{
factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase();
@@ -93,14 +95,17 @@ namespace osu.Game.Tests.Visual.SongSelect
usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory));
- Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, defaultBeatmap = Beatmap.Default));
+ Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
Beatmap.SetDefault();
}
[SetUp]
- public virtual void SetUp() =>
- Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); });
+ public virtual void SetUp() => Schedule(() =>
+ {
+ Ruleset.Value = new OsuRuleset().RulesetInfo;
+ manager?.Delete(manager.GetAllUsableBeatmapSets());
+ });
[Test]
public void TestDummy()
@@ -137,7 +142,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
createSongSelect();
changeRuleset(2);
- importForRuleset(0);
+ addRulesetImportStep(0);
AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null);
}
@@ -146,8 +151,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
createSongSelect();
changeRuleset(2);
- importForRuleset(2);
- importForRuleset(1);
+ addRulesetImportStep(2);
+ addRulesetImportStep(1);
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2);
changeRuleset(1);
@@ -184,7 +189,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("empty mods", () => !Mods.Value.Any());
void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++;
- void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex--;
+ void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex++;
}
[Test]
@@ -209,7 +214,21 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("start not requested", () => !startRequested);
}
- private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
+ [Test]
+ public void TestHideSetSelectsCorrectBeatmap()
+ {
+ int? previousID = null;
+ createSongSelect();
+ addRulesetImportStep(0);
+ AddStep("Move to last difficulty", () => songSelect.Carousel.SelectBeatmap(songSelect.Carousel.BeatmapSets.First().Beatmaps.Last()));
+ AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID);
+ AddStep("Hide first beatmap", () => manager.Hide(songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First()));
+ AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID);
+ }
+
+ private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id));
+
+ private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
private static int importId;
private int getImportId() => ++importId;
@@ -231,7 +250,7 @@ namespace osu.Game.Tests.Visual.SongSelect
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
for (int i = 0; i < 100; i += 10)
- manager.Import(createTestBeatmapSet(i, usableRulesets));
+ manager.Import(createTestBeatmapSet(i, usableRulesets)).Wait();
});
}
diff --git a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs
index 53ce25ebb3..a68fd0ef40 100644
--- a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs
+++ b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs
@@ -4,9 +4,9 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Screens;
using osu.Framework.Testing;
+using osu.Game.Graphics.Sprites;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osuTK.Graphics;
@@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load()
{
- AddInternal(new SpriteText
+ AddInternal(new OsuSpriteText
{
Text = screenText,
Colour = Color4.White,
diff --git a/osu.Game.Tests/Visual/Tournament/TestSceneDrawings.cs b/osu.Game.Tests/Visual/Tournament/TestSceneDrawings.cs
deleted file mode 100644
index 995819f7ae..0000000000
--- a/osu.Game.Tests/Visual/Tournament/TestSceneDrawings.cs
+++ /dev/null
@@ -1,85 +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.ComponentModel;
-using osu.Framework.Allocation;
-using osu.Game.Screens.Tournament;
-using osu.Game.Screens.Tournament.Teams;
-
-namespace osu.Game.Tests.Visual.Tournament
-{
- [Description("for tournament use")]
- public class TestSceneDrawings : ScreenTestScene
- {
- [BackgroundDependencyLoader]
- private void load()
- {
- LoadScreen(new Drawings
- {
- TeamList = new TestTeamList(),
- });
- }
-
- private class TestTeamList : ITeamList
- {
- public IEnumerable Teams { get; } = new[]
- {
- new DrawingsTeam
- {
- FlagName = "GB",
- FullName = "United Kingdom",
- Acronym = "UK"
- },
- new DrawingsTeam
- {
- FlagName = "FR",
- FullName = "France",
- Acronym = "FRA"
- },
- new DrawingsTeam
- {
- FlagName = "CN",
- FullName = "China",
- Acronym = "CHN"
- },
- new DrawingsTeam
- {
- FlagName = "AU",
- FullName = "Australia",
- Acronym = "AUS"
- },
- new DrawingsTeam
- {
- FlagName = "JP",
- FullName = "Japan",
- Acronym = "JPN"
- },
- new DrawingsTeam
- {
- FlagName = "RO",
- FullName = "Romania",
- Acronym = "ROM"
- },
- new DrawingsTeam
- {
- FlagName = "IT",
- FullName = "Italy",
- Acronym = "PIZZA"
- },
- new DrawingsTeam
- {
- FlagName = "VE",
- FullName = "Venezuela",
- Acronym = "VNZ"
- },
- new DrawingsTeam
- {
- FlagName = "US",
- FullName = "United States of America",
- Acronym = "USA"
- },
- };
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs b/osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs
new file mode 100644
index 0000000000..096ac951de
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/OsuGridTestScene.cs
@@ -0,0 +1,50 @@
+// 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.Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ ///
+ /// An abstract test case which exposes small cells arranged in a grid.
+ /// Useful for displaying multiple configurations of a tested component at a glance.
+ ///
+ public abstract class OsuGridTestScene : OsuTestScene
+ {
+ private readonly Drawable[,] cells;
+
+ ///
+ /// The amount of rows in the grid.
+ ///
+ protected readonly int Rows;
+
+ ///
+ /// The amount of columns in the grid.
+ ///
+ protected readonly int Cols;
+
+ ///
+ /// Constructs a grid test case with the given dimensions.
+ ///
+ protected OsuGridTestScene(int rows, int cols)
+ {
+ Rows = rows;
+ Cols = cols;
+
+ GridContainer testContainer;
+ Add(testContainer = new GridContainer { RelativeSizeAxes = Axes.Both });
+
+ cells = new Drawable[rows, cols];
+ for (int r = 0; r < rows; r++)
+ for (int c = 0; c < cols; c++)
+ cells[r, c] = new Container { RelativeSizeAxes = Axes.Both };
+
+ testContainer.Content = cells.ToJagged();
+ }
+
+ protected Container Cell(int index) => (Container)cells[index / Cols, index % Cols];
+ protected Container Cell(int row, int col) => (Container)cells[row, col];
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs
new file mode 100644
index 0000000000..867b3130c9
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.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 System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.UserInterface;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneBackButton : OsuTestScene
+ {
+ private readonly BackButton button;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(TwoLayerButton)
+ };
+
+ public TestSceneBackButton()
+ {
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(300),
+ Masking = true,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.SlateGray
+ },
+ button = new BackButton
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Action = () => button.Hide(),
+ }
+ }
+ };
+
+ AddStep("show button", () => button.Show());
+ AddStep("hide button", () => button.Hide());
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs
index 590ee4e720..23d9112b25 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs
@@ -84,7 +84,6 @@ namespace osu.Game.Tests.Visual.UserInterface
testLocalCursor();
testUserCursorOverride();
testMultipleLocalCursors();
- ReturnUserInput();
}
///
@@ -177,7 +176,7 @@ namespace osu.Game.Tests.Visual.UserInterface
/// Checks if a cursor is visible.
///
/// The cursor to check.
- private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State == Visibility.Visible;
+ private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State.Value == Visibility.Visible;
///
/// Checks if a cursor is at the current inputmanager screen position.
@@ -193,7 +192,7 @@ namespace osu.Game.Tests.Visual.UserInterface
public CursorContainer Cursor { get; }
public bool ProvidingUserCursor { get; }
- public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor;
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || (SmoothTransition && !ProvidingUserCursor);
private readonly Box background;
@@ -219,7 +218,7 @@ namespace osu.Game.Tests.Visual.UserInterface
},
Cursor = new TestCursorContainer
{
- State = providesUserCursor ? Visibility.Hidden : Visibility.Visible,
+ State = { Value = providesUserCursor ? Visibility.Hidden : Visibility.Visible },
}
};
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs
new file mode 100644
index 0000000000..f92aae43d2
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingBar.cs
@@ -0,0 +1,50 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.UserInterface;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneExpandingBar : OsuTestScene
+ {
+ public TestSceneExpandingBar()
+ {
+ Container container;
+ ExpandingBar expandingBar;
+
+ Add(container = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.Gray,
+ Alpha = 0.5f,
+ RelativeSizeAxes = Axes.Both,
+ },
+ expandingBar = new ExpandingBar
+ {
+ Anchor = Anchor.Centre,
+ ExpandedSize = 10,
+ CollapsedSize = 2,
+ Colour = Color4.DeepSkyBlue,
+ }
+ }
+ });
+
+ AddStep(@"Collapse", () => expandingBar.Collapse());
+ AddStep(@"Expand", () => expandingBar.Expand());
+ AddSliderStep(@"Resize container", 1, 300, 150, value => container.ResizeTo(value));
+ AddStep(@"Horizontal", () => expandingBar.RelativeSizeAxes = Axes.X);
+ AddStep(@"Anchor top", () => expandingBar.Anchor = Anchor.TopCentre);
+ AddStep(@"Vertical", () => expandingBar.RelativeSizeAxes = Axes.Y);
+ AddStep(@"Anchor left", () => expandingBar.Anchor = Anchor.CentreLeft);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs
index 0c9ce50288..c80b3e6297 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs
@@ -26,8 +26,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
new NamedIconButton("No change", new IconButton()),
new NamedIconButton("Background colours", new ColouredIconButton()),
- new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }),
- new NamedIconButton("Unchanging size", new IconButton(), false),
+ new NamedIconButton("Full-width", new IconButton { Size = new Vector2(200, 30) }),
new NamedIconButton("Icon colours", new IconButton
{
IconColour = Color4.Green,
@@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private class NamedIconButton : Container
{
- public NamedIconButton(string name, IconButton button, bool allowSizeChange = true)
+ public NamedIconButton(string name, IconButton button)
{
AutoSizeAxes = Axes.Y;
Width = 200;
@@ -101,13 +100,7 @@ namespace osu.Game.Tests.Visual.UserInterface
}
};
- if (allowSizeChange)
- iconContainer.AutoSizeAxes = Axes.Both;
- else
- {
- iconContainer.RelativeSizeAxes = Axes.X;
- iconContainer.Height = 30;
- }
+ iconContainer.AutoSizeAxes = Axes.Both;
button.Anchor = Anchor.Centre;
button.Origin = Anchor.Centre;
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingAnimation.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingAnimation.cs
index b9a6d74f19..b0233d35f9 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingAnimation.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingAnimation.cs
@@ -3,13 +3,12 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
- public class TestSceneLoadingAnimation : GridTestScene //todo: this should be an OsuTestScene
+ public class TestSceneLoadingAnimation : OsuGridTestScene
{
public TestSceneLoadingAnimation()
: base(2, 2)
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
similarity index 90%
rename from osu.Game.Tests/Visual/UserInterface/TestSceneMods.cs
rename to osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index 2e36ba39ed..80408ab43b 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneMods.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -24,11 +24,10 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
[Description("mod select and icon display")]
- public class TestSceneMods : OsuTestScene
+ public class TestSceneModSelectOverlay : OsuTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(ModSelectOverlay),
typeof(ModDisplay),
typeof(ModSection),
typeof(ModIcon),
@@ -77,7 +76,7 @@ namespace osu.Game.Tests.Visual.UserInterface
public void TestOsuMods()
{
var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 0);
- AddStep("change ruleset", () => { Ruleset.Value = ruleset; });
+ changeRuleset(ruleset);
var instance = ruleset.CreateInstance();
@@ -109,7 +108,7 @@ namespace osu.Game.Tests.Visual.UserInterface
public void TestManiaMods()
{
var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 3);
- AddStep("change ruleset", () => { Ruleset.Value = ruleset; });
+ changeRuleset(ruleset);
testRankedText(ruleset.CreateInstance().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
}
@@ -120,7 +119,7 @@ namespace osu.Game.Tests.Visual.UserInterface
var rulesetOsu = rulesets.AvailableRulesets.First(r => r.ID == 0);
var rulesetMania = rulesets.AvailableRulesets.First(r => r.ID == 3);
- AddStep("change ruleset to null", () => { Ruleset.Value = null; });
+ changeRuleset(null);
var instance = rulesetOsu.CreateInstance();
var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
@@ -128,15 +127,15 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("set mods externally", () => { modDisplay.Current.Value = new[] { noFailMod }; });
- AddStep("change ruleset to osu", () => { Ruleset.Value = rulesetOsu; });
+ changeRuleset(rulesetOsu);
AddAssert("ensure mods still selected", () => modDisplay.Current.Value.Single(m => m is OsuModNoFail) != null);
- AddStep("change ruleset to mania", () => { Ruleset.Value = rulesetMania; });
+ changeRuleset(rulesetMania);
AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any(m => m is OsuModNoFail));
- AddStep("change ruleset to osu", () => { Ruleset.Value = rulesetOsu; });
+ changeRuleset(rulesetOsu);
AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any());
}
@@ -217,14 +216,11 @@ namespace osu.Game.Tests.Visual.UserInterface
private void testRankedText(Mod mod)
{
- AddWaitStep("wait for fade", 1);
- AddAssert("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0);
+ AddUntilStep("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0);
selectNext(mod);
- AddWaitStep("wait for fade", 1);
- AddAssert("check for unranked", () => modSelect.UnrankedLabel.Alpha != 0);
+ AddUntilStep("check for unranked", () => modSelect.UnrankedLabel.Alpha != 0);
selectPrevious(mod);
- AddWaitStep("wait for fade", 1);
- AddAssert("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0);
+ AddUntilStep("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0);
}
private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1));
@@ -240,6 +236,15 @@ namespace osu.Game.Tests.Visual.UserInterface
});
}
+ private void changeRuleset(RulesetInfo ruleset)
+ {
+ AddStep($"change ruleset to {ruleset}", () => { Ruleset.Value = ruleset; });
+ waitForLoad();
+ }
+
+ private void waitForLoad() =>
+ AddUntilStep("wait for icons to load", () => modSelect.AllLoaded);
+
private void checkNotSelected(Mod mod)
{
AddAssert($"check {mod.Name} is not selected", () =>
@@ -255,6 +260,8 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public new Bindable> SelectedMods => base.SelectedMods;
+ public bool AllLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded);
+
public ModButton GetModButton(Mod mod)
{
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs
index a62fd6467b..ab2ca47100 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs
@@ -3,7 +3,6 @@
using NUnit.Framework;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Overlays;
@@ -23,9 +22,9 @@ namespace osu.Game.Tests.Visual.UserInterface
};
Add(mc);
- AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
- AddStep(@"show", () => mc.State = Visibility.Visible);
+ AddStep(@"show", () => mc.Show());
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
+ AddStep(@"show", () => mc.Hide());
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
index 0cb7c2484d..d8a4514df1 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
+using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
@@ -17,9 +18,6 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneNotificationOverlay : OsuTestScene
{
- private readonly NotificationOverlay manager;
- private readonly List progressingNotifications = new List();
-
public override IReadOnlyList RequiredTypes => new[]
{
typeof(NotificationSection),
@@ -30,25 +28,33 @@ namespace osu.Game.Tests.Visual.UserInterface
typeof(Notification)
};
- public TestSceneNotificationOverlay()
+ private NotificationOverlay notificationOverlay;
+
+ private readonly List progressingNotifications = new List();
+
+ private SpriteText displayedCount;
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
progressingNotifications.Clear();
- Content.Add(manager = new NotificationOverlay
+ Content.Children = new Drawable[]
{
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight
- });
+ notificationOverlay = new NotificationOverlay
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight
+ },
+ displayedCount = new OsuSpriteText()
+ };
- SpriteText displayedCount = new SpriteText();
-
- Content.Add(displayedCount);
-
- void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state);
- void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected);
-
- manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; };
+ notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; };
+ });
+ [Test]
+ public void TestBasicFlow()
+ {
setState(Visibility.Visible);
AddStep(@"simple #1", sendHelloNotification);
AddStep(@"simple #2", sendAmazingNotification);
@@ -60,6 +66,7 @@ namespace osu.Game.Tests.Visual.UserInterface
setState(Visibility.Hidden);
AddRepeatStep(@"add many simple", sendManyNotifications, 3);
+
AddWaitStep("wait some", 5);
checkProgressingCount(0);
@@ -68,18 +75,122 @@ namespace osu.Game.Tests.Visual.UserInterface
checkProgressingCount(1);
- AddAssert("Displayed count is 33", () => manager.UnreadCount.Value == 33);
+ checkDisplayedCount(33);
AddWaitStep("wait some", 10);
checkProgressingCount(0);
-
- setState(Visibility.Visible);
-
- //AddStep(@"barrage", () => sendBarrage());
}
- private void sendBarrage(int remaining = 10)
+ [Test]
+ public void TestImportantWhileClosed()
+ {
+ AddStep(@"simple #1", sendHelloNotification);
+
+ AddAssert("Is visible", () => notificationOverlay.State.Value == Visibility.Visible);
+
+ checkDisplayedCount(1);
+
+ AddStep(@"progress #1", sendUploadProgress);
+ AddStep(@"progress #2", sendDownloadProgress);
+
+ checkProgressingCount(2);
+ checkDisplayedCount(3);
+ }
+
+ [Test]
+ public void TestUnimportantWhileClosed()
+ {
+ AddStep(@"background #1", sendBackgroundNotification);
+
+ AddAssert("Is not visible", () => notificationOverlay.State.Value == Visibility.Hidden);
+
+ checkDisplayedCount(1);
+
+ AddStep(@"background progress #1", sendBackgroundUploadProgress);
+
+ AddWaitStep("wait some", 5);
+
+ checkProgressingCount(0);
+
+ checkDisplayedCount(2);
+
+ AddStep(@"simple #1", sendHelloNotification);
+
+ checkDisplayedCount(3);
+ }
+
+ [Test]
+ public void TestSpam()
+ {
+ setState(Visibility.Visible);
+ AddRepeatStep("send barrage", sendBarrage, 10);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed);
+
+ if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3)
+ {
+ var p = progressingNotifications.Find(n => n.State == ProgressNotificationState.Queued);
+
+ if (p != null)
+ p.State = ProgressNotificationState.Active;
+ }
+
+ foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active))
+ {
+ if (n.Progress < 1)
+ n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle();
+ else
+ n.State = ProgressNotificationState.Completed;
+ }
+ }
+
+ private void checkDisplayedCount(int expected) =>
+ AddAssert($"Displayed count is {expected}", () => notificationOverlay.UnreadCount.Value == expected);
+
+ private void sendDownloadProgress()
+ {
+ var n = new ProgressNotification
+ {
+ Text = @"Downloading Haitai...",
+ CompletionText = "Downloaded Haitai!",
+ };
+ notificationOverlay.Post(n);
+ progressingNotifications.Add(n);
+ }
+
+ private void sendUploadProgress()
+ {
+ var n = new ProgressNotification
+ {
+ Text = @"Uploading to BSS...",
+ CompletionText = "Uploaded to BSS!",
+ };
+ notificationOverlay.Post(n);
+ progressingNotifications.Add(n);
+ }
+
+ private void sendBackgroundUploadProgress()
+ {
+ var n = new BackgroundProgressNotification
+ {
+ Text = @"Uploading to BSS...",
+ CompletionText = "Uploaded to BSS!",
+ };
+ notificationOverlay.Post(n);
+ progressingNotifications.Add(n);
+ }
+
+ private void setState(Visibility state) => AddStep(state.ToString(), () => notificationOverlay.State.Value = state);
+
+ private void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected);
+
+ private void sendBarrage()
{
switch (RNG.Next(0, 4))
{
@@ -99,69 +210,37 @@ namespace osu.Game.Tests.Visual.UserInterface
sendDownloadProgress();
break;
}
-
- if (remaining > 0)
- Scheduler.AddDelayed(() => sendBarrage(remaining - 1), 80);
- }
-
- protected override void Update()
- {
- base.Update();
-
- progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed);
-
- if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3)
- {
- var p = progressingNotifications.Find(n => n.State == ProgressNotificationState.Queued);
- if (p != null)
- p.State = ProgressNotificationState.Active;
- }
-
- foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active))
- {
- if (n.Progress < 1)
- n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle();
- else
- n.State = ProgressNotificationState.Completed;
- }
- }
-
- private void sendDownloadProgress()
- {
- var n = new ProgressNotification
- {
- Text = @"Downloading Haitai...",
- CompletionText = "Downloaded Haitai!",
- };
- manager.Post(n);
- progressingNotifications.Add(n);
- }
-
- private void sendUploadProgress()
- {
- var n = new ProgressNotification
- {
- Text = @"Uploading to BSS...",
- CompletionText = "Uploaded to BSS!",
- };
- manager.Post(n);
- progressingNotifications.Add(n);
}
private void sendAmazingNotification()
{
- manager.Post(new SimpleNotification { Text = @"You are amazing" });
+ notificationOverlay.Post(new SimpleNotification { Text = @"You are amazing" });
}
private void sendHelloNotification()
{
- manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" });
+ notificationOverlay.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" });
+ }
+
+ private void sendBackgroundNotification()
+ {
+ notificationOverlay.Post(new BackgroundNotification { Text = @"Welcome to osu!. Enjoy your stay!" });
}
private void sendManyNotifications()
{
for (int i = 0; i < 10; i++)
- manager.Post(new SimpleNotification { Text = @"Spam incoming!!" });
+ notificationOverlay.Post(new SimpleNotification { Text = @"Spam incoming!!" });
+ }
+
+ private class BackgroundNotification : SimpleNotification
+ {
+ public override bool IsImportant => false;
+ }
+
+ private class BackgroundProgressNotification : ProgressNotification
+ {
+ public override bool IsImportant => false;
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.cs
new file mode 100644
index 0000000000..f73450db60
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.cs
@@ -0,0 +1,55 @@
+// 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.Collections.Generic;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics.UserInterface;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ [TestFixture]
+ public class TestSceneNumberBox : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(OsuNumberBox),
+ };
+
+ private OsuNumberBox numberBox;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ Padding = new MarginPadding { Horizontal = 250 },
+ Child = numberBox = new OsuNumberBox
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ PlaceholderText = "Insert numbers here"
+ }
+ };
+
+ clearInput();
+ AddStep("enter numbers", () => numberBox.Text = "987654321");
+ expectedValue("987654321");
+ clearInput();
+ AddStep("enter text + single number", () => numberBox.Text = "1 hello 2 world 3");
+ expectedValue("123");
+ clearInput();
+ }
+
+ private void clearInput() => AddStep("clear input", () => numberBox.Text = null);
+
+ private void expectedValue(string value) => AddAssert("expect number", () => numberBox.Text == value);
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.cs
new file mode 100644
index 0000000000..6a41d08f01
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuAnimatedButton.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 System;
+using osu.Framework.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneOsuAnimatedButton : OsuGridTestScene
+ {
+ public TestSceneOsuAnimatedButton()
+ : base(3, 2)
+ {
+ Cell(0).Add(new BaseContainer("relative sized")
+ {
+ RelativeSizeAxes = Axes.Both,
+ });
+
+ Cell(1).Add(new BaseContainer("auto sized")
+ {
+ AutoSizeAxes = Axes.Both
+ });
+
+ Cell(2).Add(new BaseContainer("relative Y auto X")
+ {
+ RelativeSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.X
+ });
+
+ Cell(3).Add(new BaseContainer("relative X auto Y")
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y
+ });
+
+ Cell(4).Add(new BaseContainer("fixed")
+ {
+ Size = new Vector2(100),
+ });
+
+ Cell(5).Add(new BaseContainer("fixed")
+ {
+ Size = new Vector2(100, 50),
+ });
+
+ AddToggleStep("toggle enabled", toggle =>
+ {
+ for (int i = 0; i < 6; i++)
+ ((BaseContainer)Cell(i).Child).Action = toggle ? () => { } : (Action)null;
+ });
+ }
+
+ public class BaseContainer : OsuAnimatedButton
+ {
+ public BaseContainer(string text)
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+
+ Add(new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = text
+ });
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs
new file mode 100644
index 0000000000..dbef7d1686
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs
@@ -0,0 +1,194 @@
+// 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.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.Containers;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ [TestFixture]
+ public class TestSceneOsuHoverContainer : ManualInputManagerTestScene
+ {
+ private OsuHoverTestContainer hoverContainer;
+ private Box colourContainer;
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
+ {
+ Child = hoverContainer = new OsuHoverTestContainer
+ {
+ Enabled = { Value = true },
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(100),
+ Child = colourContainer = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+
+ doMoveOut();
+ });
+
+ [Description("Checks IsHovered property value on a container when it is hovered/unhovered.")]
+ [TestCase(true, TestName = "Enabled_Check_IsHovered")]
+ [TestCase(false, TestName = "Disabled_Check_IsHovered")]
+ public void TestIsHoveredHasProperValue(bool isEnabled)
+ {
+ setContainerEnabledTo(isEnabled);
+
+ checkNotHovered();
+
+ moveToText();
+ checkHovered();
+
+ moveOut();
+ checkNotHovered();
+
+ moveToText();
+ checkHovered();
+
+ moveOut();
+ checkNotHovered();
+ }
+
+ [Test]
+ [Description("Checks colour fading on an enabled container when it is hovered/unhovered.")]
+ public void TestTransitionWhileEnabled()
+ {
+ enableContainer();
+
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveToText();
+ waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
+
+ moveOut();
+ waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveToText();
+ waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
+
+ moveOut();
+ waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
+ }
+
+ [Test]
+ [Description("Checks colour fading on a disabled container when it is hovered/unhovered.")]
+ public void TestNoTransitionWhileDisabled()
+ {
+ disableContainer();
+
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveToText();
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveOut();
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveToText();
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveOut();
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+ }
+
+ [Test]
+ [Description("Checks that when a disabled & hovered container gets enabled, colour fading happens")]
+ public void TestBecomesEnabledTransition()
+ {
+ disableContainer();
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveToText();
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ enableContainer();
+ waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
+ }
+
+ [Test]
+ [Description("Checks that when an enabled & hovered container gets disabled, colour fading happens")]
+ public void TestBecomesDisabledTransition()
+ {
+ enableContainer();
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveToText();
+ waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
+
+ disableContainer();
+ waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
+ }
+
+ [Test]
+ [Description("Checks that when a hovered container gets enabled and disabled multiple times, colour fading happens")]
+ public void TestDisabledChangesMultipleTimes()
+ {
+ enableContainer();
+ checkColour(OsuHoverTestContainer.IDLE_COLOUR);
+
+ moveToText();
+ waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
+
+ disableContainer();
+ waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
+
+ enableContainer();
+ waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
+
+ disableContainer();
+ waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
+ }
+
+ private void enableContainer() => setContainerEnabledTo(true);
+
+ private void disableContainer() => setContainerEnabledTo(false);
+
+ private void setContainerEnabledTo(bool newValue)
+ {
+ string word = newValue ? "Enable" : "Disable";
+ AddStep($"{word} container", () => hoverContainer.Enabled.Value = newValue);
+ }
+
+ private void moveToText() => AddStep("Move mouse to text", () => InputManager.MoveMouseTo(hoverContainer));
+
+ private void moveOut() => AddStep("Move out", doMoveOut);
+
+ private void checkHovered() => AddAssert("Check hovered", () => hoverContainer.IsHovered);
+
+ private void checkNotHovered() => AddAssert("Check not hovered", () => !hoverContainer.IsHovered);
+
+ private void checkColour(ColourInfo expectedColour)
+ => AddAssert($"Check colour to be '{expectedColour}'", () => currentColour.Equals(expectedColour));
+
+ private void waitUntilColourIs(ColourInfo expectedColour)
+ => AddUntilStep($"Wait until hover colour is {expectedColour}", () => currentColour.Equals(expectedColour));
+
+ private ColourInfo currentColour => colourContainer.DrawColourInfo.Colour;
+
+ ///
+ /// Moves the cursor to top left corner of the screen
+ ///
+ private void doMoveOut()
+ => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.TopLeft.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y));
+
+ private sealed class OsuHoverTestContainer : OsuHoverContainer
+ {
+ public static readonly Color4 HOVER_COLOUR = Color4.Red;
+ public static readonly Color4 IDLE_COLOUR = Color4.Green;
+
+ public OsuHoverTestContainer()
+ {
+ HoverColour = HOVER_COLOUR;
+ IdleColour = IDLE_COLOUR;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs
index 2c2a28394c..061039b297 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osuTK;
using osuTK.Graphics;
@@ -29,7 +30,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Colour = Color4.Teal,
RelativeSizeAxes = Axes.Both,
},
- new ScrollContainer
+ new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = flow = new FillFlowContainer
diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
index 24140125e0..9ddd8f4038 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.UserInterface
var popup = new PopupDialog
{
RelativeSizeAxes = Axes.Both,
- State = Framework.Graphics.Containers.Visibility.Visible,
+ State = { Value = Framework.Graphics.Containers.Visibility.Visible },
Icon = FontAwesome.Solid.AssistiveListeningSystems,
HeaderText = @"This is a test popup",
BodyText = "I can say lots of stuff and even wrap my words!",
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
new file mode 100644
index 0000000000..0da256855a
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
@@ -0,0 +1,79 @@
+// 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.Graphics.Containers;
+using osu.Game.Overlays.Toolbar;
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.MathUtils;
+using osu.Game.Rulesets;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneToolbarRulesetSelector : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ToolbarRulesetSelector),
+ typeof(ToolbarRulesetTabButton),
+ };
+
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
+ [Test]
+ public void TestDisplay()
+ {
+ ToolbarRulesetSelector selector = null;
+
+ AddStep("create selector", () =>
+ {
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.X,
+ Height = Toolbar.HEIGHT,
+ Child = selector = new ToolbarRulesetSelector()
+ };
+ });
+
+ AddStep("Select random", () =>
+ {
+ selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count()));
+ });
+ AddStep("Toggle disabled state", () => selector.Current.Disabled = !selector.Current.Disabled);
+ }
+
+ [Test]
+ public void TestNonFirstRulesetInitialState()
+ {
+ TestSelector selector = null;
+
+ AddStep("create selector", () =>
+ {
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.X,
+ Height = Toolbar.HEIGHT,
+ Child = selector = new TestSelector()
+ };
+
+ selector.Current.Value = rulesets.GetRuleset(2);
+ });
+
+ AddAssert("mode line has moved", () => selector.ModeButtonLine.DrawPosition.X > 0);
+ }
+
+ private class TestSelector : ToolbarRulesetSelector
+ {
+ public new Drawable ModeButtonLine => base.ModeButtonLine;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneTwoLayerButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneTwoLayerButton.cs
index b9ed1a71cc..849577186d 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneTwoLayerButton.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneTwoLayerButton.cs
@@ -1,17 +1,26 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.ComponentModel;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
+using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
- [Description("mostly back button")]
public class TestSceneTwoLayerButton : OsuTestScene
{
public TestSceneTwoLayerButton()
{
- Add(new BackButton());
+ Add(new TwoLayerButton
+ {
+ Position = new Vector2(100),
+ Text = "button",
+ Icon = FontAwesome.Solid.Check,
+ BackgroundColour = Color4.SlateGray,
+ HoverColour = Color4.SlateGray.Darken(0.2f)
+ });
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
index 23065629a6..9cdfcb6cc4 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
+using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
@@ -32,7 +33,7 @@ namespace osu.Game.Tests.Visual.UserInterface
this.api = api;
this.rulesets = rulesets;
- testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu);
+ testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result;
}
[Test]
@@ -41,7 +42,7 @@ namespace osu.Game.Tests.Visual.UserInterface
TestUpdateableBeatmapBackgroundSprite background = null;
AddStep("load null beatmap", () => Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both });
- AddUntilStep("wait for load", () => background.ContentLoaded);
+ AddUntilStep("content loaded", () => background.ContentLoaded);
}
[Test]
@@ -92,13 +93,13 @@ namespace osu.Game.Tests.Visual.UserInterface
public void TestUnloadAndReload()
{
var backgrounds = new List();
- ScrollContainer scrollContainer = null;
+ OsuScrollContainer scrollContainer = null;
AddStep("create backgrounds hierarchy", () =>
{
FillFlowContainer backgroundFlow;
- Child = scrollContainer = new ScrollContainer
+ Child = scrollContainer = new OsuScrollContainer
{
Size = new Vector2(500),
Child = backgroundFlow = new FillFlowContainer
diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs
index f66b374cd7..3e0df8d45e 100644
--- a/osu.Game.Tests/WaveformTestBeatmap.cs
+++ b/osu.Game.Tests/WaveformTestBeatmap.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
+using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
@@ -19,30 +20,34 @@ namespace osu.Game.Tests
{
private readonly ZipArchiveReader reader;
private readonly Stream stream;
+ private readonly ITrackStore trackStore;
- public WaveformTestBeatmap()
- : base(new BeatmapInfo())
+ public WaveformTestBeatmap(AudioManager audioManager)
+ : base(new BeatmapInfo(), audioManager)
{
stream = TestResources.GetTestBeatmapStream();
reader = new ZipArchiveReader(stream);
+ trackStore = audioManager.GetTrackStore(reader);
}
- public override void Dispose()
+ protected override void Dispose(bool isDisposing)
{
- base.Dispose();
+ base.Dispose(isDisposing);
stream?.Dispose();
reader?.Dispose();
+ trackStore?.Dispose();
}
protected override IBeatmap GetBeatmap() => createTestBeatmap();
protected override Texture GetBackground() => null;
- protected override Waveform GetWaveform() => new Waveform(getAudioStream());
+ protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile));
- protected override Track GetTrack() => new TrackBass(getAudioStream());
+ protected override Track GetTrack() => trackStore.Get(firstAudioFile);
+
+ private string firstAudioFile => reader.Filenames.First(f => f.EndsWith(".mp3"));
- private Stream getAudioStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3")));
private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu")));
private Beatmap createTestBeatmap()
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 938e1ae0f8..11d70ee7be 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -3,8 +3,8 @@
-
-
+
+
diff --git a/osu.Game.Tournament.Tests/.vscode/launch.json b/osu.Game.Tournament.Tests/.vscode/launch.json
new file mode 100644
index 0000000000..0204158347
--- /dev/null
+++ b/osu.Game.Tournament.Tests/.vscode/launch.json
@@ -0,0 +1,31 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "VisualTests (Debug)",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "dotnet",
+ "args": [
+ "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Tournament.Tests.dll"
+ ],
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "Build (Debug)",
+ "env": {},
+ "console": "internalConsole"
+ },
+ {
+ "name": "VisualTests (Release)",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "dotnet",
+ "args": [
+ "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Tournament.Tests.dll"
+ ],
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "Build (Release)",
+ "env": {},
+ "console": "internalConsole"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/osu.Game.Tournament.Tests/.vscode/tasks.json b/osu.Game.Tournament.Tests/.vscode/tasks.json
new file mode 100644
index 0000000000..37f2f32874
--- /dev/null
+++ b/osu.Game.Tournament.Tests/.vscode/tasks.json
@@ -0,0 +1,47 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build (Debug)",
+ "type": "shell",
+ "command": "dotnet",
+ "args": [
+ "build",
+ "--no-restore",
+ "osu.Game.Tournament.Tests.csproj",
+ "/p:GenerateFullPaths=true",
+ "/m",
+ "/verbosity:m"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Build (Release)",
+ "type": "shell",
+ "command": "dotnet",
+ "args": [
+ "build",
+ "--no-restore",
+ "osu.Game.Tournament.Tests.csproj",
+ "/p:Configuration=Release",
+ "/p:GenerateFullPaths=true",
+ "/m",
+ "/verbosity:m"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Restore",
+ "type": "shell",
+ "command": "dotnet",
+ "args": [
+ "restore"
+ ],
+ "problemMatcher": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs
new file mode 100644
index 0000000000..f329623703
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Components/TestSceneDrawableTournamentMatch.cs
@@ -0,0 +1,93 @@
+// 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.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Tests.Visual;
+using osu.Game.Tournament.Components;
+using osu.Game.Tournament.Models;
+using osu.Game.Tournament.Screens.Ladder.Components;
+
+namespace osu.Game.Tournament.Tests.Components
+{
+ public class TestSceneDrawableTournamentMatch : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(TournamentMatch),
+ typeof(DrawableTournamentTeam),
+ };
+
+ public TestSceneDrawableTournamentMatch()
+ {
+ Container level1;
+ Container level2;
+
+ var match1 = new TournamentMatch(
+ new TournamentTeam { FlagName = { Value = "AU" }, FullName = { Value = "Australia" }, },
+ new TournamentTeam { FlagName = { Value = "JP" }, FullName = { Value = "Japan" }, Acronym = { Value = "JPN" } })
+ {
+ Team1Score = { Value = 4 },
+ Team2Score = { Value = 1 },
+ };
+
+ var match2 = new TournamentMatch(
+ new TournamentTeam
+ {
+ FlagName = { Value = "RO" },
+ FullName = { Value = "Romania" },
+ }
+ );
+
+ Child = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ level1 = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Children = new[]
+ {
+ new DrawableTournamentMatch(match1),
+ new DrawableTournamentMatch(match2),
+ new DrawableTournamentMatch(new TournamentMatch()),
+ }
+ },
+ level2 = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Margin = new MarginPadding(20),
+ Children = new[]
+ {
+ new DrawableTournamentMatch(new TournamentMatch()),
+ new DrawableTournamentMatch(new TournamentMatch())
+ }
+ }
+ }
+ };
+
+ level1.Children[0].Match.Progression.Value = level2.Children[0].Match;
+ level1.Children[1].Match.Progression.Value = level2.Children[0].Match;
+
+ AddRepeatStep("change scores", () => match1.Team2Score.Value++, 4);
+ AddStep("add new team", () => match2.Team2.Value = new TournamentTeam { FlagName = { Value = "PT" }, FullName = { Value = "Portugal" } });
+ AddStep("Add progression", () => level1.Children[2].Match.Progression.Value = level2.Children[1].Match);
+
+ AddStep("start match", () => match2.StartMatch());
+
+ AddRepeatStep("change scores", () => match2.Team1Score.Value++, 10);
+
+ AddStep("start submatch", () => level2.Children[0].Match.StartMatch());
+
+ AddRepeatStep("change scores", () => level2.Children[0].Match.Team1Score.Value++, 5);
+
+ AddRepeatStep("change scores", () => level2.Children[0].Match.Team2Score.Value++, 4);
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs
new file mode 100644
index 0000000000..72d9eb0e07
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs
@@ -0,0 +1,40 @@
+// 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.Framework.MathUtils;
+using osu.Game.Tournament.IPC;
+using osu.Game.Tournament.Screens.Gameplay.Components;
+
+namespace osu.Game.Tournament.Tests.Components
+{
+ public class TestSceneMatchScoreDisplay : LadderTestScene
+ {
+ [Cached(Type = typeof(MatchIPCInfo))]
+ private MatchIPCInfo matchInfo = new MatchIPCInfo();
+
+ public TestSceneMatchScoreDisplay()
+ {
+ Add(new MatchScoreDisplay
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Scheduler.AddDelayed(() =>
+ {
+ int amount = (int)((RNG.NextDouble() - 0.5) * 10000);
+ if (amount < 0)
+ matchInfo.Score1.Value -= amount;
+ else
+ matchInfo.Score2.Value += amount;
+ }, 100, true);
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs
new file mode 100644
index 0000000000..77fa411058
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs
@@ -0,0 +1,42 @@
+// 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.Beatmaps;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Rulesets;
+using osu.Game.Tests.Visual;
+using osu.Game.Tournament.Components;
+
+namespace osu.Game.Tournament.Tests.Components
+{
+ public class TestSceneTournamentBeatmapPanel : OsuTestScene
+ {
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 1091460 });
+ req.Success += success;
+ api.Queue(req);
+ }
+
+ private void success(APIBeatmap apiBeatmap)
+ {
+ var beatmap = apiBeatmap.ToBeatmap(rulesets);
+ Add(new TournamentBeatmapPanel(beatmap)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs
new file mode 100644
index 0000000000..41d32d9448
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs
@@ -0,0 +1,130 @@
+// 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.Framework.Graphics;
+using osu.Game.Online.Chat;
+using osu.Game.Tests.Visual;
+using osu.Game.Tournament.Components;
+using osu.Game.Tournament.IPC;
+using osu.Game.Tournament.Models;
+using osu.Game.Users;
+
+namespace osu.Game.Tournament.Tests.Components
+{
+ public class TestSceneTournamentMatchChatDisplay : OsuTestScene
+ {
+ private readonly Channel testChannel = new Channel();
+ private readonly Channel testChannel2 = new Channel();
+
+ private readonly User admin = new User
+ {
+ Username = "HappyStick",
+ Id = 2,
+ Colour = "f2ca34"
+ };
+
+ private readonly User redUser = new User
+ {
+ Username = "BanchoBot",
+ Id = 3,
+ };
+
+ private readonly User blueUser = new User
+ {
+ Username = "Zallius",
+ Id = 4,
+ };
+
+ [Cached]
+ private LadderInfo ladderInfo = new LadderInfo();
+
+ [Cached]
+ private MatchIPCInfo matchInfo = new MatchIPCInfo(); // hide parent
+
+ private readonly TournamentMatchChatDisplay chatDisplay;
+
+ public TestSceneTournamentMatchChatDisplay()
+ {
+ Add(chatDisplay = new TournamentMatchChatDisplay
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ });
+
+ ladderInfo.CurrentMatch.Value = new TournamentMatch
+ {
+ Team1 =
+ {
+ Value = new TournamentTeam { Players = new BindableList { redUser } }
+ },
+ Team2 =
+ {
+ Value = new TournamentTeam { Players = new BindableList { blueUser } }
+ }
+ };
+
+ chatDisplay.Channel.Value = testChannel;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ AddStep("message from admin", () => testChannel.AddNewMessages(new Message(nextMessageId())
+ {
+ Sender = admin,
+ Content = "I am a wang!"
+ }));
+
+ AddStep("message from team red", () => testChannel.AddNewMessages(new Message(nextMessageId())
+ {
+ Sender = redUser,
+ Content = "I am team red."
+ }));
+
+ AddStep("message from team red", () => testChannel.AddNewMessages(new Message(nextMessageId())
+ {
+ Sender = redUser,
+ Content = "I plan to win!"
+ }));
+
+ AddStep("message from team blue", () => testChannel.AddNewMessages(new Message(nextMessageId())
+ {
+ Sender = blueUser,
+ Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand."
+ }));
+
+ AddStep("message from admin", () => testChannel.AddNewMessages(new Message(nextMessageId())
+ {
+ Sender = admin,
+ Content = "Okay okay, calm down guys. Let's do this!"
+ }));
+
+ AddStep("multiple messages", () => testChannel.AddNewMessages(new Message(nextMessageId())
+ {
+ Sender = admin,
+ Content = "I spam you!"
+ },
+ new Message(nextMessageId())
+ {
+ Sender = admin,
+ Content = "I spam you!!!1"
+ },
+ new Message(nextMessageId())
+ {
+ Sender = admin,
+ Content = "I spam you!1!1"
+ }));
+
+ AddStep("change channel to 2", () => chatDisplay.Channel.Value = testChannel2);
+
+ AddStep("change channel to 1", () => chatDisplay.Channel.Value = testChannel);
+ }
+
+ private int messageId;
+
+ private long? nextMessageId() => messageId++;
+ }
+}
diff --git a/osu.Game.Tournament.Tests/LadderTestScene.cs b/osu.Game.Tournament.Tests/LadderTestScene.cs
new file mode 100644
index 0000000000..b49341d0d1
--- /dev/null
+++ b/osu.Game.Tournament.Tests/LadderTestScene.cs
@@ -0,0 +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.Tests.Visual;
+using osu.Game.Tournament.Models;
+
+namespace osu.Game.Tournament.Tests
+{
+ public abstract class LadderTestScene : OsuTestScene
+ {
+ [Resolved]
+ protected LadderInfo Ladder { get; private set; }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs
new file mode 100644
index 0000000000..201736f38a
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs
@@ -0,0 +1,23 @@
+// 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.Tests.Visual;
+using osu.Game.Tournament.Components;
+using osu.Game.Tournament.Screens.Gameplay;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneGameplayScreen : OsuTestScene
+ {
+ [Cached]
+ private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay();
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Add(new GameplayScreen());
+ Add(chat);
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneLadderEditorScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneLadderEditorScreen.cs
new file mode 100644
index 0000000000..a45c5de2bd
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneLadderEditorScreen.cs
@@ -0,0 +1,23 @@
+// 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.Cursor;
+using osu.Game.Tournament.Screens.Editors;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneLadderEditorScreen : LadderTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Add(new OsuContextMenuContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new LadderEditorScreen()
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneLadderScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneLadderScreen.cs
new file mode 100644
index 0000000000..2be0564c82
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneLadderScreen.cs
@@ -0,0 +1,23 @@
+// 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.Cursor;
+using osu.Game.Tournament.Screens.Ladder;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneLadderScreen : LadderTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Add(new OsuContextMenuContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new LadderScreen()
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs
new file mode 100644
index 0000000000..a7011c6d3c
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs
@@ -0,0 +1,24 @@
+// 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.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Game.Tournament.Screens.MapPool;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneMapPoolScreen : LadderTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(MapPoolScreen)
+ };
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Add(new MapPoolScreen { Width = 0.7f });
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneRoundEditorScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneRoundEditorScreen.cs
new file mode 100644
index 0000000000..e15ac416b0
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneRoundEditorScreen.cs
@@ -0,0 +1,18 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Tournament.Screens.Editors;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneRoundEditorScreen : LadderTestScene
+ {
+ public TestSceneRoundEditorScreen()
+ {
+ Add(new RoundEditorScreen
+ {
+ Width = 0.85f // create room for control panel
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs
new file mode 100644
index 0000000000..f3e65919eb
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs
@@ -0,0 +1,18 @@
+// 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.Tests.Visual;
+using osu.Game.Tournament.Screens.Schedule;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneScheduleScreen : OsuTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Add(new ScheduleScreen());
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs
new file mode 100644
index 0000000000..edf1477b06
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneShowcaseScreen.cs
@@ -0,0 +1,18 @@
+// 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.Tests.Visual;
+using osu.Game.Tournament.Screens.Showcase;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneShowcaseScreen : OsuTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Add(new ShowcaseScreen());
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamEditorScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamEditorScreen.cs
new file mode 100644
index 0000000000..097bad4a02
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamEditorScreen.cs
@@ -0,0 +1,18 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Tournament.Screens.Editors;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneTeamEditorScreen : LadderTestScene
+ {
+ public TestSceneTeamEditorScreen()
+ {
+ Add(new TeamEditorScreen
+ {
+ Width = 0.85f // create room for control panel
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs
new file mode 100644
index 0000000000..3d340e393c
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.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.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Tournament.Models;
+using osu.Game.Tournament.Screens.TeamIntro;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneTeamIntroScreen : LadderTestScene
+ {
+ [Cached]
+ private readonly Bindable currentMatch = new Bindable();
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ var match = new TournamentMatch();
+ match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA");
+ match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN");
+ match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
+ currentMatch.Value = match;
+
+ Add(new TeamIntroScreen
+ {
+ FillMode = FillMode.Fit,
+ FillAspectRatio = 16 / 9f
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs
new file mode 100644
index 0000000000..6f5e17a36e
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.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.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Tournament.Models;
+using osu.Game.Tournament.Screens.TeamWin;
+
+namespace osu.Game.Tournament.Tests.Screens
+{
+ public class TestSceneTeamWinScreen : LadderTestScene
+ {
+ [Cached]
+ private readonly Bindable currentMatch = new Bindable();
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ var match = new TournamentMatch();
+ match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA");
+ match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN");
+ match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
+ currentMatch.Value = match;
+
+ Add(new TeamWinScreen
+ {
+ FillMode = FillMode.Fit,
+ FillAspectRatio = 16 / 9f
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs b/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs
new file mode 100644
index 0000000000..378614343a
--- /dev/null
+++ b/osu.Game.Tournament.Tests/TestSceneTournamentSceneManager.cs
@@ -0,0 +1,18 @@
+// 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.Platform;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Tournament.Tests
+{
+ public class TestSceneTournamentSceneManager : OsuTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load(Storage storage)
+ {
+ Add(new TournamentSceneManager());
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs
new file mode 100644
index 0000000000..f7ad757926
--- /dev/null
+++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs
@@ -0,0 +1,27 @@
+// 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.Testing;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Backgrounds;
+
+namespace osu.Game.Tournament.Tests
+{
+ public class TournamentTestBrowser : TournamentGameBase
+ {
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ LoadComponentAsync(new Background("Menu/menu-background-0")
+ {
+ Colour = OsuColour.Gray(0.5f),
+ Depth = 10
+ }, AddInternal);
+
+ // Have to construct this here, rather than in the constructor, because
+ // we depend on some dependencies to be loaded within OsuGameBase.load().
+ Add(new TestBrowser());
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/TournamentTestRunner.cs b/osu.Game.Tournament.Tests/TournamentTestRunner.cs
new file mode 100644
index 0000000000..1f63f7c545
--- /dev/null
+++ b/osu.Game.Tournament.Tests/TournamentTestRunner.cs
@@ -0,0 +1,22 @@
+// 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.Framework;
+using osu.Framework.Platform;
+
+namespace osu.Game.Tournament.Tests
+{
+ public static class TournamentTestRunner
+ {
+ [STAThread]
+ public static int Main(string[] args)
+ {
+ using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
+ {
+ host.Run(new TournamentTestBrowser());
+ return 0;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
new file mode 100644
index 0000000000..1c169184fb
--- /dev/null
+++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
@@ -0,0 +1,23 @@
+
+
+
+ osu.Game.Tournament.Tests.TournamentTestRunner
+
+
+
+
+
+
+
+
+ WinExe
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Tournament/Components/ControlPanel.cs b/osu.Game.Tournament/Components/ControlPanel.cs
new file mode 100644
index 0000000000..a9bb1bf42f
--- /dev/null
+++ b/osu.Game.Tournament/Components/ControlPanel.cs
@@ -0,0 +1,69 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tournament.Components
+{
+ ///
+ /// An element anchored to the right-hand area of a screen that provides streamer level controls.
+ /// Should be off-screen.
+ ///
+ public class ControlPanel : Container
+ {
+ private readonly FillFlowContainer buttons;
+
+ protected override Container Content => buttons;
+
+ public ControlPanel()
+ {
+ RelativeSizeAxes = Axes.Both;
+ AlwaysPresent = true;
+ Width = 0.15f;
+ Anchor = Anchor.TopRight;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = new Color4(54, 54, 54, 255)
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "Control Panel",
+ Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22)
+ },
+ buttons = new FillFlowContainer
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Width = 0.75f,
+ Position = new Vector2(0, 35f),
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 5f),
+ },
+ };
+ }
+
+ public class Spacer : CompositeDrawable
+ {
+ public Spacer(float height = 20)
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = height;
+ AlwaysPresent = true;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/DateTextBox.cs b/osu.Game.Tournament/Components/DateTextBox.cs
new file mode 100644
index 0000000000..ee7e350970
--- /dev/null
+++ b/osu.Game.Tournament/Components/DateTextBox.cs
@@ -0,0 +1,44 @@
+// 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.Framework.Bindables;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Settings;
+
+namespace osu.Game.Tournament.Components
+{
+ public class DateTextBox : SettingsTextBox
+ {
+ public new Bindable Bindable
+ {
+ get => bindable;
+ set
+ {
+ bindable = value.GetBoundCopy();
+ bindable.BindValueChanged(dto =>
+ base.Bindable.Value = dto.NewValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true);
+ }
+ }
+
+ // hold a reference to the provided bindable so we don't have to in every settings section.
+ private Bindable bindable;
+
+ public DateTextBox()
+ {
+ base.Bindable = new Bindable();
+ ((OsuTextBox)Control).OnCommit = (sender, newText) =>
+ {
+ try
+ {
+ bindable.Value = DateTimeOffset.Parse(sender.Text);
+ }
+ catch
+ {
+ // reset textbox content to its last valid state on a parse failure.
+ bindable.TriggerChange();
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs
new file mode 100644
index 0000000000..361bd92770
--- /dev/null
+++ b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs
@@ -0,0 +1,55 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using JetBrains.Annotations;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Tournament.Models;
+
+namespace osu.Game.Tournament.Components
+{
+ public abstract class DrawableTournamentTeam : CompositeDrawable
+ {
+ public readonly TournamentTeam Team;
+
+ protected readonly Sprite Flag;
+ protected readonly OsuSpriteText AcronymText;
+
+ [UsedImplicitly]
+ private Bindable acronym;
+
+ [UsedImplicitly]
+ private Bindable flag;
+
+ protected DrawableTournamentTeam(TournamentTeam team)
+ {
+ Team = team;
+
+ Flag = new Sprite
+ {
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit
+ };
+
+ AcronymText = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(weight: FontWeight.Regular),
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ if (Team == null) return;
+
+ (acronym = Team.Acronym.GetBoundCopy()).BindValueChanged(acronym => AcronymText.Text = Team?.Acronym.Value?.ToUpperInvariant() ?? string.Empty, true);
+ (flag = Team.FlagName.GetBoundCopy()).BindValueChanged(acronym => Flag.Texture = textures.Get($@"Flags/{Team.FlagName}"), true);
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs
new file mode 100644
index 0000000000..c07882ddd0
--- /dev/null
+++ b/osu.Game.Tournament/Components/SongBar.cs
@@ -0,0 +1,251 @@
+// 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.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Legacy;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Screens.Menu;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tournament.Components
+{
+ public class SongBar : CompositeDrawable
+ {
+ private BeatmapInfo beatmap;
+
+ public BeatmapInfo Beatmap
+ {
+ get => beatmap;
+ set
+ {
+ if (beatmap == value)
+ return;
+
+ beatmap = value;
+ update();
+ }
+ }
+
+ private LegacyMods mods;
+
+ public LegacyMods Mods
+ {
+ get => mods;
+ set
+ {
+ mods = value;
+ update();
+ }
+ }
+
+ private Container panelContents;
+ private Container innerPanel;
+ private Container outerPanel;
+ private TournamentBeatmapPanel panel;
+
+ private float panelWidth => expanded ? 0.6f : 1;
+
+ private const float main_width = 0.97f;
+ private const float inner_panel_width = 0.7f;
+
+ private bool expanded;
+
+ public bool Expanded
+ {
+ get => expanded;
+ set
+ {
+ expanded = value;
+ panel?.ResizeWidthTo(panelWidth, 800, Easing.OutQuint);
+
+ if (expanded)
+ {
+ innerPanel.ResizeWidthTo(inner_panel_width, 800, Easing.OutQuint);
+ outerPanel.ResizeWidthTo(main_width, 800, Easing.OutQuint);
+ }
+ else
+ {
+ innerPanel.ResizeWidthTo(1, 800, Easing.OutQuint);
+ outerPanel.ResizeWidthTo(0.25f, 800, Easing.OutQuint);
+ }
+ }
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ RelativeSizeAxes = Axes.Both;
+
+ InternalChildren = new Drawable[]
+ {
+ outerPanel = new Container
+ {
+ Masking = true,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Color4.Black.Opacity(0.2f),
+ Type = EdgeEffectType.Shadow,
+ Radius = 5,
+ },
+ RelativeSizeAxes = Axes.X,
+ Anchor = Anchor.BottomRight,
+ Origin = Anchor.BottomRight,
+ RelativePositionAxes = Axes.X,
+ X = -(1 - main_width) / 2,
+ Y = -10,
+ Width = main_width,
+ Height = TournamentBeatmapPanel.HEIGHT,
+ CornerRadius = TournamentBeatmapPanel.HEIGHT / 2,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(0.93f),
+ },
+ new OsuLogo
+ {
+ Triangles = false,
+ Colour = OsuColour.Gray(0.33f),
+ Scale = new Vector2(0.08f),
+ Margin = new MarginPadding(50),
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ },
+ innerPanel = new Container
+ {
+ Masking = true,
+ CornerRadius = TournamentBeatmapPanel.HEIGHT / 2,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Width = inner_panel_width,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(0.86f),
+ },
+ panelContents = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ }
+ }
+ }
+ }
+ }
+ };
+
+ Expanded = true;
+ }
+
+ private void update()
+ {
+ if (beatmap == null)
+ {
+ panelContents.Clear();
+ return;
+ }
+
+ var bpm = beatmap.BeatmapSet.OnlineInfo.BPM;
+ var length = beatmap.OnlineInfo.Length;
+ string hardRockExtra = "";
+ string srExtra = "";
+
+ //var ar = beatmap.BaseDifficulty.ApproachRate;
+ if ((mods & LegacyMods.HardRock) > 0)
+ {
+ hardRockExtra = "*";
+ srExtra = "*";
+ }
+
+ if ((mods & LegacyMods.DoubleTime) > 0)
+ {
+ //ar *= 1.5f;
+ bpm *= 1.5f;
+ length /= 1.5f;
+ srExtra = "*";
+ }
+
+ panelContents.Children = new Drawable[]
+ {
+ new DiffPiece(("Length", TimeSpan.FromSeconds(length).ToString(@"mm\:ss")))
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.BottomLeft,
+ },
+ new DiffPiece(("BPM", $"{bpm:0.#}"))
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.TopLeft
+ },
+ new DiffPiece(
+ //("CS", $"{beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"),
+ //("AR", $"{ar:0.#}{srExtra}"),
+ ("OD", $"{beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"),
+ ("HP", $"{beatmap.BaseDifficulty.DrainRate:0.#}{hardRockExtra}")
+ )
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.BottomRight
+ },
+ new DiffPiece(("Star Rating", $"{beatmap.StarDifficulty:0.#}{srExtra}"))
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.TopRight
+ },
+ panel = new TournamentBeatmapPanel(beatmap)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(panelWidth, 1)
+ }
+ };
+ }
+
+ public class DiffPiece : TextFlowContainer
+ {
+ public DiffPiece(params (string heading, string content)[] tuples)
+ {
+ Margin = new MarginPadding { Horizontal = 15, Vertical = 1 };
+ AutoSizeAxes = Axes.Both;
+
+ void cp(SpriteText s, Color4 colour)
+ {
+ s.Colour = colour;
+ s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15);
+ }
+
+ for (var i = 0; i < tuples.Length; i++)
+ {
+ var tuple = tuples[i];
+
+ if (i > 0)
+ {
+ AddText(" / ", s =>
+ {
+ cp(s, OsuColour.Gray(0.33f));
+ s.Spacing = new Vector2(-2, 0);
+ });
+ }
+
+ AddText(new OsuSpriteText { Text = tuple.heading }, s => cp(s, OsuColour.Gray(0.33f)));
+ AddText(" ", s => cp(s, OsuColour.Gray(0.33f)));
+ AddText(new OsuSpriteText { Text = tuple.content }, s => cp(s, OsuColour.Gray(0.5f)));
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs
new file mode 100644
index 0000000000..d5e28c1e3e
--- /dev/null
+++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs
@@ -0,0 +1,203 @@
+// 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.Collections.Specialized;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Localisation;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Tournament.Models;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tournament.Components
+{
+ public class TournamentBeatmapPanel : CompositeDrawable
+ {
+ public readonly BeatmapInfo Beatmap;
+ private readonly string mods;
+
+ private const float horizontal_padding = 10;
+ private const float vertical_padding = 5;
+
+ public const float HEIGHT = 50;
+
+ private readonly Bindable currentMatch = new Bindable();
+ private Box flash;
+
+ public TournamentBeatmapPanel(BeatmapInfo beatmap, string mods = null)
+ {
+ if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
+
+ Beatmap = beatmap;
+ this.mods = mods;
+ Width = 400;
+ Height = HEIGHT;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(LadderInfo ladder, TextureStore textures)
+ {
+ currentMatch.BindValueChanged(matchChanged);
+ currentMatch.BindTo(ladder.CurrentMatch);
+
+ CornerRadius = HEIGHT / 2;
+ Masking = true;
+
+ AddRangeInternal(new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ new UpdateableBeatmapSetCover
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(0.5f),
+ BeatmapSet = Beatmap.BeatmapSet,
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Padding = new MarginPadding(vertical_padding),
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = new LocalisedString((
+ $"{Beatmap.Metadata.ArtistUnicode ?? Beatmap.Metadata.Artist} - {Beatmap.Metadata.TitleUnicode ?? Beatmap.Metadata.Title}",
+ $"{Beatmap.Metadata.Artist} - {Beatmap.Metadata.Title}")),
+ Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true),
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Padding = new MarginPadding(vertical_padding),
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = "mapper",
+ Padding = new MarginPadding { Right = 5 },
+ Font = OsuFont.GetFont(italics: true, weight: FontWeight.Regular, size: 14)
+ },
+ new OsuSpriteText
+ {
+ Text = Beatmap.Metadata.AuthorString,
+ Padding = new MarginPadding { Right = 20 },
+ Font = OsuFont.GetFont(italics: true, weight: FontWeight.Bold, size: 14)
+ },
+ new OsuSpriteText
+ {
+ Text = "difficulty",
+ Padding = new MarginPadding { Right = 5 },
+ Font = OsuFont.GetFont(italics: true, weight: FontWeight.Regular, size: 14)
+ },
+ new OsuSpriteText
+ {
+ Text = Beatmap.Version,
+ Font = OsuFont.GetFont(italics: true, weight: FontWeight.Bold, size: 14)
+ },
+ }
+ }
+ },
+ },
+ flash = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Gray,
+ Blending = BlendingMode.Additive,
+ Alpha = 0,
+ },
+ });
+
+ if (!string.IsNullOrEmpty(mods))
+ AddInternal(new Sprite
+ {
+ Texture = textures.Get($"mods/{mods}"),
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Margin = new MarginPadding(20),
+ Scale = new Vector2(0.5f)
+ });
+ }
+
+ private void matchChanged(ValueChangedEvent match)
+ {
+ if (match.OldValue != null)
+ match.OldValue.PicksBans.CollectionChanged -= picksBansOnCollectionChanged;
+ match.NewValue.PicksBans.CollectionChanged += picksBansOnCollectionChanged;
+ updateState();
+ }
+
+ private void picksBansOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ => updateState();
+
+ private BeatmapChoice choice;
+
+ private void updateState()
+ {
+ var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap.OnlineBeatmapID);
+
+ bool doFlash = found != choice;
+ choice = found;
+
+ if (found != null)
+ {
+ if (doFlash)
+ flash?.FadeOutFromOne(500).Loop(0, 10);
+
+ BorderThickness = 6;
+
+ switch (found.Team)
+ {
+ case TeamColour.Red:
+ BorderColour = Color4.Red;
+ break;
+
+ case TeamColour.Blue:
+ BorderColour = Color4.Blue;
+ break;
+ }
+
+ switch (found.Type)
+ {
+ case ChoiceType.Pick:
+ Colour = Color4.White;
+ Alpha = 1;
+ break;
+
+ case ChoiceType.Ban:
+ Colour = Color4.Gray;
+ Alpha = 0.5f;
+ break;
+ }
+ }
+ else
+ {
+ Colour = Color4.White;
+ BorderThickness = 0;
+ Alpha = 1;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs b/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs
new file mode 100644
index 0000000000..48c5b9bd35
--- /dev/null
+++ b/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs
@@ -0,0 +1,93 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Online.Chat;
+using osu.Game.Overlays.Chat;
+using osu.Game.Tournament.IPC;
+using osu.Game.Tournament.Models;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tournament.Components
+{
+ public class TournamentMatchChatDisplay : StandAloneChatDisplay
+ {
+ private readonly Bindable chatChannel = new Bindable();
+
+ private ChannelManager manager;
+
+ public TournamentMatchChatDisplay()
+ {
+ RelativeSizeAxes = Axes.X;
+ Y = 100;
+ Size = new Vector2(0.45f, 112);
+ Margin = new MarginPadding(10);
+ Anchor = Anchor.BottomCentre;
+ Origin = Anchor.BottomCentre;
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load(MatchIPCInfo ipc)
+ {
+ if (ipc != null)
+ {
+ chatChannel.BindTo(ipc.ChatChannel);
+ chatChannel.BindValueChanged(c =>
+ {
+ if (string.IsNullOrWhiteSpace(c.NewValue))
+ return;
+
+ int id = int.Parse(c.NewValue);
+
+ if (id <= 0) return;
+
+ if (manager == null)
+ {
+ AddInternal(manager = new ChannelManager());
+ Channel.BindTo(manager.CurrentChannel);
+ }
+
+ foreach (var ch in manager.JoinedChannels.ToList())
+ manager.LeaveChannel(ch);
+
+ var channel = new Channel
+ {
+ Id = id,
+ Type = ChannelType.Public
+ };
+
+ manager.JoinChannel(channel);
+ manager.CurrentChannel.Value = channel;
+ }, true);
+ }
+ }
+
+ protected override ChatLine CreateMessage(Message message) => new MatchMessage(message);
+
+ protected class MatchMessage : StandAloneMessage
+ {
+ public MatchMessage(Message message)
+ : base(message)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(LadderInfo info)
+ {
+ //if (info.CurrentMatch.Value.Team1.Value.Players.Any(u => u.Id == Message.Sender.Id))
+ // ColourBox.Colour = red;
+ //else if (info.CurrentMatch.Value.Team2.Value.Players.Any(u => u.Id == Message.Sender.Id))
+ // ColourBox.Colour = blue;
+ //else if (Message.Sender.Colour != null)
+ // SenderText.Colour = ColourBox.Colour = OsuColour.FromHex(Message.Sender.Colour);
+ }
+
+ private readonly Color4 red = new Color4(186, 0, 18, 255);
+ private readonly Color4 blue = new Color4(17, 136, 170, 255);
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/TourneyVideo.cs b/osu.Game.Tournament/Components/TourneyVideo.cs
new file mode 100644
index 0000000000..4f4660f645
--- /dev/null
+++ b/osu.Game.Tournament/Components/TourneyVideo.cs
@@ -0,0 +1,45 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.IO;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Video;
+using osu.Game.Graphics;
+
+namespace osu.Game.Tournament.Components
+{
+ public class TourneyVideo : CompositeDrawable
+ {
+ private readonly VideoSprite video;
+
+ public TourneyVideo(Stream stream)
+ {
+ if (stream == null)
+ {
+ InternalChild = new Box
+ {
+ Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.3f), OsuColour.Gray(0.6f)),
+ RelativeSizeAxes = Axes.Both,
+ };
+ }
+ else
+ InternalChild = video = new VideoSprite(stream)
+ {
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ };
+ }
+
+ public bool Loop
+ {
+ set
+ {
+ if (video != null)
+ video.Loop = value;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs
new file mode 100644
index 0000000000..4fd858bd12
--- /dev/null
+++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs
@@ -0,0 +1,194 @@
+// 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.IO;
+using System.Linq;
+using Microsoft.Win32;
+using osu.Framework.Allocation;
+using osu.Framework.Logging;
+using osu.Framework.Platform;
+using osu.Framework.Platform.Windows;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Legacy;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Rulesets;
+using osu.Game.Tournament.Models;
+
+namespace osu.Game.Tournament.IPC
+{
+ public class FileBasedIPC : MatchIPCInfo
+ {
+ [Resolved]
+ protected IAPIProvider API { get; private set; }
+
+ [Resolved]
+ protected RulesetStore Rulesets { get; private set; }
+
+ private int lastBeatmapId;
+
+ [BackgroundDependencyLoader]
+ private void load(LadderInfo ladder, GameHost host)
+ {
+ StableStorage stable;
+
+ try
+ {
+ stable = new StableStorage(host as DesktopGameHost);
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, "Stable installation could not be found; disabling file based IPC");
+ return;
+ }
+
+ const string file_ipc_filename = "ipc.txt";
+ const string file_ipc_state_filename = "ipc-state.txt";
+ const string file_ipc_scores_filename = "ipc-scores.txt";
+ const string file_ipc_channel_filename = "ipc-channel.txt";
+
+ if (stable.Exists(file_ipc_filename))
+ Scheduler.AddDelayed(delegate
+ {
+ try
+ {
+ using (var stream = stable.GetStream(file_ipc_filename))
+ using (var sr = new StreamReader(stream))
+ {
+ var beatmapId = int.Parse(sr.ReadLine());
+ var mods = int.Parse(sr.ReadLine());
+
+ if (lastBeatmapId != beatmapId)
+ {
+ lastBeatmapId = beatmapId;
+
+ var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.BeatmapInfo != null);
+
+ if (existing != null)
+ Beatmap.Value = existing.BeatmapInfo;
+ else
+ {
+ var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId });
+ req.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets);
+ API.Queue(req);
+ }
+ }
+
+ Mods.Value = (LegacyMods)mods;
+ }
+ }
+ catch
+ {
+ // file might be in use.
+ }
+
+ try
+ {
+ using (var stream = stable.GetStream(file_ipc_channel_filename))
+ using (var sr = new StreamReader(stream))
+ {
+ ChatChannel.Value = sr.ReadLine();
+ }
+ }
+ catch (Exception)
+ {
+ // file might be in use.
+ }
+
+ try
+ {
+ using (var stream = stable.GetStream(file_ipc_state_filename))
+ using (var sr = new StreamReader(stream))
+ {
+ State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine());
+ }
+ }
+ catch (Exception)
+ {
+ // file might be in use.
+ }
+
+ try
+ {
+ using (var stream = stable.GetStream(file_ipc_scores_filename))
+ using (var sr = new StreamReader(stream))
+ {
+ Score1.Value = int.Parse(sr.ReadLine());
+ Score2.Value = int.Parse(sr.ReadLine());
+ }
+ }
+ catch (Exception)
+ {
+ // file might be in use.
+ }
+ }, 250, true);
+ }
+
+ ///
+ /// A method of accessing an osu-stable install in a controlled fashion.
+ ///
+ private class StableStorage : WindowsStorage
+ {
+ protected override string LocateBasePath()
+ {
+ bool checkExists(string p)
+ {
+ return File.Exists(Path.Combine(p, "ipc.txt"));
+ }
+
+ string stableInstallPath = string.Empty;
+
+ try
+ {
+ try
+ {
+ stableInstallPath = "G:\\My Drive\\Main\\osu!tourney";
+
+ if (checkExists(stableInstallPath))
+ return stableInstallPath;
+
+ stableInstallPath = "G:\\My Drive\\Main\\osu!mappool";
+
+ if (checkExists(stableInstallPath))
+ return stableInstallPath;
+ }
+ catch
+ {
+ }
+
+ try
+ {
+ using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
+ stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
+
+ if (checkExists(stableInstallPath))
+ return stableInstallPath;
+ }
+ catch
+ {
+ }
+
+ stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
+ if (checkExists(stableInstallPath))
+ return stableInstallPath;
+
+ stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
+ if (checkExists(stableInstallPath))
+ return stableInstallPath;
+
+ return null;
+ }
+ finally
+ {
+ Logger.Log($"Stable path for tourney usage: {stableInstallPath}");
+ }
+ }
+
+ public StableStorage(DesktopGameHost host)
+ : base(string.Empty, host)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/IPC/MatchIPCInfo.cs b/osu.Game.Tournament/IPC/MatchIPCInfo.cs
new file mode 100644
index 0000000000..701258c6c7
--- /dev/null
+++ b/osu.Game.Tournament/IPC/MatchIPCInfo.cs
@@ -0,0 +1,20 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Legacy;
+
+namespace osu.Game.Tournament.IPC
+{
+ public class MatchIPCInfo : Component
+ {
+ public Bindable Beatmap { get; } = new Bindable();
+ public Bindable Mods { get; } = new Bindable();
+ public Bindable State { get; } = new Bindable();
+ public Bindable ChatChannel { get; } = new Bindable();
+ public BindableInt Score1 { get; } = new BindableInt();
+ public BindableInt Score2 { get; } = new BindableInt();
+ }
+}
diff --git a/osu.Game.Tournament/IPC/TourneyState.cs b/osu.Game.Tournament/IPC/TourneyState.cs
new file mode 100644
index 0000000000..ef1c612a53
--- /dev/null
+++ b/osu.Game.Tournament/IPC/TourneyState.cs
@@ -0,0 +1,14 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Tournament.IPC
+{
+ public enum TourneyState
+ {
+ Initialising,
+ Idle,
+ WaitingForClients,
+ Playing,
+ Ranking
+ }
+}
diff --git a/osu.Game.Tournament/Models/BeatmapChoice.cs b/osu.Game.Tournament/Models/BeatmapChoice.cs
new file mode 100644
index 0000000000..384b349b24
--- /dev/null
+++ b/osu.Game.Tournament/Models/BeatmapChoice.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;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace osu.Game.Tournament.Models
+{
+ ///
+ /// A beatmap choice by a team from a tournament's map pool.
+ ///
+ [Serializable]
+ public class BeatmapChoice
+ {
+ [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
+ public TeamColour Team;
+
+ [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
+ public ChoiceType Type;
+
+ public int BeatmapID;
+ }
+
+ [JsonConverter(typeof(StringEnumConverter))]
+ public enum TeamColour
+ {
+ Red,
+ Blue
+ }
+
+ [JsonConverter(typeof(StringEnumConverter))]
+ public enum ChoiceType
+ {
+ Pick,
+ Ban,
+ }
+}
diff --git a/osu.Game.Tournament/Models/LadderEditorInfo.cs b/osu.Game.Tournament/Models/LadderEditorInfo.cs
new file mode 100644
index 0000000000..70fd115e25
--- /dev/null
+++ b/osu.Game.Tournament/Models/LadderEditorInfo.cs
@@ -0,0 +1,12 @@
+// 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.Bindables;
+
+namespace osu.Game.Tournament.Models
+{
+ public class LadderEditorInfo
+ {
+ public readonly Bindable Selected = new Bindable();
+ }
+}
diff --git a/osu.Game.Tournament/Models/LadderInfo.cs b/osu.Game.Tournament/Models/LadderInfo.cs
new file mode 100644
index 0000000000..547c4eab08
--- /dev/null
+++ b/osu.Game.Tournament/Models/LadderInfo.cs
@@ -0,0 +1,27 @@
+// 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.Collections.Generic;
+using Newtonsoft.Json;
+using osu.Framework.Bindables;
+
+namespace osu.Game.Tournament.Models
+{
+ ///
+ /// Holds the complete data required to operate the tournament system.
+ ///
+ [Serializable]
+ public class LadderInfo
+ {
+ public BindableList Matches = new BindableList();
+ public BindableList Rounds = new BindableList();
+ public BindableList Teams = new BindableList();
+
+ // only used for serialisation
+ public List Progressions = new List();
+
+ [JsonIgnore]
+ public Bindable CurrentMatch = new Bindable();
+ }
+}
diff --git a/osu.Game.Tournament/Models/RoundBeatmap.cs b/osu.Game.Tournament/Models/RoundBeatmap.cs
new file mode 100644
index 0000000000..5d43d0ca66
--- /dev/null
+++ b/osu.Game.Tournament/Models/RoundBeatmap.cs
@@ -0,0 +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.Game.Beatmaps;
+
+namespace osu.Game.Tournament.Models
+{
+ public class RoundBeatmap
+ {
+ public int ID;
+ public string Mods;
+
+ public BeatmapInfo BeatmapInfo;
+ }
+}
diff --git a/osu.Game.Tournament/Models/TournamentMatch.cs b/osu.Game.Tournament/Models/TournamentMatch.cs
new file mode 100644
index 0000000000..06cce3d59e
--- /dev/null
+++ b/osu.Game.Tournament/Models/TournamentMatch.cs
@@ -0,0 +1,125 @@
+// 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.Collections.Generic;
+using System.Collections.ObjectModel;
+using Newtonsoft.Json;
+using osu.Framework.Bindables;
+using osu.Game.Tournament.Screens.Ladder.Components;
+using SixLabors.Primitives;
+
+namespace osu.Game.Tournament.Models
+{
+ ///
+ /// A collection of two teams competing in a head-to-head match.
+ ///
+ [Serializable]
+ public class TournamentMatch
+ {
+ public int ID;
+
+ public List Acronyms
+ {
+ get
+ {
+ List acronyms = new List();
+ if (Team1Acronym != null) acronyms.Add(Team1Acronym);
+ if (Team2Acronym != null) acronyms.Add(Team2Acronym);
+ return acronyms;
+ }
+ }
+
+ [JsonIgnore]
+ public readonly Bindable Team1 = new Bindable();
+
+ public string Team1Acronym;
+
+ public readonly Bindable Team1Score = new Bindable();
+
+ [JsonIgnore]
+ public readonly Bindable Team2 = new Bindable();
+
+ public string Team2Acronym;
+
+ public readonly Bindable Team2Score = new Bindable();
+
+ public readonly Bindable Completed = new Bindable();
+
+ public readonly Bindable Losers = new Bindable();
+
+ public readonly ObservableCollection PicksBans = new ObservableCollection();
+
+ [JsonIgnore]
+ public readonly Bindable Round = new Bindable();
+
+ [JsonIgnore]
+ public readonly Bindable Progression = new Bindable();
+
+ [JsonIgnore]
+ public readonly Bindable LosersProgression = new Bindable();
+
+ ///
+ /// Should not be set directly. Use LadderInfo.CurrentMatch.Value = this instead.
+ ///
+ public readonly Bindable Current = new Bindable